Reputation:
From C
using JNI
, I would like to create a Java
object that is then assigned to a variable declared in Java
. I have been reading a lot about this subject but so far could not reach to any conclusion or code a solution. Basically, what I would like to do is the following:
In Java
:
(...)
Object myVar;
c_function(myVar);
System.out.println(myVar); // print the content of myVar which was created in C
(...)
In C
:
#include "jni.h"
(...)
void c_function(jobject my_var)
{
jclass jcls;
jcls = env->FindClass("java/lang/Object");
my_var = env->AllocObject(jcls);
}
(...)
Upvotes: 0
Views: 290
Reputation: 719219
You appear to be trying to using JNI to implement a method with a pass by reference argument.
Sorry, but it won't work. Java arguments are pass by value. Period.
No matter what you do in the C code, the Java code is going to pass the value of myVar
as a call argument. Since the C side is never told what address of myVar
is, and since (in general) it cannot find this out, you cannot make call by reference work.
Instead, you need to use one of the standard workarounds for this limitation in Java:
Just return the value as a regular result and rely on the caller to assign it to the destination variable.
Return multiple value as a result array or object and rely on the caller to assign the values to the destination variables.
Pass an array or object as an argument and return values by modifying its cells of fields.
In Java 9+ create and pass a VarHandle
; see https://www.baeldung.com/java-variable-handles.
Use reflection. This only works if myVar
is a class or instance field, and you will need to pass the name of the field as a String
or in the form of a Field
reference.
On the C size, just perform the relevant operations on the Java objects using JNI operations.
It is possible (in theory) for the C code to break JNI abstraction and try to find out the address of myVar
on the stack and assign stuff to it directly.
This is nasty / dangerous / non-portable stuff. It is liable to fail if low-level JVM implementation details change from one platform to the next. (Including JVM versions on the same hardware.)
If you mess up, you are liable to corrupt stack or heap memory with unpredictable consequences.
There is no good way for the C side to figure out which variable the caller is passing as a parameter. That's fine it it is always the same one. But my reading of the question is that you expect this to work for an arbitrary (reference) variable.
Upvotes: 1
Reputation: 2422
Java is pass-by-value. So you can't modify value of reference even using JNI. Here are some options you can use
You can change signature of your function from
void c_function(Object o)
to
Object c_function()
So you can use the following C implementation
JNIEXPORT jobject JNICALL Java_Main_func (JNIEnv *env, jclass cls) {
jclass jcls = (*env)->FindClass(env, "java/lang/Object");
jobject obj = (*env)->AllocObject(env, jcls);
return obj;
}
You can use AtomicReference
, Object[]
or your own custom implmentation.
Here is an example with Object[]
Java code:
public class Main {
static {
System.loadLibrary("Main");
}
public static native void func(Object[] o);
public static void main(String[] argv) {
Object[] v = new Object[] { null };
func(v);
System.out.println(v[0]);
}
}
C code:
JNIEXPORT void JNICALL Java_Main_func (JNIEnv *env, jclass cls, jobjectArray jarr) {
jclass jcls = (*env)->FindClass(env, "java/lang/Object");
jobject obj = (*env)->AllocObject(env, jcls);
(*env)->SetObjectArrayElement(env, jarr, 0, obj);
}
Upvotes: 1