Tuesday, March 27, 2007

JNI, C++ object instantiation

So my next task is to create a java gui using a C++ library.
The library is pretty simple. There is one main class with a few supporting classes. The supporting classes are basically just structs which hold variables.
The first thing to be done is create a Java class similar to the main C++ class. This is called a peer class. The methods in the java class are native functions whose implementations will be done using the Java Native Interface (JNI).
In the java class constructor, a jni call to create the C++ class will need to be made. The java class will also need to override the finalize function in which it will have a jni call to delete the C++ class.
The java class also needs to contain a variable which will be used to contain the pointer to the C++ class.
Now my first problem: How do I implement the JNI function which creates the C++ class? If anyone knows the answer, feel free to leave me a comment.
According to this link, a reinterpret cast is involved.

C++ class:

#include "CPPObject.h"
#include


CppObject::CppObject(int aFoo) {
foo = aFoo;
}


void CppObject::printFoo() {
printf ("Value of foo is: %i\n", foo);
}

Java peer class:

public class CppObjectPeer {
static {
System.loadLibrary("cppobjects");
}
protected long ptr;
public CppObjectPeer(int aFoo) {
this.ptr = createCppObject(aFoo); //create object in native code
}
public void printFoo() {
printFoo(this.ptr); //print from native code
}
//native methods
private final native long createCppObject(int aFoo);
private native void printFoo(long ptr);
}


Jni implementations:

JNIEXPORT jlong JNICALL Java_CppObjectPeer_createCppObject
(JNIEnv *env, jobject obj, jint fooValue) {
CppObject *cppObj = new CppObject(fooValue);
return reinterpret_cast(cppObj);
}


JNIEXPORT void JNICALL Java_CppObjectPeer_printFoo
(JNIEnv *env, jobject obj, jlong ptr) {
CppObject *cppObj = reinterpret_cast(ptr);
cppObj->printFoo();
}


Well, it looks simple enough. Just not sure why the book: Essential Jni: Java Native Interface (Essential Java), by Rob Gordon doesn't use this technique. Is this a new technique?

The book uses a Registry class and a hash code function to store the pointer to the C++ object. This method seems like a lot of work for what I need. As an alternative, he suggests using get/setCID functions which call get/setLongField functions. The set functions requires an int val be passed in and I don't understand where the val comes from. I guess for now I'm going to try the reinterpret_cast method.
Stay tuned for how it works out.




3 comments:

Anonymous said...

how did it work?

Ice Princess said...

It worked out great, except in place of the reinterpret_cast call, I cast the return value to a (jlong) in the jni Java_CppObjectPeer_createCppObject function.
When the jlong value is passed into the jni Java_CppObjectPeer_printFoo function,
I cast to the object type instead of using the reinterpret_cast:
CppObject *cppObj = (CppObject*) (ptr);

Unknown said...

Hello,

I was looking for some tutorial for communicating between C++ class functions and Java, and I found your blog. Thank you for sharing what you found and how you made it working. It was quite useful.

I tried to work on the code that you posted, but am getting error:

In function Java_CppObjectPeer_printFoo':
CppObjectPeer.cpp:(.text+0x73): undefined reference to `CPPObject::printFoo()'
collect2: ld returned 1 exit status

The following is the implementation of the functions, like you suggested:
#include "CppObjectPeer.h"
#include "CPPObject.h"
#include

JNIEXPORT jlong JNICALL Java_CppObjectPeer_createCppObject (JNIEnv *env, jobject obj, jint fooValue) {
CPPObject *cppObj = new CPPObject(fooValue);
return (jlong)(cppObj);
}

JNIEXPORT void JNICALL Java_CppObjectPeer_printFoo (JNIEnv *env, jobject obj, jlong ptr){
CPPObject *cppObj = (CPPObject*)(ptr);
cppObj->printFoo();
}

Did you get any such error?

Thank you.