As of Java 18 the incubating foreign function interface doesn't appear to have a good way to handle C++ code. I am working on a project that requires bindings to C++ and I would like to know how to avoid creating a thunk library in C.
One of the C++ classes looks something like this:
namespace library {
typedef uint8_t byte;
class CppClass {
public:
static oncstexpr const char* DefaultArgument = "default";
CppClass(const std::string& argument = DefaultArgument);
virtual ~CppClass();
bool doStuff();
bool handleData(std::vector<byte>* data);
private:
std::unique_ptr<InternalType> internalState;
};
}
I would like to create a Java class that looks something like the following to mirror that (with error checking left out):
public final class CppClass implements AutoCloseable {
public static final String DefaultArgument = "default";
private static final MethodHandle NEW;
private static final MethodHandle FREE;
private static final MethodHandle DO_STUFF;
private static final MethodHandle HANDLE_DATA;
static{
var binder = Natives.getBinder();
NEW = binder.bind("(manged constructor)", ValueLayout.ADDRESS, ValueLayout.ADDRESS);
FREE = binder.bindVoid("(manged deconstructor)", ValueLayout.ADDRESS);
DO_STUFF = binder.bind("(manged doStuff)", ValueLayout.JAVA_BYTE, ValueLayout.ValueLayout.ADDRESS);
HANDLE_DATA = binder.bind("manged handleData)", ValueLayout.JAVA_BYTE, ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.JAVA_LONG);
}
private final MemorySegment pointer;
public CppClass() {
this(DefaultArgument);
}
public CppClass(String argument) {
try(var scope = MemoryScope.newConfinedScope()) {
var allocator = MemoryAllocator.nativeAllocator(scope);
pointer = (MemoryAddress)NEW.invokeExact(
allocator.allocateUtf8String(argument)
);
}
}
@Override
public void close() {
FREE.invokeExact(pointer);
}
public boolean doStuff() {
return (byte)DO_STUFF.invokeExact(pointer) != 0;
}
public boolean handleData(MemorySegment segment) {
return (byte)HANDLE_DATA.invokeEact(pointer, segment.address(), segment.byteSize()) != 0;
}
}
where Binder
looks something like this:
public interface Binder {
MethodHandle bind(String name, FunctionDescriptor desc);
MethodHandle bind(String name, MemoryLayout result, MemoryLayout... args);
MethodHandle bindVoid(String name, MemoryLayout... args);
}
I am not sure what parts of this are correct. My biggest implementation questions are:
So the general answer seems to be "just create a shim library" because the C++ ABI is far more fluid and not supported by Java.
As for the answers at the end:
void*
pointersthis
as a void*
and treat it as an opaque pointer