This blog post is more of a question to others (probably much more knowledgeable people than me) who have already wrapped C++ libraries in Java. So if you have and the strategy I document as part of this blog post is completely wrong please comment and point me towards a better one!
Anyways let’s get started!
We take a very simply case where we have a C++-Class like this:
#ifndef CTESTSIMPLE_H #define CTESTSIMPLE_H class CTestSimple { public: CTestSimple(); virtual int test(); int test_delegate(); }; #endif // CTESTSIMPLE_H
#include "ctestsimple.h" CTestSimple::CTestSimple() { } int CTestSimple::test() { return 100; } int CTestSimple::test_delegate() { return test(); }
and in java we want to do the following things:
import testjni.TestSimple; public class TestApp { public static void main(String[] args) { System.load("/Users/tomschindl/build-JTestJNI/libJTestJNI.dylib"); System.out.println("===== Simple"); TestSimple t = new TestSimple(); System.out.println("Direct: " + t.test()); System.out.println("Delegate:" + t.test_delegate()); System.out.println("===== No Override"); NoOverride tn = new NoOverride(); System.out.println("Direct: " + tn.test()); System.out.println("Delegate: " + tn.test_delegate()); System.out.println("===== With Override"); SubTestSimple ts = new SubTestSimple(); System.out.println("Direct: " + ts.test()); System.out.println("Delegate: " + ts.test_delegate()); System.out.println("===== With Override"); SubSuperTestSimple tss = new SubSuperTestSimple(); System.out.println("Direct: " + tss.test()); System.out.println("Delegate: " + ts.test_delegate()); } static class SubTestSimple extends TestSimple { @Override public int test() { return 0; } } static class SubSuperTestSimple extends TestSimple { @Override public int test() { return super.test() + 10; } } static class NoOverride extends TestSimple { } }
We expect the application to print:
===== Simple Direct: 100 Delegate:100 ===== No Override Direct: 100 Delegate: 100 ===== With Override Direct: 0 Delegate: 0 ===== With Override & super Direct: 110 Delegate: 110
The strategy I found to make this work looks like this:
- On the C++ side one needs to define a subclass of CTestSimple I named JTestSimple
#ifndef JTESTSIMPLE_H #define JTESTSIMPLE_H #include "ctestsimple.h" #include <jni.h> class JTestSimple : public CTestSimple { private: jobject jObject; JNIEnv* env; jmethodID jTest; public: JTestSimple(JNIEnv *,jobject,jboolean derived); virtual int test(); }; #endif // JTESTSIMPLE_H
#include "jtestsimple.h" #include <iostream> #include <jni.h> JTestSimple::JTestSimple(JNIEnv * env, jobject o, jboolean derived) { this->jObject = env->NewGlobalRef(o); this->env = env; this->jTest = 0; if( derived ) { jclass cls = env->GetObjectClass(this->jObject); jclass superCls = env->GetSuperclass(cls); jmethodID baseMethod = env->GetMethodID(superCls,"test","()I"); jmethodID custMethod = env->GetMethodID(cls,"test","()I"); if( baseMethod != custMethod ) { this->jTest = custMethod; } } } int JTestSimple::test() { if( this->jTest != 0 ) { return env->CallIntMethod(this->jObject,this->jTest); } return CTestSimple::test(); }
The important step is to check in if the java-object we got passed in has overwritten the test()-method because in
that case we need to call from C++ to java for the correct result. - On the Java side we define our object like this
package testjni; public class TestSimple { private long NativeObject; public TestSimple() { NativeObject = createNativeInstance(); } protected long createNativeInstance() { return Native_new(getClass() != TestSimple.class); } public int test() { if( getClass() == TestSimple.class ) { return Native_test(); } else { return Native_test_explicit(); } } public final int test_delegate() { return Native_test_delegate(); } private native long Native_new(boolean subclassed); private native int Native_test(); private native int Native_test_explicit(); private native int Native_test_delegate(); }
Some remarks are probably needed:
- Notice that the there are 2 native delegates for
test()
depending on fact whether the method is invoked in subclass or not test_delegate()
is final because it is none-virtual on the C++ side and hence is not subject to overloading in subclasses
- Notice that the there are 2 native delegates for
- On the JNI-Side
#include <jni.h> #include <stdio.h> #include <iostream> #include "testjni_TestSimple.h" #include "jtestsimple.h" JNIEXPORT jint JNICALL Java_testjni_TestSimple_Native_1test (JNIEnv * env, jobject thiz) { jclass cls = env->GetObjectClass(thiz); jfieldID id = env->GetFieldID(cls,"NativeObject","J"); jlong pointer = env->GetLongField(thiz,id); JTestSimple* obj = (JTestSimple*)pointer; return obj->test(); } JNIEXPORT jint JNICALL Java_testjni_TestSimple_Native_1test_1explicit (JNIEnv * env, jobject thiz) { jclass cls = env->GetObjectClass(thiz); jfieldID id = env->GetFieldID(cls,"NativeObject","J"); jlong pointer = env->GetLongField(thiz,id); JTestSimple* obj = (JTestSimple*)pointer; return obj->CTestSimple::test(); } JNIEXPORT jint JNICALL Java_testjni_TestSimple_Native_1test_1delegate (JNIEnv * env, jobject thiz) { jclass cls = env->GetObjectClass(thiz); jfieldID id = env->GetFieldID(cls,"NativeObject","J"); jlong pointer = env->GetLongField(thiz,id); JTestSimple* obj = (JTestSimple*)pointer; return obj->test_delegate(); } JNIEXPORT jlong JNICALL Java_testjni_TestSimple_Native_1new (JNIEnv * env, jobject thiz, jboolean derived) { jlong rv = (jlong)new JTestSimple(env, thiz, derived); return rv; }
The only interesting thing is the
Java_testjni_TestSimple_Native_1test_1explicit
who does not invoke the test-method on JTestSimple but on its super class CTestSimple.
So my dear C++/Java gurus how bad / dump / … is this strategy?
I would always prefer to use a toolkit for this. In a recent case, SWIG showed to be an easy to use and fully working choice (with additional benefit like spitting out a Python interface if needed).
SWIG of course is an option!
Some years ago I have been working on a bindings generator which we used for a 3d rendering engine.
I that project we used the same approach to store address of c++ objects in a java member. But we did not need to override c++ methods in java, so your JTestSimple was not required for us.
Your code makes sense to me.
Regards,
Christoph
https://code.google.com/p/xbig/
https://code.google.com/p/ogre4j/
http://www.ogre3d.org/
Hi Tom,
I’m neither a C++ nor a JNDI expert but why do you exactly need this kind of complexity (concerning inheritance)? Because if I want to override a method in a sub-class, why should I want to call the method from a the super class depending on some c-logic?
For me it would make more sense to stay with the standard Java behavior. Means if the sub-class wants to call the super method is should anounce this with “super.test()”. With this behavior a normal Java-Developer would see the super call and doesn’t have to know the logic in the c-code.
So for me it would be:
package testjni;
public class TestSimple {
private final long NativeObject;
public TestSimple() {
NativeObject = createNativeInstance();
}
private final long createNativeInstance() {
return Native_new(getClass() != TestSimple.class);
}
public int test() {
return Native_test();
/*
if( getClass() == TestSimple.class ) {
return Native_test();
} else {
return Native_test_explicit();
}
*/
}
public final int test_delegate() {
return Native_test_delegate();
}
private native long Native_new(boolean subclassed);
private native int Native_test();
// private native int Native_test_explicit();
private native int Native_test_delegate();
}
And a Sub-Class would look like
public class SubTestSimple extends TestSimple {
@Override
public int test() {
return 0;
}
}
And Sub-Super-Class would look like
public class SubSuperTestSimple extends TestSimple {
@Override
public int test() {
return super.test() + 10; // here i can see this want’s to have the super call which in will got to the native code
}
}
Regards
WhoCares
Your code will end up in an endless loop – when the method from the base-class is invoked we never should call back from C++ to Java
Have you looked at JNA or gluegen?
gluegen & JNA are only for c if I get that right and so you’d have to write some C glue code. The task at hand is that I need to be able to subclass C++-Classes in Java!
Well there is JNAerator which claims to handle C++ to JNA bridging. Never used it but might work. There also used to be something (I think its most recent name was Koala, but look for the work of Richard Dale, he was the original author) that was used to generate the Java bindings for Qt and KDE. Those bindings have rather bitten the dust but the code is probably still in the archives somewhere.
Thanks for the pointers!
Remi Forax suggests JNR [1] (example here [2]) In Java 9 there will be a new project called panama [3] (same team).
[1] https://github.com/jnr/
[2] https://github.com/jnr/jnr-invoke
[3] http://openjdk.java.net/projects/panama/
Yeah, subclassing C++ classes with Java classes certainly adds to the complexity. But it looks like your approach is something I’d do.
And don’t trust any tools that generates this for you. You’ll end up fighting it since it won’t generate code the way you want it to. Better to hand code it yourself, or build you’re own generator ;).
The plan is to have my own generator and get the meta from an DSL instead of the c++ headers
In XBiG we used Doxygens XML output as meta and generated code from that