I am trying to create the JNI wrapper for the following functions in C:
int err = new_instance(const char* name, instance_t* instance);
name
- input, instance
- output
int err = get_value(const instance_t instance, int *val);
instance
- input, val
- output
where instance_t
is defined as:
typedef void* instance_t;
I am all lost in the SWIG manual for Java, since it doesn't simply support input parameters as the output type. I had no problems whatsoever with the Python wrapper (shown below).
// instance_t [argout]
%typemap(in, numinputs=0) instance_t* instance (instance_t temp = 0) {
$1 = &temp;
}
%typemap(argout) instance_t *instance {
%append_output(PyLong_FromLongLong((long long)* $1));
}
// instance_t [in]
%typemap(in) instance_t instance {
$1 = (instance_t) PyLong_AsLongLong($input);
}
The Java extension to SWIG makes it very easy to plumb in existing C/C++ code for access from Java, as SWIG writes the Java Native Interface (JNI) code for you.
JNI is the Java Native Interface. It defines a way for the bytecode that Android compiles from managed code (written in the Java or Kotlin programming languages) to interact with native code (written in C/C++).
In software design, the Java Native Interface (JNI) is a foreign function interface programming framework that enables Java code running in a Java virtual machine (JVM) to call and be called by native applications (programs specific to a hardware and operating system platform) and libraries written in other languages ...
You can do this using SWIG and Java in several different ways. I've created the following header to illustrate all my examples, based on what you showed in the question:
typedef void* instance_t;
int new_instance(const char* name, instance_t * instance);
int get_value(const instance_t instance, int *val);
We can use the cpointer.i from the SWIG library to give us the functions we need to write a Java overload that calls the default version of new_instance
(which we make private since it becomes an implementation detail).
%module test
%{
#include "test.h"
%}
%include <cpointer.i>
// Have SWIG create a helper class for "pointer to pointer" type of handle
%pointer_class(instance_t, inst_ptr);
// Hide default version of new_instance
%javamethodmodifiers new_instance "private";
// Supply Java version of new_instance now with useful method signature
%pragma(java) modulecode=%{
public static SWIGTYPE_p_void new_instance(String name) {
inst_ptr ptr = new inst_ptr();
final int err = new_instance(name, ptr.cast());
if (0!=err) {
// throw or whatever
}
return ptr.value();
}
%}
%include "test.h"
Note that this example probably leaks as is since ptr.value()
is non-owning by default.
In this next example we write an "overload" (but since I assumed you're writing C and not C++ we had to use %rename
to make this work) in C alone, specifically for the SWIG interface. The original version of the function gets ignored completely since it's pretty useless to us.
%module test
%{
#include "test.h"
%}
// Hide the default new_instance
%ignore new_instance;
%include "test.h"
// Pretend our wrapper specific "overload" was called new_instance all along
%rename(new_instance) new_instance_overload;
// Don't leak our new instance
%newobject new_instance;
// Declare, define and wrap a special version of new_instance
%inline %{
instance_t new_instance_overload(const char* name) {
instance_t result = NULL;
const int err = new_instance(name, &result);
if (err) {
// See later on/other Q for cross language exception example
}
return result;
}
%}
We can actually do something very similar to your Python example using Java typemaps, although the process is more convoluted since Java has strong typing and we need to respect that.
This solution is also substantially similar to my older answer on the same underlying issue, with the additional complexity that getting strong typing working in Java (instead of just SWIGTYPE_p_void
) is trickier here when the underlying typedef is to void*
instead of a forward declaration of a struct.
%module test
%{
#include "test.h"
%}
// Provide 'instance' class for strong typing (instead of void* semantics)
%rename(Instance) instance;
%nodefaultctor;
struct instance {};
typedef instance * instance_t;
// Don't leak (not that we have a destructor yet, but...)
%newobject new_instance;
// Change new_instance to return instance of Instance
%typemap(jstype) int new_instance "$typemap(jstype,instance_t)";
%typemap(jni) int new_instance "$typemap(jni,instance_t)";
%typemap(jtype) int new_instance "$typemap(jtype,instance_t)";
// Hide the instance_t argument and use a temporary instead under the hood
%typemap(in,numinputs=0) instance_t * ($1_basetype tmp) %{
$1 = &tmp;
%}
// After the call copy the result back
%typemap(argout) instance_t * %{
*($1_ltype)&$result = *$1;
%}
// Inside Java construct the proxy with the correct long pointer
%typemap(javaout) int new_instance {
return new $typemap(jstype,int new_instance)($jnicall, $owner);
}
// Some error handling
%javaexception("Exception") new_instance {
$action
if (!result) {
// JNI code to raise exception, untested in this form
jclass clazz = JCALL1(FindClass, jenv, "Exception");
JCALL2(ThrowNew, jenv, clazz, "Failure creating thing");
return $null;
}
}
%include "test.h"
I would encourage you to look at the generated code around the call to new_instance()
to fully understand what these typemaps are doing.
As far as the call to get_value
is concerned the instance_t
gets handled automatically from the interface above and the int*
arg out needs to get handled either similary to the above example, or using a trick with an array containing only one element:
%include <typemaps.i>
%apply int *OUTPUT { int *val };
%include "test.h"
Which you would then call as:
int outarr[] = new int[1];
final int err = test.get_value(instance, outarr);
// outarr[0] then constains the value
Of course you could then take that trick and use something like the %pragma(java) modulecode
of my first example in this answer to supply another overload that behaves more naturally:
%javamethodmodifiers get_value "private";
%pragma(java) modulecode=%{
public static int get_value(Instance instance) {
int outarr[] = new int[1];
final int err = test.get_value(instance, outarr);
if (0!=err) {
// throw, blah blah
}
return outarr[0];
}
%}
(Note that this trick with arrays would also have worked for a fourth solution to the instance_t*
problem, but because it's not a primitive type there's a lot more work involved for no real gain)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With