0

所以首先是一些背景:

我一直致力于用 JNI 为 Java 包装一个 C++ 库,特别是 Squirrel 脚本语言。当我需要将本机函数传递给 Squirrel 虚拟机时,问题就来了。Squirrel 要求一个函数是一个 SQFUNCTION,该函数定义为一个以 HSQUIRRELVM 作为参数并返回 SQInteger 的函数,但请记住,我是为 Java 包装它的。我可以让 C++ 从一个作业中调用一个 Java 方法就好了,但是我需要将该调用包装在一个 lambda 函数中,以便将它实际传递给 Squirrel。通常我会将 [=] 作为 lambda 捕获,因此它可以引用我的变量,但是由于某种原因我完全不确定,捕获变量会更改 lambda 函数的类型,并且它不再被识别为 SQFUNCTION。我最近的方式 我们决定使用常量向量或数组来解决这个问题,以便 lambda 可以访问它。我告诉 Squirrel 对象存储在向量/数组中的哪个位置,并让 lambda 从 Squirrel 获取该值以访问它。问题在于:在正确的插槽中有一个对象,但不是我放在那里的对象。

问题变成了我对 C++ 或 JNI 的经验并不丰富,而且我搜索的任何内容都没有告诉我这是什么类型的问题。我已经尝试存储对象和指向对象的指针,但是这两种方法都产生了相同的结果。我正在存储 的实例,JSqTestFunc但代码正在检索JSqVM. 除了与 Squirrel 交互之外,这两个类的唯一共同点是它们扩展了 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检索时的 a。

任何帮助,即使是不同的方法,都非常感谢,因为我花了太长时间和太多努力来解决这个问题。谢谢!

4

1 回答 1

2

在发布此内容后,我继续进行了更深入的研究,并意识到这确实是 JNI 方面与本地和全球参考相关的问题。我所要做的就是env->NewGlobalRef(object);然后将对象存储在数组中。那解决了它。

我将留下这个问题并回答,以防将来对任何人有所帮助。

于 2014-09-10T20:59:42.243 回答