2014-09-10 2 views
0

Итак, сначала немного фона:C++/JNI - Сохраненный объект (jobject) Неожиданно изменяется в векторах и массивах, проблемах на C++ или JNI?

Я работаю над оберткой библиотеки C++ для Java с помощью JNI, в частности языка скриптов Squirrel. Проблема возникает, когда мне нужно передать собственную функцию на виртуальную машину Squirrel. Squirrel требует, чтобы функция была SQFUNCTION, определяемой как функция, которая имеет HSQUIRRELVM в качестве параметра и возвращает SQInteger, но имейте в виду, что я обертываю это для Java. Я могу заставить C++ вызывать Java-метод из jobject просто отлично, но мне нужно было обернуть этот вызов в лямбда-функцию, чтобы фактически передать его Squirrel. Обычно я помещал [=] как захват лямбда, чтобы он мог ссылаться на мои переменные, но по какой-то причине я совершенно не уверен в том, что захват переменных изменяет тип функции лямбда, и он больше не распознается как SQFUNCTION. Самый последний способ, которым я решил исправить это, - это либо постоянный вектор, либо массив, чтобы лямбда могла получить к нему доступ. Я говорю Squirrel, где в векторе/массиве объект хранится и позволяет лямбда получить это значение от Squirrel для доступа к нему. В чем проблема: в правильном слоте есть объект, но это не тот объект, который я там помещаю.

Проблема в том, что я не супер опытный с C++ или JNI, и ничего, что я искал, сказал мне, что это за проблема. Я попытался сохранить объект и указатели на объект, но оба метода имеют одинаковый результат. Я храню экземпляр JSqTestFunc, но код извлекает экземпляр JSqVM. Единственное, что имеют эти два класса, кроме взаимодействия с белкой, заключается в том, что они расширяют 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 

Как вы можете видеть, индекс 0 из jobject массива запоминает JSqTestFunc первоначально но JSqVM при извлечении.

Любая помощь вообще, даже другой способ сделать это, очень ценится, поскольку я трачу слишком много времени и слишком много усилий, пытаясь исправить это. Благодаря!

ответ

2

Я пошел дальше и исследовал более интенсивно после публикации этого и понял, что это действительно проблема на стороне JNI, относящаяся к локальным и глобальным ссылкам. Все, что мне нужно было сделать, это env->NewGlobalRef(object);, а затем сохранить объект в массиве. Это решило.

Я собираюсь оставить этот вопрос и ответить на него, если он поможет кому-либо в будущем.

Смежные вопросы