How to Pass C Structs Back and Forth to Java Code in Jni

How to pass C structs back and forth to Java code in JNI?

You need to create a Java class with the same members as C struct, and 'map' them in the C code via methods env->GetIntField, env->SetIntField, env->GetFloatField, env->SetFloatField, and so on - in short, lots of manual labor, hopefully there already exist programs that do it automatically: JNAerator (http://code.google.com/p/jnaerator) and SWIG (http://www.swig.org/). Both have their pros and cons, the choice is up to you.

Passing large C structure through JNI efficiently

For pure efficiency, it's best to minimize calls that go through JNI, so your best bet is sending all the data in either through option 1 and creating a new object.

You could also have a "recieveUpdate( ... 40 params ...)" method on the Java side if you wanted to avoid allocating a new object per update, though it's cringeworthy design.

How to pass C structs back and forth to Java code in JNI?

You need to create a Java class with the same members as C struct, and 'map' them in the C code via methods env->GetIntField, env->SetIntField, env->GetFloatField, env->SetFloatField, and so on - in short, lots of manual labor, hopefully there already exist programs that do it automatically: JNAerator (http://code.google.com/p/jnaerator) and SWIG (http://www.swig.org/). Both have their pros and cons, the choice is up to you.

How to pass a c structure from native c library to java and access its elements

Create a Java object in your C code and populate it with the data from the C struct. See the accepted answer for How to pass C structs back and forth to Java code in JNI? for more details.

JNI: From C code to Java and JNI

For question #1:

You can use a jchar. Primitive chars in java are not signed, it's about the only primitive that isn't. Note that jchar is a UTF-16 char, so you will have to "map" the jchar to a regular char, as you would have to with any character conversion issue. For simple conversions, this can typically be done by casting

char c_char = (char)java_char;

because the core ASCII shares the same numeric values between ASCII and UTF-16. However, this is prone to error should anyone actually attempt to pass a "special" character through the interface. A much better way would be to (in the java side, as it is easier) convert the characters to bytes using the appropriate character set for your platform (to ensure platform compatibility in the C layers). Then you only need to pass a byte[] to the JNI call, and the bytes will correctly correspond to the characters that C likely will expect.

For question #2:

If your CheckEnrollmentExists(...) method is the JNI binding entry point, you cannot change data types safely. That means that all entry inputs must be JNI data type values. While you might be able to select the C data type equivalents (and you might be able to get your compiler to do it anyway) such techniques should be frowned upon. This implicitly means that JNI entry points cannot accept struct data structure not defined in the JNI headers. In other words, you can't pass your own struct to the method.

If the method needs access to a C struct across calls, use another means. I've seen people store the pointer to the allocated data structure in a member integer or long (doing correct casting). You can then rewrite the native code side to retrieve the pointer from the "this" object being passed into the call, and the do a dereference to obtain the required data.

For Question #3:

This is actually the same as question #2. In the "binding wrapper" you put, you would retrieve the pointer's stored value in the java object's int or long field, cast it to the appropriate struct pointer and then pass it to the internal method. As the passing of the pointer is a C to C call, no extra magic is required.

Update nested java objects from C

In a nutshell, you need to do the following:

// Get foo.g
jclass cls_struct2 = env->FindClass("struct2");
jfieldID fld_struct2_g = env->GetFieldID(cls_struct2, "g", "[Lstruct2;");
jarray foo_g = (jarray) env->GetObjectField(foo, fld_struct2_g);

// Look up field IDs of struct1
jclass cls_struct1 = env->FindClass("struct1");
jfieldID fld_struct1_a = env->GetFieldID(cls_struct1, "a", "C");
jfieldID fld_struct1_d = env->GetFieldID(cls_struct1, "d", "[C");

// Loop over the array
jsize s = env->GetArrayLength(foo_g);
for (int i = 0; i < s; i++) {
jobject element = env->GetObjectArrayElement(foo_g, i);
env->SetCharField(element, fld_struct1_a, _struct2[i].a);
jcharArray element_d = (jcharArray) env->GetObjectField(element, fld_struct1_d);
env->SetCharArrayRegion(element_d, 0, sizeof(_struct2[i].d) / sizeof(_struct2[i].d[0]), _struct2[i].d);
}

If the char[20] fields are actually null-terminated strings, you may be better off converting them to String and using NewStringUTF on the C++ side.

EDIT: The answer above is C++ instead of C, but that is mostly cosmetic (env->Method(ARGS) should be (*env)->Method(env, ARGS)

EDIT 2: From your question it is not clear if your Java code is in a namespace. If it is, you may need pkg/path/to/MyOuterClass$struct1.

EDIT 3: Corrected modification of .d

How to pass a com.sun.jna.Structure from Java to struct in C, using JNA

Don't mark the fields as volatile. When you do that, JNA won't write to them unless you do an explicit Structure.writeField().



Related Topics



Leave a reply



Submit