2

I have following sample code in C++

class SomeClass: public ISomeClass
{
private:
    int myMember;

public:
    SomeClass(int value) : myMember(value) {}
    __declspec(dllexport)
    int GetCount() override
    {
        return myMember;
    }
};

extern "C" __declspec(dllexport)
int doSomething(ISomeClass** someCl)
{
    *someCl = new SomeClass(600);
    return (*someCl)->GetCount() + 2;
}

And I'm successfully using this in Java with FFI with the following code:

try(Arena arena = Arena.ofConfined()) {
        Linker linker = Linker.nativeLinker();
        SymbolLookup my = SymbolLookup.libraryLookup("C:\\Path\\to\\my.dll", arena);
        MethodHandle doSomething = linker.downcallHandle(
                my.find("doSomething").orElseThrow(),
                FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS)
        );
        MemorySegment someAddress = arena.allocate(ValueLayout.ADDRESS);
        int res = (int)doSomething.invoke(someAddress);
        System.out.println("Result is " + res);
    }
    catch (Throwable t)
    {
        System.out.println("Error is " + t.getMessage());
    }

And it correctly outputs 602.

My question is - how can I represent C++'s SomeClass in Java, so that I can pass that representation from Java to C++ instead of MemorySegment someAddress on the line doSomething.invoke(someAddress) and that I can later use that representation to call ThatRepresentation->GetCount() method directly on that class inside Java?

4
  • It might be more useful to have extern "C" for createSomeClass() / GetCount(ISomeClass* someCl) / doSomething(ISomeClass* someCl) and then you can manage instances and whatever calls you want on C++ instance.
    – DuncG
    Commented May 10 at 18:39
  • I don't have a control over the C++ library. This is just an example where I'm currently stuck. What would be the easiest way to create some C wrapper around this dll as a bridge between java and C++? Commented May 10 at 18:47
  • 2
    You need use JNA and JNI Commented May 10 at 19:02
  • I think you'd need to wrap the calls you have now in a Java class, or use JNI as Farhood mentions.
    – markspace
    Commented May 10 at 19:22

1 Answer 1

3

What would be the easiest way to create some C wrapper around this dll as a bridge between java and C++?

Typically, a good way to represent a C++ class in Java is as an opaque pointer, where all interactions with the object happen on the native side.

If you have a C++ library you would like to access, check to see if it already has a C interface that you can link against directly using FFM. If not, you would have to define a C interface yourself to link against. This is an art, more than something that can be done mechanically, unfortunately.

For the example class you've given, I might write something like this:

Create a header file for the C interface (CHeader.h):

#ifdef __cplusplus
extern "C" {
#endif

typedef struct SomeClass SomeClass;

__declspec(dllexport)
SomeClass* new_SomeClass(int);

__declspec(dllexport)
void delete_SomeClass(SomeClass*);

__declspec(dllexport)
int SomeClass_GetCount(SomeClass*);

#ifdef __cplusplus
}
#endif

Then create a C++ implementation (Impl.cpp):

#include "CHeader.h"
#include "CppHeader.hpp"

extern "C" {

__declspec(dllexport)
SomeClass* new_SomeClass(int value) {
    return new SomeClass(value);
}

__declspec(dllexport)
void delete_SomeClass(SomeClass* inst) {
    delete inst;
}

__declspec(dllexport)
int SomeClass_GetCount(SomeClass* inst) {
    return inst->GetCount();
}

}

(where CppHeader.hpp contains the definition of SomeClass).

After compiling (cl /LD Impl.cpp), I can then use the C interface through FFM:

System.loadLibrary("Impl");
Linker linker = Linker.nativeLinker();
SymbolLookup lookup = SymbolLookup.loaderLookup();
MethodHandle newSomeClass = linker.downcallHandle(
    lookup.find("new_SomeClass").orElseThrow(),
    FunctionDescriptor.of(ADDRESS, JAVA_INT));
MethodHandle deleteSomeClass = linker.downcallHandle(
    lookup.find("delete_SomeClass").orElseThrow(),
    FunctionDescriptor.ofVoid(ADDRESS));
MethodHandle SomeClassGetCount = linker.downcallHandle(
    lookup.find("SomeClass_GetCount").orElseThrow(),
    FunctionDescriptor.of(JAVA_INT, ADDRESS));

MemorySegment inst = (MemorySegment) newSomeClass.invoke(600);
System.out.println((int) SomeClassGetCount.invoke(inst));
deleteSomeClass.invoke(inst);

I think you can see the pattern here: for every C++ member function you would like to access, define an extern "C" wrapper that takes a pointer to the class, and then calls the function you want to call.


If your C++ class is a standard layout type (or if you can reliably derive the layout from a C++ class), you may also access its fields more directly, by creating a GroupLayout that represents the layout of the class, and then deriving var handles to access its fields.

2
  • Thanks for the full example. I just saw that dll has ?GetCount@SomeClass@NMSPC@@UEAAIXZ exported - this is I guess method from this object that is exported. Seems I can use lookup.find to find this method - but not sure if I can then call it and pass it this from my someAddress? Currently there is no exception, but I get strange int results at the output (instead of the expected 600). Commented May 10 at 19:53
  • 2
    @BojanVukasovic FFM only supports linking against C functions. If you try to link against and call C++ function any way, you essentially get undefined behavior. It might yield the expected result, it might crash, or it might silently corrupt memory somewhere else in the process. Commented May 10 at 20:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.