Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I've got some C functions which I am calling through JNI which take a pointer to a structure, and some other functions which will allocate/free a pointer to the same type of structure so that it is a bit easier to deal with my wrapper. Surprisingly, the JNI documentation says very little about how to deal with C structures.

My C header file looks like so:

typedef struct _MyStruct {   float member; } MyStruct;  MyStruct* createNewMyStruct(); void processData(int *data, int numObjects, MyStruct *arguments); 

The corresponding JNI C wrapper file contains:

JNIEXPORT jobject JNICALL Java_com_myorg_MyJavaClass_createNewMyStruct(JNIEnv *env, jobject this) {   return createNewMyStruct(); }  JNIEXPORT void JNICALL Java_com_myorg_MyJavaClass_processData(JNIEnv *env, jobject this, jintArray data,                                        jint numObjects, jobject arguments) {   int *actualData = (*env)->GetIntArrayElements(env, data, NULL);   processData(actualData, numObjects, arguments);   (*env)->ReleaseIntArrayElements(env, data, actualData, NULL); } 

...and finally, the corresponding Java class:

public class MyJavaClass {   static { System.loadLibrary("MyJniLibrary"); }    private native MyStruct createNewMyStruct();   private native void processData(int[] data, int numObjects, MyStruct arguments);    private class MyStruct {     float member;   }    public void test() {     MyStruct foo = createNewMyStruct();     foo.member = 3.14159f;     int[] testData = new int[10];     processData(testData, 10, foo);   } } 

Unfortunately, this code crashes the JVM right after hitting createNewMyStruct(). I'm a bit new to JNI and have no idea what the problem could be.

Edit: I should note that the C code is very vanilla C, is well-tested and was ported from a working iPhone project. Also, this project is using the Android NDK framework, which lets you run native C code from an Android project from within JNI. However, I don't think that this is strictly an NDK issue... it seems like a JNI setup/initialization error on my part.

like image 938
Nik Reiman Avatar asked Oct 13 '10 11:10

Nik Reiman


People also ask

What is Jclass in JNI?

typedef jobject jclass; In C++, JNI introduces a set of dummy classes to enforce the subtyping relationship. For example: class _jobject {}; class _jclass : public _jobject {}; ...

Does JNI makes the Java code machine dependent?

It allows Java code that runs inside a Java Virtual Machine (VM) to interoperate with applications and libraries written in other programming languages, such as C, C++, and assembly. The most important benefit of the JNI is that it imposes no restrictions on the implementation of the underlying Java VM.

What is JNI Bridge?

Introduction to Java Native Interface: Establishing a bridge between Java and C/C++ JNI (Java Native Interface) is a foreign function interface that allows code running on JVM to call (or be called by) native applications. Using JNI, one can call methods written in C/C++ or even access assembly language.

How does Java JNI work?

The JNI framework lets a native method use Java objects in the same way that Java code uses these objects. A native method can create Java objects and then inspect and use these objects to perform its tasks. A native method can also inspect and use objects created by Java application code.


2 Answers

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.

like image 112
iirekm Avatar answered Oct 09 '22 07:10

iirekm


It's crashing because Java_com_myorg_MyJavaClass_createNewMyStruct is declared to return jobject, but is actually returning struct MyStruct. If you ran this with CheckJNI enabled, the VM would complain loudly and abort. Your processData() function is also going to be fairly upset about what it gets handed in arguments.

A jobject is an object on the managed heap. It can have extra stuff before or after the declared fields, and the fields don't have to be laid out in memory in any particular order. So you can't map a C struct on top of a Java class.

The most straightforward way to deal with this was identified in an earlier answer: manipulate the jobject with JNI functions. Allocate the objects from Java or with NewObject, Get/Set the object fields with appropriate calls.

There are various ways to "cheat" here. For example, you could include a byte[] in your Java object that holds sizeof(struct MyStruct) bytes and then use GetByteArrayElements to get a pointer to it. A bit ugly, especially if you want to access the fields from the Java side as well.

like image 33
fadden Avatar answered Oct 09 '22 08:10

fadden