0

だから最初にいくつかの背景:

私は Java 用の C++ ライブラリを JNI、特に Squirrel スクリプト言語でラップすることに取り組んできました。問題は、Squirrel 仮想マシンにネイティブ関数を渡す必要があるときに発生します。Squirrel では、パラメーターとして HSQUIRRELVM を持ち、SQInteger を返す関数として定義された SQFUNCTION である関数が必要ですが、Java 用にこれをラップしていることに注意してください。C++ でジョブジェクトから Java メソッドを呼び出せるようにすることはできますが、実際に Squirrel に渡すには、その呼び出しをラムダ関数でラップする必要がありました。通常、変数を参照できるように [=] をラムダ キャプチャとして配置しますが、何らかの理由で変数をキャプチャするとラムダ関数の型が変更され、SQFUNCTION として認識されなくなります。最近のやり方は ラムダがそれにアクセスできるように、定数ベクトルまたは配列のいずれかでこれを修正することにしました。オブジェクトが格納されているベクトル/配列の場所を Squirrel に伝え、ラムダに Squirrel からその値を取得させてアクセスさせます。問題は次のとおりです。正しいスロットにオブジェクトがありますが、それは私がそこに置いたオブジェクトではありません。

問題は、私が C++ や JNI の経験が豊富でなく、検索してもこれがどのような問題であるかを教えてくれなかったことです。オブジェクトとオブジェクトへのポインターを保存しようとしましたが、両方の方法で同じ結果が得られます。のインスタンスを格納していますJSqTestFuncが、コードは のインスタンスを取得していますJSqVM。Squirrel との対話以外にこれら 2 つのクラスに共通する唯一の点は、Object を拡張することです。それ以外の場合は、まったく無関係です。

私の質問は複数の部分に分かれているはずです:

  1. これは C++ の問題ですか、それとも JNI の問題ですか?
  2. どうすればこれを修正できますか?

それは JNI の問題に違いないと思いますが、C++ が愚かであることも否定できません。JNI が jobject クラスとそれへの参照を処理する方法に慣れていないため、おそらく jobject は別のクラスのデータを内部に保存することになります。これに関連するものや、C++ 配列/ベクトル ストレージの問題も見つかりませんでした。

C++ 関数は次のようになります。

static const int m_maxClosures = 8;
static int m_closures = 0;

static JNIEnv *m_envs[m_maxClosures];
static jobject m_objs[m_maxClosures];

JNIEXPORT void JNICALL Java_com_yourlocalfax_jsquirrel_Squirrel_sq_1newclosure_1native(JNIEnv *env, jclass c, jlong vmhandle, jobject func, jlong nfreevars) {
    HSQUIRRELVM v = fromPointerHandleToObject<HSQUIRRELVM>(vmhandle);

    int idx = m_closures;

    printf("Creating number %d closure of %d", idx, m_maxClosures);

    m_closures++;

    m_envs[idx] = env;
    m_objs[idx] = func;

    sq_pushinteger(v, idx);

    JNIEnv *e = m_envs[idx];
    jobject o = m_objs[idx];

    jobject clsObj = e->CallObjectMethod(o, e->GetMethodID(e->GetObjectClass(o), "getClass", "()Ljava/lang/Class;"));
    jstring strObj = (jstring)e->CallObjectMethod(clsObj, e->GetMethodID(e->GetObjectClass(clsObj), "getName", "()Ljava/lang/String;"));

    const char* str = e->GetStringUTFChars(strObj, NULL);
    printf("\nInitial calling class is: %s\n", str);
    e->ReleaseStringUTFChars(strObj, str);

    SQFUNCTION f = [](HSQUIRRELVM v) {
        print_args(v);
        squirrel_stack_trace(v);

        SQInteger i;

        sq_pushinteger(v, 0); // Push the index in the table TO GET
        sq_get(v, 1); // Push the index of the actual table
        sq_getinteger(v, -1, &i); // Get the newly pushed value (integer)
        //sq_getinteger(v, 2, &i);
        printf("Location Id is %d of %d", i, m_maxClosures);

        JNIEnv *e = m_envs[i];
        jobject o = m_objs[i];

        jobject clsObj = e->CallObjectMethod(o, e->GetMethodID(e->GetObjectClass(o), "getClass", "()Ljava/lang/Class;"));
        jstring strObj = (jstring)e->CallObjectMethod(clsObj, e->GetMethodID(e->GetObjectClass(clsObj), "getName", "()Ljava/lang/String;"));

        const char* str = e->GetStringUTFChars(strObj, NULL);
        printf("\nCalling class is: %s\n", str);
        e->ReleaseStringUTFChars(strObj, str);

        jclass cls = e->GetObjectClass(o);
        jmethodID m = e->GetMethodID(cls, "function", "(Lcom/yourlocalfax/jsquirrel/JSqVM;)I");
        //sq_pushinteger(v, e->CallIntMethod(o, m));
        return (SQInteger)0;
    };

    sq_newclosure(v, f, nfreevars + 1);
}

私はまだ失敗していないことに言及する必要がありfromPointerHandleToObjectます.私は他のすべての関数呼び出しでそれを使用し、毎回動作します. そのためのコードも見たい場合は、投稿できます。

出力は次のとおりです。

Creating number 0 closure of 8
Initial calling class is: com.yourlocalfax.jsquirrel.test.JSqTestFunc
Location Id is 0 of 8
Calling class is: com.yourlocalfax.jsquirrel.JSqVM

ご覧のとおり、jobject 配列のインデックス 0 には、JSqTestFunc最初は が格納されますが、JSqVM取得されると が格納されます。

これを修正するのにあまりにも長い時間と多大な労力を費やしてきたので、これを行う別の方法であっても、どんな助けでも大歓迎です。ありがとう!

4

1 に答える 1

2

これを投稿した後、私は先に進み、さらに徹底的に調査しましたが、実際にはローカルおよびグローバル参照に関連する JNI 側の問題であることに気付きました。私がしなければならなかったのはenv->NewGlobalRef(object);、オブジェクトを配列に格納することだけでした。それで解決しました。

この質問を残して、将来誰かに役立つ場合に備えて回答します。

于 2014-09-10T20:59:42.243 に答える