Return function pointer from C++ behavior to Java object with JNI
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
add a comment |
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
add a comment |
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
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
java c++ jni function-pointers swig
edited Nov 14 '18 at 14:05
Lisek
asked Nov 14 '18 at 13:59
LisekLisek
142217
142217
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
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 callAttachCurrentThread()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 );
...
Are you reffering to the part of the code where I defined globalJEnv*and didgbl_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 yourvoid WrapperFunction(int a)directly from Java code. However, you can save function pointers asjlongvalues. See edits for details.
– Andrew Henle
Nov 14 '18 at 15:07
add a comment |
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
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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 callAttachCurrentThread()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 );
...
Are you reffering to the part of the code where I defined globalJEnv*and didgbl_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 yourvoid WrapperFunction(int a)directly from Java code. However, you can save function pointers asjlongvalues. See edits for details.
– Andrew Henle
Nov 14 '18 at 15:07
add a comment |
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 callAttachCurrentThread()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 );
...
Are you reffering to the part of the code where I defined globalJEnv*and didgbl_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 yourvoid WrapperFunction(int a)directly from Java code. However, you can save function pointers asjlongvalues. See edits for details.
– Andrew Henle
Nov 14 '18 at 15:07
add a comment |
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 callAttachCurrentThread()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 );
...
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 callAttachCurrentThread()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 );
...
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 globalJEnv*and didgbl_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 yourvoid WrapperFunction(int a)directly from Java code. However, you can save function pointers asjlongvalues. See edits for details.
– Andrew Henle
Nov 14 '18 at 15:07
add a comment |
Are you reffering to the part of the code where I defined globalJEnv*and didgbl_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 yourvoid WrapperFunction(int a)directly from Java code. However, you can save function pointers asjlongvalues. 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
add a comment |
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.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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