13

I haven't seen this anywhere (or maybe I'm jsut simple not seeing it) but is there a way to use JNI to return the c/c++ object and use that object in java?

For example (very simple):

class simpleClass{
...
private:
int intVar;
public:
int getIntVar();
void setIntVar(int someNum);
...
}

In my java code, how would I go about doing something like:

...
simpleClass sc = new simpleClass();
sc.setIntVar(9);
System.out.println(sc.getIntVar());
...

I realize this is a VERY simplistic example but I'm just looking for concept - the class I have in mind that is in c++ is very large and am looking to avoid creating a TON of wrapper methods...

If it's not possible that's fine, just hoping to save a few days coding lol

KevinDTimm
  • 14,226
  • 3
  • 42
  • 60
redhotspike
  • 1,056
  • 5
  • 17
  • 39

3 Answers3

16

Your Java version of SimpleClass should do two things. One, keep a private long value that stores the value of the C++ pointer to the backing native object (you might have to use BigInteger depending on how large a native pointer can be - unsigned long long?). Two, make the public methods (e.g. setIntVal) native.

public class SimpleClass {
    private long nativePtr;

    public SimpleClass() {
        nativePtr = initNativeSimpleClass();
    }

    public void destroy() {
        destroyNativeSimpleClass();
        nativePtr = 0L;
    }

    protected void finalize() throws Throwable {
        destroyNativeSimpleClass();
        nativePtr = 0L;
    }

    public native int getIntVal();
    public native void setIntVal(int val);

    private native long initNativeSimpleClass();
    private native void destroyNativeSimpleClass();
}

Then implement those native methods in your JNI code. The initNativeSimpleClass() method will new a backing C++ instance of SimpleClass. The destroyNativeSimpleClass() method will then delete that instance. The accessor methods will use the value of nativePtr, cast it to a real pointer, and perform the appropriate operations on the native backing instance.

This idiom poses a real risk of leaking memory because users of the class MUST call destroy when they are done with an instance. If they don't, the backing native instance may not be properly destroyed. You can, as I've shown in the example, override finalize to call the native destroyer function, but all the caveats about how finalize can't be relied on still apply. By setting the nativePtr value to 0 upon destruction you avoid seg faults if destroy is called multiple times (it's safe in C++ to delete NULL).

elect
  • 6,765
  • 10
  • 53
  • 119
pedorro
  • 3,079
  • 1
  • 24
  • 24
  • This doesn't avoid creating wrapper functions but it's really the only way to use JNI to put a Java front on native classes. – pedorro Apr 18 '12 at 16:02
  • I definitely like this take on what I proposed...I have to say that I like this better than just "using" the c++ object: it provides a way to document that I'm using the native methods. Thanks! – redhotspike Apr 18 '12 at 19:03
5

No you can't. The C++ and Java ABIs are completely different - for one, c++ doesn't define one. And really c++ has so many features that can't be mapped to Java at all this just can't work. How do you expect Java would handle c++ templates? Pointers to primitives? Objects that aren't pointers?

Now what you can do, is use SWIG to generate the right wrapper methods for you - that will actually work and is not much more work than what you planned :)

Voo
  • 29,040
  • 11
  • 82
  • 156
  • 4
    Note that C++ templates are irrelevant for this question, because templates are compiled away in C++. JNI would have to support classes instantiated from those templates, which look no different from regular classes. – Dan Nissenbaum Apr 18 '12 at 15:48
  • @Dan If you write a not header-only template class, you have to be able to tell the compiler to instantiate a new version of the class in client code, so I'd think the compiler has to know about templates as well (though no idea if that's required by the standard - never had to do that in reality). – Voo Apr 18 '12 at 15:54
  • @Voo It needs the definition of the template at compile time to generate the instantiation. Once generated for each of the necessary types used the object code is just as if you had written out a class for each type. AFAIK without including a cpp file you can't reference a template that isn't in your translation unit unless it's definition is entirely inline for the same reason. – AJG85 Apr 18 '12 at 16:09
  • @Voo Those are things (pointers to primitives, etc) that I definitely hadn't considered. This makes me think I'm just going to wittle away at the wrapper methods :) – redhotspike Apr 18 '12 at 19:01
  • 1
    @redhot As I said, have a look at [SWIG](http://www.swig.org/), it basically takes a simple description of your c++ interfaces (which often enough can just be the header) and generates the right wrappers for you. I haven't used it myself for Java, but it works nicely for Python and I've heard good things from people using it for java too. Has the advantage of a) being less work in the long run and b) meaning your wrappers and c++ files never get out of sync. – Voo Apr 18 '12 at 19:14
  • @Voo I've actually already been looking at it! lol It looks like it's perfect for what I'm trying to do. Thanks! – redhotspike Apr 18 '12 at 19:17
0

JNI defines its interface for primitive (or fairly primitive) types only, as well as memory buffer passing/management functions. There is no capability to map complex object types. However, you might be able to achieve this effect by writing your own (single) serialization/deserialization functions, as in Returning a C++ class to Java via JNI.

Community
  • 1
  • 1
Dan Nissenbaum
  • 13,558
  • 21
  • 105
  • 181