-
Notifications
You must be signed in to change notification settings - Fork 591
Description
I have been trying to build a JavaCPP binding for libremidi, and had been failing to bind this function:
LIBREMIDI_EXPORT
int libremidi_midi_out_port_name(
const libremidi_midi_out_port* port, const char** name, size_t* len);
The implementation for this function assigns the actual pointer to the string into name
.
Now, when I tried to use the binding (I could manage to build it with some mappings) via byte[]
(excuse my Kotlin code):
name = ByteArray(1024)
println("libremidi_midi_out_port_name returned " + library.libremidi_midi_out_port_name(port, name, size))
it failed to get the expected string. It did work when I made changes to the code to use BytePointer
though:
val nameBuf = ByteBuffer.allocateDirect(1024)
val namePtr = BytePointer(nameBuf) // we should only need a space for pointer...
println(namePtr.address())
println("libremidi_midi_out_port_name returned " + library.libremidi_midi_out_port_name(port, namePtr, size))
println(namePtr.address())
println("name size: " + size.get())
name = ByteArray(size.get().toInt())
namePtr.asBuffer().get(name)
The thing is that the pointee string at const char** name
can be modified, and when we pass byte[]
that needs to be copied back to the binding parameter. It seems that JavaCPP works if the C function argument were char**
.
The cause of the problem is at Generator.parametersAfter()
:
// If const array, then use JNI_ABORT to avoid copying unmodified data back to JVM
final String releaseArrayFlag;
if (cast.contains(" const *") || cast.startsWith("(const ")) {
releaseArrayFlag = "JNI_ABORT";
} else {
releaseArrayFlag = "0";
}
The resulting generated code (jnilibremidi.cpp
), especially for libremidi_midi_out_port_name()
with byte[]
, looks like this.
This assumes that the const char **
argument must be used only as an input and not to alter the output. But that's not correct. It would be true only if it is const char * const *
. This StackOverflow question describes it well, namely:
const char* the_string
: I can change which char the_string points to, but I cannot modify the char to which it points.
const char* const the_string
: I cannot change which char the_string points to, nor can I modify the char to which it points.
It is implemented for some optimization, but to ensure the implementation correctness the copying condition at parametersAfter()
should rather be like:
if (cast.contains("* const") || cast.endsWith("const)")) {
releaseArrayFlag = "JNI_ABORT";
} else {
releaseArrayFlag = "0";
}
I will create a PR based on this suggestion.