web-dev-qa-db-ja.com

C構造体をJava JNIのコードに前後に渡す方法は?

構造体へのポインタを取得するJNIを介して呼び出すC関数と、同じタイプの構造体へのポインタを割り当て/解放する他の関数があるため、ラッパーを少し簡単に処理できます。 。驚くべきことに、JNIのドキュメントには、C構造体の扱い方についてほとんど書かれていません。

私のCヘッダーファイルは次のようになります:

_typedef struct _MyStruct {
  float member;
} MyStruct;

MyStruct* createNewMyStruct();
void processData(int *data, int numObjects, MyStruct *arguments);
_

対応するJNI Cラッパーファイルには以下が含まれます。

_JNIEXPORT jobject JNICALL
Java_com_myorg_MyJavaClass_createNewMyStruct(JNIEnv *env, jobject this) {
  return createNewMyStruct();
}

JNIEXPORT void JNICALL
Java_com_myorg_MyJavaClass_processData(JNIEnv *env, jobject this, jintArray data,
                                       jint numObjects, jobject arguments) {
  int *actualData = (*env)->GetIntArrayElements(env, data, NULL);
  processData(actualData, numObjects, arguments);
  (*env)->ReleaseIntArrayElements(env, data, actualData, NULL);
}
_

...そして最後に、対応するJavaクラス:

_public class MyJavaClass {
  static { System.loadLibrary("MyJniLibrary"); }

  private native MyStruct createNewMyStruct();
  private native void processData(int[] data, int numObjects, MyStruct arguments);

  private class MyStruct {
    float member;
  }

  public void test() {
    MyStruct foo = createNewMyStruct();
    foo.member = 3.14159f;
    int[] testData = new int[10];
    processData(testData, 10, foo);
  }
}
_

残念ながら、このコードはcreateNewMyStruct()を押した直後にJVMをクラッシュさせます。私はJNIに少し慣れていないので、問題が何であるかわかりません。

編集:Cコードは非常にVanilla Cであり、十分にテストされており、動作中のiPhoneプロジェクトから移植されていることに注意してください。また、このプロジェクトでは、Android NDKフレームワークを使用しています。これにより、JNI内からAndroidプロジェクトからネイティブCコードを実行できます。ただし、これは厳密にはNDKの問題だと思います...私の側ではJNIのセットアップ/初期化エラーのようです。

63
Nik Reiman

C構造体と同じメンバーを持つJavaクラスを作成し、メソッドenv-> GetIntField、env-> SetIntField、env-> GetFloatFieldを介してCコードに「マッピング」する必要があります。 env-> SetFloatFieldなど-手短に言えば、手作業が多く、それを自動的に行うプログラムが既に存在することを願っています:JNAerator( http://code.google.com/p/jnaerator )とSWIG( http://www.swig.org/ )。どちらにも長所と短所がありますが、選択はあなた次第です。

41
iirekm

_Java_com_myorg_MyJavaClass_createNewMyStruct_はjobjectを返すように宣言されていますが、実際にはstruct MyStructを返しているため、クラッシュします。 CheckJNIを有効にしてこれを実行すると、VMが大声で文句を言って中断します。processData()関数も、arguments

jobjectは、マネージヒープ上のオブジェクトです。宣言されたフィールドの前または後に余分なものを含めることができ、フィールドを特定の順序でメモリに配置する必要はありません。したがって、C構造体をJavaクラスの上にマッピングすることはできません。

これに対処する最も簡単な方法は、以前の回答で特定されました。JNI関数でjobjectを操作します。オブジェクトをJavaから、またはNewObjectGet/Setを使用して適切な呼び出しで割り当てます。

ここで「チート」するさまざまな方法があります。たとえば、sizeof(struct MyStruct)バイトを保持するJavaオブジェクトに_byte[]_を含めてから、GetByteArrayElementsを使用してポインタを取得できます。Java側からもフィールドにアクセスしたい場合は特にそうです。

10
fadden

C構造体は変数のコレクションです(一部は関数ポインターです)。 Javaに渡すのは良い考えではありません。一般に、ポインターのようにより複雑な型をJavaに渡す方法が問題です。

JNIブックでは、ポインター/構造をネイティブに保ち、Javaを推奨します。Java$ ===を推奨します。いくつかの役立つ記事を読むことができます。 JavaTM Native Interface Programmer's Guide and Specification、 読みました 9.5 Peer Classes それに対処する解決策があります。

7
qrtt1
  1. JavaとC++の両方の側でクラスを作成し、メンバー変数を入れます。C++構造体は、パブリックデータメンバーを持つクラスにすぎません。
  2. IDEを使用して、メンバー変数のセッターとゲッターを自動的に作成します。
  3. Javahを使用して、JavaクラスからCヘッダーファイルを生成します。
  4. C++側で編集を行い、セッターとゲッターが生成されたヘッダーファイルと一致するようにします。
  5. JNIコードを入力します。

これは理想的な解決策ではありませんが、少し時間を節約でき、少なくとも編集可能なスケルトンを提供します。この機能はIDEに追加できますが、大きな需要がなければ、おそらく実現しません。ほとんどのIDEは混合言語プロジェクトもサポートしていません。ましてや、互いに対話させることは言うまでもありません。

0
Bill