C++/JNI-存储对象(jobject)在向量和数组中发生意外更改,C++或JNI问题

C++/JNI - Stored Object (jobject) Changes Unexpectedly in Vectors and Arrays, C++ or JNI Issue?

本文关键字:C++ 意外 问题 JNI 对象 存储 JNI- jobject 向量 数组      更新时间:2023-10-16

所以首先有一些背景:

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

问题是我对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: %sn", 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: %sn", 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

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

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

我会留下这个问题,然后再回答,以防将来对任何人都有帮助。