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

时间:2014-09-10 20:47:06

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

首先是一些背景:

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

问题在于,我对C ++或JNI没有超级经验,我搜索过的任何内容都告诉我这是什么类型的问题。我已经尝试将对象和指针存储到对象中,但两种方法都会产生相同的结果。我正在存储JSqTestFunc的实例,但代码正在检索JSqVM的实例。除了与Squirrel交互之外,这两个类的唯一共同点是它们扩展了Object,否则它们完全不相关。

我想我的问题应该分为多个部分:

  1. 这是C ++问题还是JNI问题?
  2. 我该如何解决这个问题?
  3. 我觉得它必须是一个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

    任何帮助,即使采用不同的方法,我都非常感激,因为我花了太长时间并且花费太多精力来解决这个问题。谢谢!

1 个答案:

答案 0 :(得分:2)

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

我将离开这个问题并回答,以防将来帮助任何人。