简体   繁体   中英

Passing std::unique_ptr to JNI

I'm wrapping some C++ code with JNI, and stumbled upon the following factory function:

std::unique_ptr<MetricPlanner> create_metric_planner(*arguments*)

I need to pass a reference to the planner this function creates back to Java for later use, but am confounded as to a) how to pass it back, and b) what will happen to it once it gets passed on.

Normally, I've been passing like so:

Director *DIRECTOR = new Director(arguments);
return (jlong)DIRECTOR;

and it's worked like a charm.

Can someone explain the analogous process for referencing objects with JNI when a factory function returning this type of pointer is used, instead of a normal constructor?

Since you're passing the return value of the create_metric_planner function to Java, and then using it later, you do not want the unique_ptr to destroy the return value when its scope ends. To do that you must call unique_ptr::release .

return (jlong)create_metric_planner( ... ).release();

Do not forget that at some point, when you're done using the object returned by that function, you must delete it (or call some deleter function provided by the library you're using).

I use the following code to create, use, and delete an object with a std::unique_ptr , while keeping the reference on the Java side (to keep the object alive, even when switching activities).

To create the object and pass the pointer to Java:

void Java_com_domain_project_activity_createObject(JNIEnv *env, jobject obj) {
        Object* object = (std::makeUnique<Object>()).release();
        env->SetLongField(obj, getPtrFieldId(env, obj), (jlong)object);
    }

To delete the object:

void Java_com_domain_project_activity_deleteObject(JNIEnv *env, jobject obj) {
        Object* object = (Object*) env->GetLongField(obj, getPtrFieldId(env, obj));
        delete object;
    }

To work on the object:

void Java_com_domain_project_activity_workOnObject(JNIEnv *env, jobject obj) {
        Object* object = (Object*) env->GetLongField(obj, getPtrFieldId(env, obj));
        // Work on object
}

And finally to keep the pointer stored on the Java side:

jfieldID getPtrFieldId(JNIEnv * env, jobject obj)
{
    static jfieldID ptrFieldId = 0;

    if (!ptrFieldId)
    {
        jclass c = env->GetObjectClass(obj);
        ptrFieldId = env->GetFieldID(c, "objPtr", "J");
        env->DeleteLocalRef(c);
    }

    return ptrFieldId;
}

Ideally I would not use unique_ptr for these cases, only normal pointers, but the unique_ptr is required in my project.

I also found this website very useful: https://www.studiofuga.com/2017/03/10/ac-smart-pointer-wrapper-for-use-with-jni/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM