Using Java to wrap over C++
Asked Answered
L

6

13

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:

  1. What tools can I use to achieve this wrapping.
  2. How much of the C++ code would I have to necessarily modify, if any.
  3. Any other insights/follow up questions that you'd have.

Thanks.

Lemmie answered 28/7, 2010 at 18:34 Comment(0)
I
4

You should look for JNI - Java Native Interface

Impudicity answered 28/7, 2010 at 18:38 Comment(4)
does this really support C++, or does it only support functions, and no C++-classes/class-methods etc?Trepang
So what I'm seeing in JNI is that the wrappers are being written before the C++ (or whatever other language) code is actually introduced. I already have all the C++ code, so what is it that I'm not getting right about JNI?Lemmie
I believe you have to write something in C++ to call your code, and the rest in Java to call your wrapper. So, you will have your original code, the C++ wrapper accessing the original code, the Java JNI and the Java accessing the Java wrapper.Impudicity
@Lemmie Write a java class with the api you want to use and mark the methods native (see JNI documentation). Run javah on that class. Take the resulting header file and implement it in c/c++ (in c++ the functions must be extern "C" but I think jni.h takes care of that for you.Torrent
B
9

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.

http://www.swig.org/

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.

Brut answered 29/7, 2010 at 0:10 Comment(4)
SWIG has tended to introduce crashes of its own to code that would otherwise work just fine on its own, and it makes it hard to debug where the problem is.Penumbra
We've used it on two projects so far. The SWIG wrappers have been handling thousands of calls per second and we have wrapped hundreds of classes. We've yet to experience a crash that was a result of SWIG generated code. Admittedly there are many tricks and pitfalls since pointers to objects are being shared between the two languages.Brut
Hey, this looks pretty promising. I'll definitely look into it.Lemmie
@Lemmie If you don't want to rely on finalization to clean up the C++ objects created by the Java wrappers, you will want to look into the ReferenceQueue class: download-llnw.oracle.com/javase/6/docs/api/java/lang/ref/… You can use a reference queue to tell you when your SWIG objects are going to be garbage collected and then free their associated resources. You may also want to look into Boost smart pointers (or something similar) so you don't de-allocate a C++ object while Java has a hold on it, although you don't have to use smart pointers to manage this.Brut
B
5

JNI

  • If your code is c++
  • JNI is part of the java runtime
  • declare native functions and generate a c/c++ header with javah
  • write c/c++ code to glue your code to the java methods

JNA

  • If you have a c api for your code
  • does not work with c++ classes
  • JNA is not part of the java runtime, jna.jar is 300kb
  • declare a java interface with the c methods you use
  • write java code to glue your dll to java
  • need to create a java copy of c structures used by the dll functions

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.

  • JACOB: java COM bridge (windows)
  • ...
Backfill answered 28/7, 2010 at 19:14 Comment(1)
+1, although JNA/JNI are both C (not C++) interfaces. When you say that JNA does not work with c++ classes, you forget that you return an opaque pointer. It does mean writing an extern "C" function for each member function that takes said opaque pointer as an argument (c-with-classes style). I recommend using JNA if you already have an extern "C" api and JNI if you don't (JNI will make you write one, but only for the methods you mark as native in Java)Torrent
I
4

You should look for JNI - Java Native Interface

Impudicity answered 28/7, 2010 at 18:38 Comment(4)
does this really support C++, or does it only support functions, and no C++-classes/class-methods etc?Trepang
So what I'm seeing in JNI is that the wrappers are being written before the C++ (or whatever other language) code is actually introduced. I already have all the C++ code, so what is it that I'm not getting right about JNI?Lemmie
I believe you have to write something in C++ to call your code, and the rest in Java to call your wrapper. So, you will have your original code, the C++ wrapper accessing the original code, the Java JNI and the Java accessing the Java wrapper.Impudicity
@Lemmie Write a java class with the api you want to use and mark the methods native (see JNI documentation). Run javah on that class. Take the resulting header file and implement it in c/c++ (in c++ the functions must be extern "C" but I think jni.h takes care of that for you.Torrent
M
3

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.

Marlyn answered 28/7, 2010 at 23:33 Comment(0)
C
2

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.

Candelabra answered 28/7, 2010 at 18:40 Comment(0)
P
0

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.

Penumbra answered 28/7, 2010 at 18:43 Comment(5)
why? because it's not being developed actively?Mycah
@robert: I'm not sure what kts' reason is. I guess he feels it's not standard. GCJ is certainly being developed actively.Penumbra
@robert Ken has it right. gcj is non-standard (no feelings need be involved) and has all the performance of a tin of biscuits. Most java programs I use (eclipse azureus are the biggest) run like quadriplegic dogs on gcj's java. Heck just a quick check of the status page for gcj (gcc.gnu.org/java/status.html) should make any sane dev run for sun-java6-jdk.Torrent
@kts: gcj is non-standard, but remember we're talking about wrapping C++ code here -- that's already not 100% pure java.Penumbra
@Ken You could use standard JNI practice -- essentially extern "C" symbols -- and use the standard JNI to call you c++. Then you can use any Java runtime that supports the native keyword. If you write standard c++, this will be portable across most of the systems that standard java is. If you use gcj and CNI, you stuck (for all intents and purposes) on linux. And, worst of all, stuck using gcj's runtime. There is absolutely no reason to use CNI/gcj when real solutions exist.Torrent

© 2022 - 2024 — McMap. All rights reserved.