簡體   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