Reputation: 3394
I have a project written in C++ and I'm looking to write a Java GUI as an interface to it. The choice of Java is fixed so I'd need to learn how to be able to call the C++ code from Java. Rewriting the C++ code is not an option. I'd like input on:
Thanks.
Upvotes: 13
Views: 23252
Reputation: 58770
Compile the GUI using GCJ, and use the CNI to wrap the C++ code. See http://gcc.gnu.org/java/faq.html#6_2 for some comparative examples of using CNI versus JNI (what you'd use with all other Java runtimes.)
The CNI is supposed to be a lot simpler than JNI.
Upvotes: 0
Reputation: 15656
JNI
JNA
Both of them are platform independent. I prefer jna myself since it is easy to mess up jni code. There are several platform dependent solutions around, too - but I don't know them that well.
Upvotes: 5
Reputation: 41
I'd go for some form of IPC (pipes, maybe even sockets). This way, your code is reduced to copying to and from byte arrays in C++, and using InputStreams and OutputStreams in Java.
I've recently worked on a project where we had a library distributed by a third party, which was written in C++. But every system we have that might use this library was written in Java.
We went down the route of wrapping the library as a native executable, that reads input from stdin, translating it to function calls to the library. Correspondingly, results from the library were converted and printed to stdout.
This also meant that the wrapper was easy to test, since all I had to do was invoke it on the command line. Spotted a lot of bugs and problems because of this. Thoroughly recommend it.
The next problem was 'How do I invoke the C++ wrapper' and 'How do I package it with the Java app'? We actually avoided the answers to these questions, by exposing the executable via inetd. So our java applications invoked the C code by opening a socket. Because I'd coded the wrapper to communicate via stdout and stdin, I didn't have to modify it at all to expose it via TCP (Read up on inetd if you're puzzled). The neatest little bit of programming I've ever done... :-)
Sorry I went off on a tangent up there. I was trying to illustrate the flexibility you can get if you decide to separate the C++ code into a separate process. The point is, you've done the work of marshalling your data structures up front. So initially, you might keep your other process local and communicate to it via a pipe. The thing is, if you ever decide that you need to send the requests to a remote TCP server, it wouldn't take a lot of effort to change the java program. You've already done the bulk of the work. It may be worth considering. Whether it's actually suitable for you, I don't know.
I haven't coded with JNI, but we do have apps that use it at work, and they all suffer memory management issues:
1) If the C/C++ code makes a mistake with pointer arithmetic, your Java app's screwed as well. It'll probably die with a SIGSEGV. So your C/C++ code better be solid, well tested and worth your trust. With the project I was working on, most of the Java apps were server processes, that are supposed to work 24/7. We didn't trust this third party library that much, useful as it may be.
2) Passing data structures between the Java and C++ can be troublesome. Especially if you pass Java objects into the C functions. You usually have to perform some sort of conversion, which raises such issues like 'should I copy this data structure? When should I destroy it?' It's especially bad if you inadvertently call 'free' on some object that was allocated in the Java program.
3) I've seen some JNI code. It just looks ugly... [barf]
Sorry for the long post.
Upvotes: 3
Reputation: 12443
I recently worked on a project in which we had to do exactly the same thing. We had a data model written in C++ and needed to put a Java GUI on top. What we ended up doing was identifying the C++ classes that we needed to access from the GUI and used SWIG to generate plain old java classes which wrapped the C++ objects.
The Java classes generated by SWIG have identical interfaces to the C++ classes they are wrapping which means that communicating with the C++ objects, from Java, involves only working with Java objects.
Here is an example:
Given two C++ classes:
class SomeClass {
public:
void foo(SomeOtherClass bar);
SomeOtherClass baz();
}
class SomeOtherClass {
public:
void do();
}
SWIG will generate two Java classes:
public class SomeClass {
public void foo(SomeOtherClass bar);
public SomeOtherClass baz();
}
public class SomeOtherClass {
public void do();
}
Calling C++ objects, from Java, is just like writing regular Java:
SomeClass sc = new SomeClass();
SomeOtherClass soc = sc.baz();
sc.foo(soc);
Line 1: The Java wrapper of SomeClass is instantiated as well as a C++ object of type SomeClass.
Line 2: Calls to the sc instance of SomeClass are forwarded to the C++ instance of SomeClass. The return value of the C++ instance is passed to the Java wrapper and returned by the Java wrapper.
Line 3: SWIG handles converting from the Java wrapper types (or java primitive types) to the underlying C++ types.
SWIG will take care of converting to/from Java/C++ types during method invocations and all the JNI details are hidden away from view :)
The SWIG interface code needed to generate a Java wrapper for a C++ class can be as simple as:
interface.i: { #include "ClassDefinition.h" } %include "ClassDefinition.h"
SWIG is very powerful. Anything you need to do you can do either with the basic features, typemaps, javacode type maps, or directors.
SWIG also allows your C++ code to call your Java objects without any modification to your existing C++ code. This is a feature called "cross language polymorphism." cross language polymorphism essentially lets you create Java classes that subclass C++ classes. You can then pass instances of those Java classes as parameters to C++ method calls. Any invocations, from C++, on the passed in instance will be forwarded back to your Java object. I won't get into the details here, but it isn't terribly complicated once you get over the initial shock of the idea.
Upvotes: 9
Reputation: 17017
Depending on how tightly coupled the interface needs to be to the C++ code, the easiest thing to do might be to run the GUI and the C++ code as separate programs that communicate via some kind of IPC (sockets, named pipes on Unix, etc.)
The other alternative that I have heard about, but never done, is to create a JNI (Java Native Interface) wrapper around your C++ code. From what I have head, this isn't a straight forward exercise.
Upvotes: 2