使用SWIG的C函数的JNI包装器 – 应该是什么类型图?

我正在尝试为C中的以下函数创建JNI包装器:

int err = new_instance(const char* name, instance_t* instance); 

name – 输入, instance – 输出

 int err = get_value(const instance_t instance, int *val); 

instance – 输入, val – 输出

其中instance_t定义为:

 typedef void* instance_t; 

我完全迷失在Java的SWIG手册中,因为它不仅仅支持输入参数作为输出类型。 我用Python包装器没有任何问题(如下所示)。

在Java的情况下使用typemap的正确方法是什么?

 // 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); } 

您可以通过几种不同的方式使用SWIG和Java来完成此操作。 我已根据您在问题中显示的内容创建了以下标题来说明我的所有示例:

 typedef void* instance_t; int new_instance(const char* name, instance_t * instance); int get_value(const instance_t instance, int *val); 

在界面中编写一些Java:

我们可以使用SWIG库中的cpointer.i为我们提供编写Java重载所需的函数,这些函数调用new_instance的默认版本(我们将其设为私有,因为它成为实现细节)。

 %module test %{ #include "test.h" %} %include  // 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" 

请注意,此示例可能会泄漏,因为默认情况下ptr.value()不是非拥有的。

在界面中编写一些C:

在下一个例子中,我们编写了一个“重载”(但是因为我假设你正在编写C而不是C ++,我们必须使用%rename来完成这项工作)仅在C中,特别是对于SWIG接口。 该函数的原始版本被完全忽略,因为它对我们来说是无用的。

 %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; } %} 

使用类型地图

我们实际上可以使用Java类型映射执行与Python示例非常相似的操作,尽管由于Java具有强大的类型,我们需要尊重它,因此该过程更复杂。

这个解决方案也基本上类似于我在同一个基础问题上的旧答案 ,当底层typedef为void*而不是前向声明时,在Java中使用强大的键入工作(而不仅仅是SWIGTYPE_p_void )的额外复杂性更加棘手。结构。

 %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" 

我鼓励您查看围绕调用new_instance()的生成代码,以完全理解这些类型映射正在做什么。

至于对get_value的调用,从上面的接口自动处理instance_t ,并且int* arg out需要像上面的例子一样处理,或者使用一个只包含一个元素的数组的技巧:

 %include  %apply int *OUTPUT { int *val }; %include "test.h" 

你会称之为:

 int outarr[] = new int[1]; final int err = test.get_value(instance, outarr); // outarr[0] then constains the value 

当然,你可以接受这个技巧并使用像我的第一个例子中的%pragma(java) modulecode这样的答案来提供另一个行为更自然的重载:

 %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]; } %} 

(请注意,使用数组的这个技巧也适用于instance_t*问题的第四个解决方案,但因为它不是原始类型,所以涉及更多工作而没有真正的增益)