Итак, сначала немного предыстории:
Я работал над упаковкой библиотеки C ++ для Java с JNI, в частности, с языком сценариев Squirrel. Проблема возникает, когда мне нужно передать нативную функцию в Squirrel Virtual Machine. Squirrel требует, чтобы функция была SQFUNCTION, определенной как функция, которая имеет HSQUIRRELVM в качестве параметра и возвращает SQInteger, но имейте в виду, что я оборачиваю это для Java. Я могу заставить C ++ вызывать Java-метод из рабочего объекта просто отлично, но мне нужно было обернуть этот вызов в лямбда-функцию, чтобы фактически передать его Squirrel. Обычно я бы добавил [=] в качестве лямбда-захвата, чтобы он мог ссылаться на мои переменные, но по какой-то причине я совершенно не уверен, что захват переменных меняет тип лямбда-функции и больше не распознается как SQFUNCTION. Самый последний способ, с помощью которого я решил исправить это, — это либо постоянный вектор, либо массив, чтобы лямбда могла получить к нему доступ. Я сообщаю Squirrel, где в векторе / массиве хранится объект, и позволяю лямбде получить это значение от Squirrel для доступа к нему. Проблема в том, что в правильном слоте есть объект, но я не помещаю туда объект.
Проблема в том, что я не обладаю достаточным опытом работы с C ++ или JNI, и ничто из того, что я искал, не говорит мне, что это за проблема. Я пытался сохранить объект и указатели на объект, но оба метода дают один и тот же результат. Я храню экземпляр JSqTestFunc
но код извлекает экземпляр JSqVM
, Единственное, что объединяет эти два класса помимо взаимодействия с Squirrel, это то, что они расширяют Object, иначе они совершенно не связаны.
Я думаю, мой вопрос должен состоять из нескольких частей:
Я чувствую, что это должно быть проблемой 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
когда получено.
Любая помощь, даже другой способ сделать это, высоко ценится, поскольку я трачу слишком много времени и слишком много усилий, пытаясь это исправить. Спасибо!
После публикации я продолжил исследовать более интенсивно и понял, что это действительно проблема со стороны JNI, связанная с местными и глобальными ссылками. Все, что мне нужно было сделать, это env->NewGlobalRef(object);
а затем сохранить объект в массиве. Это решило это.
Я собираюсь оставить этот вопрос и ответить на случай, если это поможет кому-нибудь в будущем.