【问题标题】:C++/JNI - Stored Object (jobject) Changes Unexpectedly in Vectors and Arrays, C++ or JNI Issue?C++/JNI - 向量和数组、C++ 或 JNI 问题中的存储对象 (jobject) 意外更改?
【发布时间】:2014-11-04 15:44:53
【问题描述】:

首先介绍一下背景:

我一直致力于使用 JNI,特别是 Squirrel 脚本语言为 Java 包装 C++ 库。当我需要将本机函数传递给 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

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

【问题讨论】:

    标签: java c++ lambda java-native-interface squirrel


    【解决方案1】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-05-06
      • 2018-04-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-07
      • 1970-01-01
      相关资源
      最近更新 更多