2009-07-06 25 views
27

JNIチュートリアル(例:this)は、オブジェクト内のプリミティブフィールドにアクセスする方法と、明示的な関数引数として(つまり、jarrayのサブクラスとして)提供される配列にアクセスする方法を非常にうまくカバーしています。しかし、jobjectにあるJava(プリミティブ)配列にアクセスするには?メインプログラムは、このようなものかもしれないJNIを使​​用してオブジェクト内の配列にアクセスする方法は?

class JavaClass { 
    ... 
    int i; 
    byte[] a; 
} 

:たとえば、私は、次のJavaオブジェクトのバイト配列を操作したい

class Test { 

    public static void main(String[] args) { 
    JavaClass jc = new JavaClass(); 
    jc.a = new byte[100]; 
    ... 
    process(jc); 
    } 

    public static native void process(JavaClass jc); 
} 

対応するC++側は次のようになります。

JNIEXPORT void JNICALL Java_Test_process(JNIEnv * env, jclass c, jobject jc) { 

    jclass jcClass = env->GetObjectClass(jc); 
    jfieldID iId = env->GetFieldID(jcClass, "i", "I"); 

    // This way we can get and set the "i" field. Let's double it: 
    jint i = env->GetIntField(jc, iId); 
    env->SetIntField(jc, iId, i * 2); 

    // The jfieldID of the "a" field (byte array) can be got like this: 
    jfieldID aId = env->GetFieldID(jcClass, "a", "[B"); 

    // But how do we operate on the array??? 
} 

私はGetByteArrayElementsを使用することを考えていたが、それはその引数としてArrayTypeを望んでいます。明らかに私は何かを欠いている。これに行く方法はありますか?

答えて

34

私は(あまりにも、JNI Struct referenceをチェックしてください)それはあなたが少しを助けることを願っています:

// Get the class 
jclass mvclass = env->GetObjectClass(*cls); 
// Get method ID for method getSomeDoubleArray that returns a double array 
jmethodID mid = env->GetMethodID(mvclass, "getSomeDoubleArray", "()[D"); 
// Call the method, returns JObject (because Array is instance of Object) 
jobject mvdata = env->CallObjectMethod(*base, mid); 
// Cast it to a jdoublearray 
jdoubleArray * arr = reinterpret_cast<jdoubleArray*>(&mvdata) 
// Get the elements (you probably have to fetch the length of the array as well 
double * data = env->GetDoubleArrayElements(*arr, NULL); 
// Don't forget to release it 
env->ReleaseDoubleArrayElements(*arr, data, 0); 

[OK]をここで私が代わりにフィールドの方法で動作(私はJavaのゲッタークリーナーを呼び出すと考えられる)ができますおそらくフィールドのためにそれを書き換えることもできます。コメントをすると、おそらく長さを取得する必要がありますリリースすることを忘れないでください。

編集:フィールドのために取得するサンプルを書き直します。 CallObjectMethodを基本的にGetObjectFieldに置き換えます。

jdoubleArray arr = *reinterpret_cast<jdoubleArray*>(&mvdata); 

しかしjdoubleArrayは、クラスへのポインタそのものであるので、私はこのようなラインから「型punnedポインタを参照解除することは、厳密なエイリアシング規則を破るだろう」と言って警告を受けるのgcc 6.3で

JNIEXPORT void JNICALL Java_Test_process(JNIEnv * env, jclass c, jobject jc) { 

    jclass jcClass = env->GetObjectClass(jc); 
    jfieldID iId = env->GetFieldID(jcClass, "i", "I"); 

    // This way we can get and set the "i" field. Let's double it: 
    jint i = env->GetIntField(jc, iId); 
    env->SetIntField(jc, iId, i * 2); 

    // The jfieldID of the "a" field (byte array) can be got like this: 
    jfieldID aId = env->GetFieldID(jcClass, "a", "[B"); 

    // Get the object field, returns JObject (because Array is instance of Object) 
    jobject mvdata = env->GetObjectField (jc, aID); 

    // Cast it to a jdoublearray 
    jdoubleArray * arr = reinterpret_cast<jdoubleArray*>(&mvdata) 

    // Get the elements (you probably have to fetch the length of the array as well 
    double * data = env->GetDoubleArrayElements(*arr, NULL); 

    // Don't forget to release it 
    env->ReleaseDoubleArrayElements(*arr, data, 0); 
} 
+0

ありがとうございます; getterを使用するのは賢明です(そしてクリーナーでさえも)。 GetXXXFieldのような方法で配列フィールドを直接取得する方法を誰かが指摘していない限り、このようにしなければなりません。 –

+1

私はフィールドの例を追加しました(基本的には、CallObjectMethodではなくGetObjectFieldを使用します)。私はもちろんそれがボックスから実行されることを保証することはできませんが、私はあなたが一般的な考えを得ることができることを願って:) – Daff

+2

右!どういうわけか私はこれをやるためにもっと簡単な方法を見つけることを期待していたので、私は定義に戻ることを嫌っていました(「配列はオブジェクトです」:-)プログラミングの心理学...もう一度ありがとう! –

1

_jdoubleArrayでは、キャストする前にアドレスを取得する必要はありません。このstatic_castは警告なしで動作します:

jdoubleArray arr = static_cast<jdoubleArray>(mvdata); 
関連する問題