通过jni从c++向java传递每个引用的参数
Pass parameters per reference from c++ to java via jni
我试图通过JNI将变量从c++传递到java。一开始,我用一些简单的代码试了试:
Java
public static void inc(int val) {
System.out.println("inc called: " + val);
val++;
}
public static void dec(int val) {
System.out.println("dec called: " + val);
val--;
}
这应该简单地对c++代码中使用Java方法创建的变量进行-/递减。c++部分如下所示:
c++jmethodID jDec = env->GetStaticMethodID(cls, "dec", "(I)V");
jmethodID jInc = env->GetStaticMethodID(cls, "inc", "(I)V");
jint val = 10;
printf("%dn", val);
env->CallStaticVoidMethod(cls, jDec, &val);
printf("%dn", val);
env->CallStaticVoidMethod(cls, jInc, val);
printf("%dn", val);
如你所见,我分别尝试了每个引用和每个值。
10
dec called: -401031272
10
inc called: 10
10
在c++代码中,值一直是10,在Java中,它要么是地址,要么是值。
如果你能给我一个提示就太好了,提前谢谢你了
正如Wojtek所说,Java按值传递参数。您可以在Java代码中添加一个返回值:
public static int inc2(int val) {
System.out.println("inc called: " + val);
return val + 1;
}
,然后在c++中调用:
jmethodID inc2 = env->GetStaticMethodID(cls, "inc2", "(I)I");
jint result = env->CallStaticIntMethod(cls, inc2, val);
printf("inc2: %dn", result);
另一种方法是使用包装器类代替:
public class IntWrapper {
private int value;
public IntWrapper(int value) {
setInt(value);
}
public int getInt() {
return value;
}
public void setInt(int value) {
this.value = value;
}
}
和以下Java方法:
public static void inc3(IntWrapper val) {
val.setInt(val.getInt()+1);
}
你可以从你的c++代码中调用它:
// create wrapper object
jclass wrapper = env->FindClass("IntWrapper");
jmethodID constructor = env->GetMethodID(wrapper, "<init>", "(I)V");
jobject wrapperObject = env->NewObject(wrapper, constructor, val);
// print value before increment
jmethodID getInt = env->GetMethodID(wrapper, "getInt", "()I");
jint ret = env->CallIntMethod(wrapperObject, getInt);
printf("Wrapper value: %dn", ret);
// call inc3
jmethodID inc3 = env->GetStaticMethodID(cls, "inc3", "(LIntWrapper;)V");
env->CallStaticVoidMethod(cls, inc3, wrapperObject);
// print result
ret = env->CallIntMethod(wrapperObject, getInt);
printf("Wrapper value after increment: %dn", ret);
或者您可以使用int[]代替Wojtek建议的包装器类。
首先,即使在纯c++上下文中,以下内容也没有意义:
jint val = 10;
// ...
env->CallStaticVoidMethod(cls, jDec, &val);
&val
返回本地变量val
的地址。这在c++中通常都无法编译。你自己试试吧:
int x = 10;
int y = &x; // error
之所以在这里编译是因为CallStaticVoidMethod
是一个可变函数。它是这样声明的:
void CallStaticVoidMethod(jclass clazz, jmethodID methodID, ...)
最后的...
部分意味着"任何事情都可以进行,但是如果您传递的任何东西在运行时我不能正确处理,您要负责。"
所以这种方法显然是注定要失败的。
然而,实际的问题在于Java没有c++意义上的引用。一切都是按值传递的;根本没有等价于c++int &
形参。
然而,Java有指针,尽管它大多称它们为"引用",令人困惑。例如,Java的Integer
引用将是c++中的Integer *
(假设c++中存在Integer
类)。天真地,你可以改变你的方法的签名来接受Integer
参数,并在c++端摆弄对象,但这也行不通,因为Java的Integer
类是不可变的,所以你不能增加它。
那你到底能做什么呢?
您可以利用单元素数组,但这不是一个很好的解决方案;它增加了您必须进行的错误检查的数量。为了使代码足够健壮,必须确保数组不包含0或> 1个元素。此外,数组会让所有看到代码的人感到困惑。
另一种解决方案是创建您自己的可变整数类并使用该类。像这样:public class MutableInteger {
private int value;
public MutableInteger(int value) {
this.value = value;
}
public void increment() {
++value;
}
public void decrement() {
--value;
}
public String toString() {
return String.valueOf(value);
}
public int get() {
return value;
}
// other operations
}
(…)
public static void inc(MutableInteger val) {
System.out.println("inc called: " + val);
val.increment();
}
代码的c++端将因此变得更加复杂。您必须首先找到您的类,找到并调用它的构造函数,找到并调用increment
方法,最后找到并调用get
方法。理想情况下,您可以将所有这些都封装在一个小的c++函数中:
void increment(JNIEnv *env, int &value)
{
jclass javaClass = env->FindClass(env, "your/package/MutableInteger");
jmethodID constructor = env->GetMethodID(env, javaClass, "<init>", "(I)V");
jobject javaObject = env->NewObject(env, javaClass, constructor, value);
jmethodID incrementMethod = env->GetMethodID(env, javaClass, "increment", "()V");
env->CallVoidMethod(javaObject, incrementMethod, value);
jmethodID getMethod = env->GetMethodID(env, javaClass, "get", "()I");
value = env->CallIntMethod(javaObject, getMethod);
}
(我已经写下了我的头,所以它可能包含错误,你当然应该在实际代码中添加很多错误处理和可能的一些jmethodID
优化。)
值得这么麻烦吗?可能不会。如果Java方法也从许多其他Java代码中调用,并且您希望保持一致的接口,那么这可能是合理的。如果Java方法只能从c++代码中调用,那么整个方法就是无意义的,正确的解决方案是将方法改为返回递增的值:
public static int inc(int val) {
System.out.println("inc called: " + val);
return val + 1;
}
"这应该简单地使用Java方法在c++代码中创建的变量进行-/递减。"不,不应该。Java通过值传递参数,所以你递增和递减的不是c++变量,而是inc
和dec
函数中的局部变量val
。
- 将const引用参数初始化为默认参数会导致悬空引用吗
- 具有常量引用参数的函数模板专用化
- C++:常量引用参数
- 字符串引用参数的效率C++
- 通过非常量引用参数修改常量引用参数
- 如何将指针变量作为引用参数传递?
- C++初始化 std::function 时如何将占位符绑定到引用/引用参数?
- 移动类的成员作为常量引用参数传递
- C++带有适用于左值和右值的引用参数的函数
- constexpr 函数的常量引用参数:gcc/msvc vs clang/icc
- 如何使用类型特征将函数的通用引用参数限制为 r 值引用?
- 委托构造函数和引用参数
- 对 const 引用参数使用默认值会导致崩溃
- 为什么我们不允许将纯引用参数传递给 std::thread,但允许传递原始指针?
- 为什么我需要将默认引用参数定义为 const 以便为其分配一个左值?
- 将非左值作为常量引用参数传递.临时是在本地作用域还是在调用方作用域中创建的?
- 如何强制函数仅接受左值引用参数
- 模板引用参数推断失败C++
- 非类型引用参数可以在运行时修改,这是否意味着模板可以在运行时实例化?
- 将unique_ptr作为引用参数或常量传递unique_ptr引用