Some time ago, I played with qtjava and gcj, creating a native code version of a java application that called Qt. However, the qtjava library had already been wrapped in jni for us by Richard Dale. Going the other way, wrapping Java code in CNI and calling that from C++ was more challenging, especially since I have only a very sketchy knowledge of gcc's command-line options. That's what comes from trusting to automake. You don't learn how to craft a compile command yourself anymore. But today I succeeded in creating a small natively compiled shared library in Java which I could call from C++. Not rocket science, but nice nonetheless.
First, I created a small java class, together with a bit of testcode so I could verify the C++ code would do the same thing:
public class ByteMucker {
private byte[] otherBytes;
private byte[] muckedUpBytes;
public ByteMucker() {
otherBytes = new byte[50000];
otherBytes[300] = 50;
}
public void muckBytes(byte[] freshBytes) {
muckedUpBytes = freshBytes;
muckedUpBytes[300] = 100;
otherBytes[300] = muckedUpBytes[300];
}
public byte[] getMuckedUpBytes() {
return muckedUpBytes;
}
public byte[] getOtherBytes() {
return otherBytes;
}
public static void main(String[] args) {
System.out.println("Going to muck things up.");
ByteMucker byteMucker = new ByteMucker();
System.out.println("1 other: " + byteMucker.getOtherBytes()[300]);
byte[] bytes = new byte[50000];
System.out.println("2 this: " + bytes[300]);
byteMucker.muckBytes(bytes);
System.out.println("3 this: " + bytes[300]);
System.out.println("4 other: " + byteMucker.getOtherBytes()[300]);
bytes[300] = 10;
System.out.println("5 this: " + byteMucker.getMuckedUpBytes()[300]);
System.out.println("6 other: " + byteMucker.getOtherBytes()[300]);
}
}
If you want to make library callable from C++ from this class, your first need to compile it to bytecode, and then generate a C++ header file from the bytecode with gcjh, so you can #include that from your C++ code:
gcj -C ByteMucker.java gcjh ByteMucker
Then we can create our shared library. That's not different from what I did last time, to create a shared library from qtjava.jar:
gcj -O2 -shared ByteMucker.java -o bm.so
Now we have everything, and can start on the C++ side of things. This is my C++ class:
using namespace std;
int main (int argc, const char* argv[]) {
cout << "Going to muck bytes" << endl;
using namespace java::lang;
try {
JvCreateJavaVM(NULL);
JvAttachCurrentThread(NULL, NULL);
System::out->println(JvNewStringLatin1("Going to muck things up"));
ByteMucker *mucker = new ByteMucker();
cout << "1 other: "
<< elements(mucker -> getOtherBytes())[300]
<< "\n";
jbyteArray bytes = JvNewByteArray(50000);
cout << "2 this: " << elements(bytes)[300] << "\n";
mucker -> muckBytes(bytes);
cout << "3 this: " << elements(bytes)[300] << "\n";
cout << "4 other: "
<< elements(mucker -> getOtherBytes())[300]
<< "\n";
elements(bytes)[300] = 10;
cout << "5 this: "
<< elements(mucker -> getMuckedUpBytes())[300]
<< "\n";
cout << "6 other: "
<< elements(mucker -> getOtherBytes())[300]
<< "\n";
JvDetachCurrentThread();
}
catch (Throwable *t) {
System::err->println(JvNewStringLatin1("exception: "));
t -> printStackTrace();
}
}
The interesting bits are all explained in the gcj manual, but that's a very sparse document. I had to google to find out how to create a new byte array -- JvNewByteArray(), but from there it was plain sailing. Notice that we can both use C++ cout and Java's println. Creating Java objects, even from C++, makes them owned by the Java runtime environment, which means they are managed by the garbage collector.
Compiling this file was easy, once I figured out the correct command line. I haven't found out how to integrate a mixed C++/Java project with either Ant or automake, although bits and pieces are starting to come together.
g++ muck.cc -lgcj bm.so -o muck
Running muck and gij ByteMucker gives the same result, as it should:
boud@talnus:~/prj/javaso> gij ByteMucker Going to muck things up. other: 50 this: 0 this: 100 other: 100 this: 10 other: 100 boud@talnus:~/prj/javaso> ./muck Going to muck bytes Going to muck things up 1 other: 50 2 this: 0 3 this: 100 4 other: 100 5 this: 10 6 other: 100
Nice... Now perhaps I should rewrite Krita to use the Java Advanced Imaging API, only there isn't a free implementation, and I've sworn never, ever to let my code get captured by a proprietary environment again. Besides, much good work on Krita is happening right now for which we don't need Java.