繁体   English   中英

通过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--;

}

这应该简单地使用Java方法输入/减少在c ++代码中创建的变量。 c ++部分看起来像这样:

C ++

jmethodID jDec = env->GetStaticMethodID(cls, "dec", "(I)V");
jmethodID jInc = env->GetStaticMethodID(cls, "inc", "(I)V");

jint val = 10;

printf("%d\n", val);
env->CallStaticVoidMethod(cls, jDec, &val);
printf("%d\n", val);
env->CallStaticVoidMethod(cls, jInc, val);
printf("%d\n", 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: %d\n", 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: %d\n", 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: %d\n", ret);

或者,您可以使用int []而不是Wojtek建议的包装类。

“这应该简单地使用Java方法输入/减少用c ++代码创建的变量。” 不,不应该。 Java按值传递参数,因此您不是递增和递减C ++变量,而是在函数incdec中递增和递减val

首先,即使在纯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 & parameter的东西。

然而,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方法并且您希望保持一致的接口,那么这可能是合理的。 如果仅从C ++代码调用Java方法,那么整个方法都是无意义的,然后正确的解决方案是将方法更改为返回递增的值

public static int inc(int val) {
  System.out.println("inc called: " + val);
  return val + 1;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM