Return function pointer from C++ behavior to Java object with JNI










0















This is way over my head so here we go.



I am setting a function pointer in C++ with an interface Java object which looks like that on the Java side:



 Info_t info_t = new Info_t();
info_t.setCallbackTest(new CallbackTest()
@Override
public void onCallback(int num)
System.out.println("callback: " + num);

);


where CallbackTest() is the interface with just one method:



public interface CallbackTest 
void onCallback(int num);



and setCallbackTest() refers to the Java native method which is reflected in the C++ side like that:



JNIEnv *gbl_env;
jmethodID gbl_method;
jobject gbl_callback;

void WrapperFunction(int a)
gbl_env->ExceptionClear();
gbl_env->CallVoidMethod(gbl_callback, gbl_method, a);
if(gbl_env->ExceptionOccurred())
gbl_env->ExceptionClear();

return;


JNIEXPORT void JNICALL Java_apiJNI_Info_1t_1callbackTest_1set(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jobject jarg2)
Info *arg1 = (Info *) 0;

(void) jenv;
(void) jcls;
(void) jarg1_;
arg1 = *(Info **)&jarg1;

jclass clazz;
jmethodID mid;

clazz = jenv->GetObjectClass(jarg2);
mid = jenv->GetMethodID(clazz, "onCallback", "(I)V");
if(mid == 0)
std::cout << "could not get method id" << std::endl;
return;

gbl_env = jenv;
gbl_method = mid;
gbl_callback = jarg2;

// here I am setting function pointer to the helper method which invokes the java code
if(arg1) (arg1)->completionCB = WrapperFunction;



All of this was written basing on the Implement callback function in JNI using Interface



I have tried calling the onCallback() method defined in Java on the C++ side and it works (just like in the link above) but now I am trying to set the value on such struct under C++



typedef void (*callbackFunction)(int);
typedef struct Info

callbackFunction completionCB;
void *caller;
Info_t;


so later on at some point I would like to get the callbackFunction completionCB back to Java as an object and probably do various stuff with it.



My question is how can I return/map the function pointer behavior from C++ back to the Java object? Basically, I would like to do the same but in the reverse order. I am able to map Java interface onto function pointer now I would like to map function pointer behavior onto Java object.



EDIT: So far I have come up with such getter for it but I am not sure how to proceed



JNIEXPORT jobject JNICALL Java_apiJNI_Info_1t_1callbackTest_1get(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) 
jobject jresult = 0;
Info *arg1 = (Info *) 0;
callbackFunction result;

(void)jenv;
(void)jcls;
(void)jarg1_;
arg1 = *(Info **)&jarg1;
result = ((arg1)->completionCB);


jclass clazz = jenv->FindClass("com/CallbackTest");
???

???
return ;










share|improve this question




























    0















    This is way over my head so here we go.



    I am setting a function pointer in C++ with an interface Java object which looks like that on the Java side:



     Info_t info_t = new Info_t();
    info_t.setCallbackTest(new CallbackTest()
    @Override
    public void onCallback(int num)
    System.out.println("callback: " + num);

    );


    where CallbackTest() is the interface with just one method:



    public interface CallbackTest 
    void onCallback(int num);



    and setCallbackTest() refers to the Java native method which is reflected in the C++ side like that:



    JNIEnv *gbl_env;
    jmethodID gbl_method;
    jobject gbl_callback;

    void WrapperFunction(int a)
    gbl_env->ExceptionClear();
    gbl_env->CallVoidMethod(gbl_callback, gbl_method, a);
    if(gbl_env->ExceptionOccurred())
    gbl_env->ExceptionClear();

    return;


    JNIEXPORT void JNICALL Java_apiJNI_Info_1t_1callbackTest_1set(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jobject jarg2)
    Info *arg1 = (Info *) 0;

    (void) jenv;
    (void) jcls;
    (void) jarg1_;
    arg1 = *(Info **)&jarg1;

    jclass clazz;
    jmethodID mid;

    clazz = jenv->GetObjectClass(jarg2);
    mid = jenv->GetMethodID(clazz, "onCallback", "(I)V");
    if(mid == 0)
    std::cout << "could not get method id" << std::endl;
    return;

    gbl_env = jenv;
    gbl_method = mid;
    gbl_callback = jarg2;

    // here I am setting function pointer to the helper method which invokes the java code
    if(arg1) (arg1)->completionCB = WrapperFunction;



    All of this was written basing on the Implement callback function in JNI using Interface



    I have tried calling the onCallback() method defined in Java on the C++ side and it works (just like in the link above) but now I am trying to set the value on such struct under C++



    typedef void (*callbackFunction)(int);
    typedef struct Info

    callbackFunction completionCB;
    void *caller;
    Info_t;


    so later on at some point I would like to get the callbackFunction completionCB back to Java as an object and probably do various stuff with it.



    My question is how can I return/map the function pointer behavior from C++ back to the Java object? Basically, I would like to do the same but in the reverse order. I am able to map Java interface onto function pointer now I would like to map function pointer behavior onto Java object.



    EDIT: So far I have come up with such getter for it but I am not sure how to proceed



    JNIEXPORT jobject JNICALL Java_apiJNI_Info_1t_1callbackTest_1get(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) 
    jobject jresult = 0;
    Info *arg1 = (Info *) 0;
    callbackFunction result;

    (void)jenv;
    (void)jcls;
    (void)jarg1_;
    arg1 = *(Info **)&jarg1;
    result = ((arg1)->completionCB);


    jclass clazz = jenv->FindClass("com/CallbackTest");
    ???

    ???
    return ;










    share|improve this question


























      0












      0








      0








      This is way over my head so here we go.



      I am setting a function pointer in C++ with an interface Java object which looks like that on the Java side:



       Info_t info_t = new Info_t();
      info_t.setCallbackTest(new CallbackTest()
      @Override
      public void onCallback(int num)
      System.out.println("callback: " + num);

      );


      where CallbackTest() is the interface with just one method:



      public interface CallbackTest 
      void onCallback(int num);



      and setCallbackTest() refers to the Java native method which is reflected in the C++ side like that:



      JNIEnv *gbl_env;
      jmethodID gbl_method;
      jobject gbl_callback;

      void WrapperFunction(int a)
      gbl_env->ExceptionClear();
      gbl_env->CallVoidMethod(gbl_callback, gbl_method, a);
      if(gbl_env->ExceptionOccurred())
      gbl_env->ExceptionClear();

      return;


      JNIEXPORT void JNICALL Java_apiJNI_Info_1t_1callbackTest_1set(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jobject jarg2)
      Info *arg1 = (Info *) 0;

      (void) jenv;
      (void) jcls;
      (void) jarg1_;
      arg1 = *(Info **)&jarg1;

      jclass clazz;
      jmethodID mid;

      clazz = jenv->GetObjectClass(jarg2);
      mid = jenv->GetMethodID(clazz, "onCallback", "(I)V");
      if(mid == 0)
      std::cout << "could not get method id" << std::endl;
      return;

      gbl_env = jenv;
      gbl_method = mid;
      gbl_callback = jarg2;

      // here I am setting function pointer to the helper method which invokes the java code
      if(arg1) (arg1)->completionCB = WrapperFunction;



      All of this was written basing on the Implement callback function in JNI using Interface



      I have tried calling the onCallback() method defined in Java on the C++ side and it works (just like in the link above) but now I am trying to set the value on such struct under C++



      typedef void (*callbackFunction)(int);
      typedef struct Info

      callbackFunction completionCB;
      void *caller;
      Info_t;


      so later on at some point I would like to get the callbackFunction completionCB back to Java as an object and probably do various stuff with it.



      My question is how can I return/map the function pointer behavior from C++ back to the Java object? Basically, I would like to do the same but in the reverse order. I am able to map Java interface onto function pointer now I would like to map function pointer behavior onto Java object.



      EDIT: So far I have come up with such getter for it but I am not sure how to proceed



      JNIEXPORT jobject JNICALL Java_apiJNI_Info_1t_1callbackTest_1get(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) 
      jobject jresult = 0;
      Info *arg1 = (Info *) 0;
      callbackFunction result;

      (void)jenv;
      (void)jcls;
      (void)jarg1_;
      arg1 = *(Info **)&jarg1;
      result = ((arg1)->completionCB);


      jclass clazz = jenv->FindClass("com/CallbackTest");
      ???

      ???
      return ;










      share|improve this question
















      This is way over my head so here we go.



      I am setting a function pointer in C++ with an interface Java object which looks like that on the Java side:



       Info_t info_t = new Info_t();
      info_t.setCallbackTest(new CallbackTest()
      @Override
      public void onCallback(int num)
      System.out.println("callback: " + num);

      );


      where CallbackTest() is the interface with just one method:



      public interface CallbackTest 
      void onCallback(int num);



      and setCallbackTest() refers to the Java native method which is reflected in the C++ side like that:



      JNIEnv *gbl_env;
      jmethodID gbl_method;
      jobject gbl_callback;

      void WrapperFunction(int a)
      gbl_env->ExceptionClear();
      gbl_env->CallVoidMethod(gbl_callback, gbl_method, a);
      if(gbl_env->ExceptionOccurred())
      gbl_env->ExceptionClear();

      return;


      JNIEXPORT void JNICALL Java_apiJNI_Info_1t_1callbackTest_1set(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jobject jarg2)
      Info *arg1 = (Info *) 0;

      (void) jenv;
      (void) jcls;
      (void) jarg1_;
      arg1 = *(Info **)&jarg1;

      jclass clazz;
      jmethodID mid;

      clazz = jenv->GetObjectClass(jarg2);
      mid = jenv->GetMethodID(clazz, "onCallback", "(I)V");
      if(mid == 0)
      std::cout << "could not get method id" << std::endl;
      return;

      gbl_env = jenv;
      gbl_method = mid;
      gbl_callback = jarg2;

      // here I am setting function pointer to the helper method which invokes the java code
      if(arg1) (arg1)->completionCB = WrapperFunction;



      All of this was written basing on the Implement callback function in JNI using Interface



      I have tried calling the onCallback() method defined in Java on the C++ side and it works (just like in the link above) but now I am trying to set the value on such struct under C++



      typedef void (*callbackFunction)(int);
      typedef struct Info

      callbackFunction completionCB;
      void *caller;
      Info_t;


      so later on at some point I would like to get the callbackFunction completionCB back to Java as an object and probably do various stuff with it.



      My question is how can I return/map the function pointer behavior from C++ back to the Java object? Basically, I would like to do the same but in the reverse order. I am able to map Java interface onto function pointer now I would like to map function pointer behavior onto Java object.



      EDIT: So far I have come up with such getter for it but I am not sure how to proceed



      JNIEXPORT jobject JNICALL Java_apiJNI_Info_1t_1callbackTest_1get(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) 
      jobject jresult = 0;
      Info *arg1 = (Info *) 0;
      callbackFunction result;

      (void)jenv;
      (void)jcls;
      (void)jarg1_;
      arg1 = *(Info **)&jarg1;
      result = ((arg1)->completionCB);


      jclass clazz = jenv->FindClass("com/CallbackTest");
      ???

      ???
      return ;







      java c++ jni function-pointers swig






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 14 '18 at 14:05







      Lisek

















      asked Nov 14 '18 at 13:59









      LisekLisek

      142217




      142217






















          1 Answer
          1






          active

          oldest

          votes


















          1














          You can not cache the JNIEnv value.



          Per Chapter 5: The Invocation API of the JNI specification (bolding mine):




          Attaching to the VM



          The JNI interface pointer (JNIEnv) is valid only in the current
          thread
          . Should another thread need to access the Java VM, it must
          first call AttachCurrentThread() to attach itself to the VM and
          obtain a JNI interface pointer. Once attached to the VM, a native
          thread works just like an ordinary Java thread running inside a native
          method. The native thread remains attached to the VM until it calls
          DetachCurrentThread() to detach itself.




          For method id and your callback object, you need to create a global references:



           gbl_method = jenv->NewGlobalReference( mid );
          gbl_callback = jenv->NewGlobalReverend( jarg2 );


          That does create one serious problem, though - that object reference held in gbl_callback will result in the Java object never getting garbage collected.



          To return a C++ callback function, you'd first need to convert the function pointer to a valid Java type. Strict conformance with the C standard (JNI is really C and not C++, so passing objects back and forth gets a bit murky when you mix in C++) means you can't treat the function pointer as anything other than a series of bytes, which means you'd need to convert the function pointer to a Java byte array and return that to Java. Then convert that Java byte array back to a function pointer when you want to use it in native code. That's a lot of code, and in JNI the more code you write the more likely you'll break something - JNI is fragile.



          But on both Windows and POSIX, a function pointer will fit into a jlong, and a jlong can accurately represent a function pointer's value, so that's a hack that works (and under POSIX it's probably not even a hack, but I'm not going to try finding the actual POSIX function pointer specifications that would allow that cast).



          The easiest way to do that is to create a long field in your Java object, and return the function pointer cast to a jlong:



          JNIEXPORT jlong JNICALL Java_apiJNI_Info_1t_1callbackTest_1set(
          JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jobject jarg2)

          ...

          return( ( jlong ) WrapperFunction );



          That would be called something like this:



          // Info_t has a long field called callback
          Info_t info_t = new Info_t();
          info_t.callback = info_t.setCallbackTest(new CallbackTest()
          @Override
          public void onCallback(int num)
          System.out.println("callback: " + num);

          );


          In my experience, the less JNI calls you make, the better. So if you can pass a value via a parameter or return it from a function call, that's simpler, safer, and a lot more reliable than trying to get/set object field values with JNI calls.



          But then you'd need to pass the callback value as another parameter:



          // function pointer, returns void, takes int arg
          typedef void (*funcPtr)( int );

          JNIEXPORT jobject JNICALL Java_apiJNI_Info_1t_1callbackTest_1get(
          JNIEnv *jenv, jclass jcls, jlong callback, jlong jarg1, jobject jarg1_)

          funcPtr f = ( funcPtr ) callback;

          f( 4 );

          ...






          share|improve this answer

























          • Are you reffering to the part of the code where I defined global JEnv* and did gbl_env = jenv;? If you do then how else can one map Java interfaces to the function pointers in C++?

            – Lisek
            Nov 14 '18 at 14:23






          • 2





            @Lisek The quote in the answer already outlines what needs to be done. See this answer for more details.

            – Michael
            Nov 14 '18 at 14:31











          • Thanks Michael. That points out the flaw of my code but still looking for the answer how could I map function pointer to the Java object.

            – Lisek
            Nov 14 '18 at 14:35











          • @Lisek That points out the flaw of my code but still looking for the answer how could I map function pointer to the Java object. There is no way to call non-JNI type functions such as your void WrapperFunction(int a) directly from Java code. However, you can save function pointers as jlong values. See edits for details.

            – Andrew Henle
            Nov 14 '18 at 15:07










          Your Answer






          StackExchange.ifUsing("editor", function ()
          StackExchange.using("externalEditor", function ()
          StackExchange.using("snippets", function ()
          StackExchange.snippets.init();
          );
          );
          , "code-snippets");

          StackExchange.ready(function()
          var channelOptions =
          tags: "".split(" "),
          id: "1"
          ;
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function()
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled)
          StackExchange.using("snippets", function()
          createEditor();
          );

          else
          createEditor();

          );

          function createEditor()
          StackExchange.prepareEditor(
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader:
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          ,
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          );



          );













          draft saved

          draft discarded


















          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53301963%2freturn-function-pointer-from-c-behavior-to-java-object-with-jni%23new-answer', 'question_page');

          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          1














          You can not cache the JNIEnv value.



          Per Chapter 5: The Invocation API of the JNI specification (bolding mine):




          Attaching to the VM



          The JNI interface pointer (JNIEnv) is valid only in the current
          thread
          . Should another thread need to access the Java VM, it must
          first call AttachCurrentThread() to attach itself to the VM and
          obtain a JNI interface pointer. Once attached to the VM, a native
          thread works just like an ordinary Java thread running inside a native
          method. The native thread remains attached to the VM until it calls
          DetachCurrentThread() to detach itself.




          For method id and your callback object, you need to create a global references:



           gbl_method = jenv->NewGlobalReference( mid );
          gbl_callback = jenv->NewGlobalReverend( jarg2 );


          That does create one serious problem, though - that object reference held in gbl_callback will result in the Java object never getting garbage collected.



          To return a C++ callback function, you'd first need to convert the function pointer to a valid Java type. Strict conformance with the C standard (JNI is really C and not C++, so passing objects back and forth gets a bit murky when you mix in C++) means you can't treat the function pointer as anything other than a series of bytes, which means you'd need to convert the function pointer to a Java byte array and return that to Java. Then convert that Java byte array back to a function pointer when you want to use it in native code. That's a lot of code, and in JNI the more code you write the more likely you'll break something - JNI is fragile.



          But on both Windows and POSIX, a function pointer will fit into a jlong, and a jlong can accurately represent a function pointer's value, so that's a hack that works (and under POSIX it's probably not even a hack, but I'm not going to try finding the actual POSIX function pointer specifications that would allow that cast).



          The easiest way to do that is to create a long field in your Java object, and return the function pointer cast to a jlong:



          JNIEXPORT jlong JNICALL Java_apiJNI_Info_1t_1callbackTest_1set(
          JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jobject jarg2)

          ...

          return( ( jlong ) WrapperFunction );



          That would be called something like this:



          // Info_t has a long field called callback
          Info_t info_t = new Info_t();
          info_t.callback = info_t.setCallbackTest(new CallbackTest()
          @Override
          public void onCallback(int num)
          System.out.println("callback: " + num);

          );


          In my experience, the less JNI calls you make, the better. So if you can pass a value via a parameter or return it from a function call, that's simpler, safer, and a lot more reliable than trying to get/set object field values with JNI calls.



          But then you'd need to pass the callback value as another parameter:



          // function pointer, returns void, takes int arg
          typedef void (*funcPtr)( int );

          JNIEXPORT jobject JNICALL Java_apiJNI_Info_1t_1callbackTest_1get(
          JNIEnv *jenv, jclass jcls, jlong callback, jlong jarg1, jobject jarg1_)

          funcPtr f = ( funcPtr ) callback;

          f( 4 );

          ...






          share|improve this answer

























          • Are you reffering to the part of the code where I defined global JEnv* and did gbl_env = jenv;? If you do then how else can one map Java interfaces to the function pointers in C++?

            – Lisek
            Nov 14 '18 at 14:23






          • 2





            @Lisek The quote in the answer already outlines what needs to be done. See this answer for more details.

            – Michael
            Nov 14 '18 at 14:31











          • Thanks Michael. That points out the flaw of my code but still looking for the answer how could I map function pointer to the Java object.

            – Lisek
            Nov 14 '18 at 14:35











          • @Lisek That points out the flaw of my code but still looking for the answer how could I map function pointer to the Java object. There is no way to call non-JNI type functions such as your void WrapperFunction(int a) directly from Java code. However, you can save function pointers as jlong values. See edits for details.

            – Andrew Henle
            Nov 14 '18 at 15:07















          1














          You can not cache the JNIEnv value.



          Per Chapter 5: The Invocation API of the JNI specification (bolding mine):




          Attaching to the VM



          The JNI interface pointer (JNIEnv) is valid only in the current
          thread
          . Should another thread need to access the Java VM, it must
          first call AttachCurrentThread() to attach itself to the VM and
          obtain a JNI interface pointer. Once attached to the VM, a native
          thread works just like an ordinary Java thread running inside a native
          method. The native thread remains attached to the VM until it calls
          DetachCurrentThread() to detach itself.




          For method id and your callback object, you need to create a global references:



           gbl_method = jenv->NewGlobalReference( mid );
          gbl_callback = jenv->NewGlobalReverend( jarg2 );


          That does create one serious problem, though - that object reference held in gbl_callback will result in the Java object never getting garbage collected.



          To return a C++ callback function, you'd first need to convert the function pointer to a valid Java type. Strict conformance with the C standard (JNI is really C and not C++, so passing objects back and forth gets a bit murky when you mix in C++) means you can't treat the function pointer as anything other than a series of bytes, which means you'd need to convert the function pointer to a Java byte array and return that to Java. Then convert that Java byte array back to a function pointer when you want to use it in native code. That's a lot of code, and in JNI the more code you write the more likely you'll break something - JNI is fragile.



          But on both Windows and POSIX, a function pointer will fit into a jlong, and a jlong can accurately represent a function pointer's value, so that's a hack that works (and under POSIX it's probably not even a hack, but I'm not going to try finding the actual POSIX function pointer specifications that would allow that cast).



          The easiest way to do that is to create a long field in your Java object, and return the function pointer cast to a jlong:



          JNIEXPORT jlong JNICALL Java_apiJNI_Info_1t_1callbackTest_1set(
          JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jobject jarg2)

          ...

          return( ( jlong ) WrapperFunction );



          That would be called something like this:



          // Info_t has a long field called callback
          Info_t info_t = new Info_t();
          info_t.callback = info_t.setCallbackTest(new CallbackTest()
          @Override
          public void onCallback(int num)
          System.out.println("callback: " + num);

          );


          In my experience, the less JNI calls you make, the better. So if you can pass a value via a parameter or return it from a function call, that's simpler, safer, and a lot more reliable than trying to get/set object field values with JNI calls.



          But then you'd need to pass the callback value as another parameter:



          // function pointer, returns void, takes int arg
          typedef void (*funcPtr)( int );

          JNIEXPORT jobject JNICALL Java_apiJNI_Info_1t_1callbackTest_1get(
          JNIEnv *jenv, jclass jcls, jlong callback, jlong jarg1, jobject jarg1_)

          funcPtr f = ( funcPtr ) callback;

          f( 4 );

          ...






          share|improve this answer

























          • Are you reffering to the part of the code where I defined global JEnv* and did gbl_env = jenv;? If you do then how else can one map Java interfaces to the function pointers in C++?

            – Lisek
            Nov 14 '18 at 14:23






          • 2





            @Lisek The quote in the answer already outlines what needs to be done. See this answer for more details.

            – Michael
            Nov 14 '18 at 14:31











          • Thanks Michael. That points out the flaw of my code but still looking for the answer how could I map function pointer to the Java object.

            – Lisek
            Nov 14 '18 at 14:35











          • @Lisek That points out the flaw of my code but still looking for the answer how could I map function pointer to the Java object. There is no way to call non-JNI type functions such as your void WrapperFunction(int a) directly from Java code. However, you can save function pointers as jlong values. See edits for details.

            – Andrew Henle
            Nov 14 '18 at 15:07













          1












          1








          1







          You can not cache the JNIEnv value.



          Per Chapter 5: The Invocation API of the JNI specification (bolding mine):




          Attaching to the VM



          The JNI interface pointer (JNIEnv) is valid only in the current
          thread
          . Should another thread need to access the Java VM, it must
          first call AttachCurrentThread() to attach itself to the VM and
          obtain a JNI interface pointer. Once attached to the VM, a native
          thread works just like an ordinary Java thread running inside a native
          method. The native thread remains attached to the VM until it calls
          DetachCurrentThread() to detach itself.




          For method id and your callback object, you need to create a global references:



           gbl_method = jenv->NewGlobalReference( mid );
          gbl_callback = jenv->NewGlobalReverend( jarg2 );


          That does create one serious problem, though - that object reference held in gbl_callback will result in the Java object never getting garbage collected.



          To return a C++ callback function, you'd first need to convert the function pointer to a valid Java type. Strict conformance with the C standard (JNI is really C and not C++, so passing objects back and forth gets a bit murky when you mix in C++) means you can't treat the function pointer as anything other than a series of bytes, which means you'd need to convert the function pointer to a Java byte array and return that to Java. Then convert that Java byte array back to a function pointer when you want to use it in native code. That's a lot of code, and in JNI the more code you write the more likely you'll break something - JNI is fragile.



          But on both Windows and POSIX, a function pointer will fit into a jlong, and a jlong can accurately represent a function pointer's value, so that's a hack that works (and under POSIX it's probably not even a hack, but I'm not going to try finding the actual POSIX function pointer specifications that would allow that cast).



          The easiest way to do that is to create a long field in your Java object, and return the function pointer cast to a jlong:



          JNIEXPORT jlong JNICALL Java_apiJNI_Info_1t_1callbackTest_1set(
          JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jobject jarg2)

          ...

          return( ( jlong ) WrapperFunction );



          That would be called something like this:



          // Info_t has a long field called callback
          Info_t info_t = new Info_t();
          info_t.callback = info_t.setCallbackTest(new CallbackTest()
          @Override
          public void onCallback(int num)
          System.out.println("callback: " + num);

          );


          In my experience, the less JNI calls you make, the better. So if you can pass a value via a parameter or return it from a function call, that's simpler, safer, and a lot more reliable than trying to get/set object field values with JNI calls.



          But then you'd need to pass the callback value as another parameter:



          // function pointer, returns void, takes int arg
          typedef void (*funcPtr)( int );

          JNIEXPORT jobject JNICALL Java_apiJNI_Info_1t_1callbackTest_1get(
          JNIEnv *jenv, jclass jcls, jlong callback, jlong jarg1, jobject jarg1_)

          funcPtr f = ( funcPtr ) callback;

          f( 4 );

          ...






          share|improve this answer















          You can not cache the JNIEnv value.



          Per Chapter 5: The Invocation API of the JNI specification (bolding mine):




          Attaching to the VM



          The JNI interface pointer (JNIEnv) is valid only in the current
          thread
          . Should another thread need to access the Java VM, it must
          first call AttachCurrentThread() to attach itself to the VM and
          obtain a JNI interface pointer. Once attached to the VM, a native
          thread works just like an ordinary Java thread running inside a native
          method. The native thread remains attached to the VM until it calls
          DetachCurrentThread() to detach itself.




          For method id and your callback object, you need to create a global references:



           gbl_method = jenv->NewGlobalReference( mid );
          gbl_callback = jenv->NewGlobalReverend( jarg2 );


          That does create one serious problem, though - that object reference held in gbl_callback will result in the Java object never getting garbage collected.



          To return a C++ callback function, you'd first need to convert the function pointer to a valid Java type. Strict conformance with the C standard (JNI is really C and not C++, so passing objects back and forth gets a bit murky when you mix in C++) means you can't treat the function pointer as anything other than a series of bytes, which means you'd need to convert the function pointer to a Java byte array and return that to Java. Then convert that Java byte array back to a function pointer when you want to use it in native code. That's a lot of code, and in JNI the more code you write the more likely you'll break something - JNI is fragile.



          But on both Windows and POSIX, a function pointer will fit into a jlong, and a jlong can accurately represent a function pointer's value, so that's a hack that works (and under POSIX it's probably not even a hack, but I'm not going to try finding the actual POSIX function pointer specifications that would allow that cast).



          The easiest way to do that is to create a long field in your Java object, and return the function pointer cast to a jlong:



          JNIEXPORT jlong JNICALL Java_apiJNI_Info_1t_1callbackTest_1set(
          JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jobject jarg2)

          ...

          return( ( jlong ) WrapperFunction );



          That would be called something like this:



          // Info_t has a long field called callback
          Info_t info_t = new Info_t();
          info_t.callback = info_t.setCallbackTest(new CallbackTest()
          @Override
          public void onCallback(int num)
          System.out.println("callback: " + num);

          );


          In my experience, the less JNI calls you make, the better. So if you can pass a value via a parameter or return it from a function call, that's simpler, safer, and a lot more reliable than trying to get/set object field values with JNI calls.



          But then you'd need to pass the callback value as another parameter:



          // function pointer, returns void, takes int arg
          typedef void (*funcPtr)( int );

          JNIEXPORT jobject JNICALL Java_apiJNI_Info_1t_1callbackTest_1get(
          JNIEnv *jenv, jclass jcls, jlong callback, jlong jarg1, jobject jarg1_)

          funcPtr f = ( funcPtr ) callback;

          f( 4 );

          ...







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Nov 14 '18 at 15:07

























          answered Nov 14 '18 at 14:18









          Andrew HenleAndrew Henle

          19.8k21333




          19.8k21333












          • Are you reffering to the part of the code where I defined global JEnv* and did gbl_env = jenv;? If you do then how else can one map Java interfaces to the function pointers in C++?

            – Lisek
            Nov 14 '18 at 14:23






          • 2





            @Lisek The quote in the answer already outlines what needs to be done. See this answer for more details.

            – Michael
            Nov 14 '18 at 14:31











          • Thanks Michael. That points out the flaw of my code but still looking for the answer how could I map function pointer to the Java object.

            – Lisek
            Nov 14 '18 at 14:35











          • @Lisek That points out the flaw of my code but still looking for the answer how could I map function pointer to the Java object. There is no way to call non-JNI type functions such as your void WrapperFunction(int a) directly from Java code. However, you can save function pointers as jlong values. See edits for details.

            – Andrew Henle
            Nov 14 '18 at 15:07

















          • Are you reffering to the part of the code where I defined global JEnv* and did gbl_env = jenv;? If you do then how else can one map Java interfaces to the function pointers in C++?

            – Lisek
            Nov 14 '18 at 14:23






          • 2





            @Lisek The quote in the answer already outlines what needs to be done. See this answer for more details.

            – Michael
            Nov 14 '18 at 14:31











          • Thanks Michael. That points out the flaw of my code but still looking for the answer how could I map function pointer to the Java object.

            – Lisek
            Nov 14 '18 at 14:35











          • @Lisek That points out the flaw of my code but still looking for the answer how could I map function pointer to the Java object. There is no way to call non-JNI type functions such as your void WrapperFunction(int a) directly from Java code. However, you can save function pointers as jlong values. See edits for details.

            – Andrew Henle
            Nov 14 '18 at 15:07
















          Are you reffering to the part of the code where I defined global JEnv* and did gbl_env = jenv;? If you do then how else can one map Java interfaces to the function pointers in C++?

          – Lisek
          Nov 14 '18 at 14:23





          Are you reffering to the part of the code where I defined global JEnv* and did gbl_env = jenv;? If you do then how else can one map Java interfaces to the function pointers in C++?

          – Lisek
          Nov 14 '18 at 14:23




          2




          2





          @Lisek The quote in the answer already outlines what needs to be done. See this answer for more details.

          – Michael
          Nov 14 '18 at 14:31





          @Lisek The quote in the answer already outlines what needs to be done. See this answer for more details.

          – Michael
          Nov 14 '18 at 14:31













          Thanks Michael. That points out the flaw of my code but still looking for the answer how could I map function pointer to the Java object.

          – Lisek
          Nov 14 '18 at 14:35





          Thanks Michael. That points out the flaw of my code but still looking for the answer how could I map function pointer to the Java object.

          – Lisek
          Nov 14 '18 at 14:35













          @Lisek That points out the flaw of my code but still looking for the answer how could I map function pointer to the Java object. There is no way to call non-JNI type functions such as your void WrapperFunction(int a) directly from Java code. However, you can save function pointers as jlong values. See edits for details.

          – Andrew Henle
          Nov 14 '18 at 15:07





          @Lisek That points out the flaw of my code but still looking for the answer how could I map function pointer to the Java object. There is no way to call non-JNI type functions such as your void WrapperFunction(int a) directly from Java code. However, you can save function pointers as jlong values. See edits for details.

          – Andrew Henle
          Nov 14 '18 at 15:07



















          draft saved

          draft discarded
















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid


          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.

          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53301963%2freturn-function-pointer-from-c-behavior-to-java-object-with-jni%23new-answer', 'question_page');

          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          這個網誌中的熱門文章

          What does pagestruct do in Eviews?

          Dutch intervention in Lombok and Karangasem

          Channel Islands