使用 SWIG 的 C 函数的 JNI 包装器 - 类型映射应该是什么?
JNI wrapper for C function using SWIG - what should be the typemap?
我正尝试在 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) {
= &temp;
}
%typemap(argout) instance_t *instance {
%append_output(PyLong_FromLongLong((long long)* ));
}
// instance_t [in]
%typemap(in) instance_t instance {
= (instance_t) PyLong_AsLongLong($input);
}
您可以使用 SWIG 和 Java 以多种不同的方式执行此操作。根据您在问题中展示的内容,我创建了以下 header 来说明我的所有示例:
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 <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"
请注意,由于 ptr.value()
默认为 non-owning,因此此示例可能会泄漏。
在界面中编写一些 C:
在下一个例子中,我们只用 C 写了一个 "overload"(但因为我假设你写的是 C 而不是 C++,所以我们不得不使用 %rename
来完成这个工作),特别是痛饮接口。该函数的原始版本被完全忽略,因为它对我们来说毫无用处。
%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 具有强类型,我们需要尊重这一点。
这个解决方案在相同的潜在问题上也与 my older answer 非常相似,但在 Java(而不仅仅是 SWIGTYPE_p_void
)中使用强类型更加复杂在这里,当基础 typedef 是 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 * (_basetype tmp) %{
= &tmp;
%}
// After the call copy the result back
%typemap(argout) instance_t * %{
*(_ltype)&$result = *;
%}
// 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 <typemaps.i>
%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*
问题的第四种解决方案,但因为它不是基本类型,所以涉及更多工作并没有真正的收获)
我正尝试在 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) {
= &temp;
}
%typemap(argout) instance_t *instance {
%append_output(PyLong_FromLongLong((long long)* ));
}
// instance_t [in]
%typemap(in) instance_t instance {
= (instance_t) PyLong_AsLongLong($input);
}
您可以使用 SWIG 和 Java 以多种不同的方式执行此操作。根据您在问题中展示的内容,我创建了以下 header 来说明我的所有示例:
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 <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"
请注意,由于 ptr.value()
默认为 non-owning,因此此示例可能会泄漏。
在界面中编写一些 C:
在下一个例子中,我们只用 C 写了一个 "overload"(但因为我假设你写的是 C 而不是 C++,所以我们不得不使用 %rename
来完成这个工作),特别是痛饮接口。该函数的原始版本被完全忽略,因为它对我们来说毫无用处。
%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 具有强类型,我们需要尊重这一点。
这个解决方案在相同的潜在问题上也与 my older answer 非常相似,但在 Java(而不仅仅是 SWIGTYPE_p_void
)中使用强类型更加复杂在这里,当基础 typedef 是 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 * (_basetype tmp) %{
= &tmp;
%}
// After the call copy the result back
%typemap(argout) instance_t * %{
*(_ltype)&$result = *;
%}
// 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 <typemaps.i>
%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*
问题的第四种解决方案,但因为它不是基本类型,所以涉及更多工作并没有真正的收获)