如何在 SWIG 生成的 Java 绑定中转换为 SWIGTYPE_p_void 类型?
我正在为 C 库开发一些 SWIG 生成的 Java 绑定.该库包含采用 void *
类型参数的函数.在 C 端,这些通常作为指针传递给类型为 float
或 int
的数组转换为 void *
.在生成的 Java 绑定中,这会导致方法采用 SWIGTYPE_p_void
类型的参数.
I am developing some SWIG-generated Java bindings for a C library. The library contains functions that take parameters of type void *
. On the C side these would typically be passed as a pointer to an array of type float
or int
cast to void *
. In the generated Java bindings, this results in methods that take parameters of type SWIGTYPE_p_void
.
在 Java 绑定中构造浮点数/整数数组以便它们可以作为 SWIGTYPE_p_void
类型传递给这些方法的最佳方法是什么?
What is the best way to construct an array of floats/ints in the Java bindings so that they can be passed as type SWIGTYPE_p_void
to these methods?
目前我正在我的 example.i 文件中定义一个辅助函数:
At the moment I am defining a helper function in my example.i file:
void *floata_to_voidp(float f[])
{
return (void *)f;
}
然后在 Java 端做这样的事情:
And then on the Java side doing something like this:
float foo[] = new float[2];
SWIGTYPE_p_void av = null;
// do something with foo
av = example.floata_to_voidp(foo);
example.myfunction(av);
这看起来相当难看,特别是因为我还需要在我的 SWIG 接口文件中为我想要支持的每个类型转换添加一个 inta_to_voidp()
等.
This seems rather ugly, especially as I would also need an inta_to_voidp()
etc in my SWIG interface file for each type conversion I want to support.
有没有办法做到这一点,而不需要辅助函数,并且在 Java 端涉及更少的额外代码来转换数据类型?
Is there a way to do this without helper functions and involving less extra code on the Java side to convert data types?
更新(17/6/12): 为问题提供更多细节:我想要做的是采用一组 C 函数,原型 int foo(const float *data, int N, const void *argv, float *result)
并将它们映射到 Java 端的方法,其中可以将任意类型的数组作为 argv
传入.注意 argv
是 const void *
而不是 void *
.
UPDATE (17/6/12): to give additional detail to the question: what I'm trying to do is take a set of C functions, with prototype int foo(const float *data, int N, const void *argv, float *result)
and map them to methods on the Java side where an array of arbitrary type can be passed in as argv
. Note that argv
is const void *
and not void *
.
推荐答案
这个答案还有一个替代方案,非常不同,并为这个问题提供更自然的解决方案,更接近您最初寻找的内容.其他建议集中在添加重载(繁琐,手动)或使 array_class
es 以一种或另一种方式实现公共接口.
There's an alternative to this answer, it's very different and gives a more natural solution to this problem, closer to what you were looking for originally. The other suggestions were focused on adding overloads (tedious, manual) or making the array_class
es implement a common interface one way or another.
它忽略的是 Object
在大多数情况下与 Java 中的 void*
非常匹配.甚至 Java 中的数组也是 Object
s.这意味着如果您将 SWIG 映射 void*
到 Object
它将接受您可能想要传入的任何数组作为输入.稍加注意和一些 JNI 我们可以然后获取指向该数组开头的指针以传递给函数.显然,我们需要拒绝非数组 Object
的异常.
What it overlooks is that Object
is a good match for void*
in Java most of the time. Even arrays in Java are Object
s. This means if you have SWIG map void*
to Object
it'll accept as inputs any arrays you might want to pass in. With a bit of care and some JNI we can then get a pointer to the start of that array to pass in to the function. Obviously we need to reject non array Object
s with an exception though.
我们最终还是会编写一些(私有)辅助函数来安排提取真正的底层指针并在完成后释放它,但是这个解决方案的好处是我们只需要这样做一次,然后我们就会得到可用于任何将数组作为 void*
的函数的类型映射.
We still end up writing some (private) helper functions to arrange extraction of the real underlying pointer and release it when done, but the nice thing about this solution is that we only have to do this once and then we end up with a typemap that can be used for any functions which take an array as void*
like this.
我最终得到了这个解决方案的以下 SWIG 界面:
I ended up with the following SWIG interface for this solution:
%module test
%{
#include <stdint.h>
void foo(void *in) {
printf("%p, %d, %g
", in, *(jint*)in, *(jdouble*)in);
}
%}
%typemap(in,numinputs=0) JNIEnv *env "$1 = jenv;"
%javamethodmodifiers arr2voidd "private";
%javamethodmodifiers arr2voidi "private";
%javamethodmodifiers freearrd "private";
%javamethodmodifiers freearri "private";
%inline %{
jlong arr2voidd(JNIEnv *env, jdoubleArray arr) {
void *ptr = (*env)->GetDoubleArrayElements(env, arr, NULL);
return (intptr_t)ptr;
}
void freearrd(JNIEnv *env, jdoubleArray arr, jlong map) {
void *ptr = 0;
ptr = *(void **)↦
(*env)->ReleaseDoubleArrayElements(env, arr, ptr, JNI_ABORT);
}
jlong arr2voidi(JNIEnv *env, jintArray arr) {
void *ptr = (*env)->GetIntArrayElements(env, arr, NULL);
return (intptr_t)ptr;
}
void freearri(JNIEnv *env, jintArray arr, jlong map) {
void *ptr = 0;
ptr = *(void **)↦
(*env)->ReleaseIntArrayElements(env, arr, ptr, JNI_ABORT);
}
%}
%pragma(java) modulecode=%{
private static long arrPtr(Object o) {
if (o instanceof double[]) {
return arr2voidd((double[])o);
}
else if (o instanceof int[]) {
return arr2voidi((int[])o);
}
throw new IllegalArgumentException();
}
private static void freeArrPtr(Object o, long addr) {
if (o instanceof double[]) {
freearrd((double[])o, addr);
return;
}
else if (o instanceof int[]) {
freearri((int[])o, addr);
return;
}
throw new IllegalArgumentException();
}
%}
%typemap(jstype) void *arr "Object"
%typemap(javain,pre=" long tmp$javainput = arrPtr($javainput);",post=" freeArrPtr($javainput, tmp$javainput);") void *arr "tmp$javainput"
void foo(void *arr);
这实现了两种数组类型,有一个小的有限数,您也可以使用片段或宏来帮助解决这个问题.在内部 SWIG 使用 jlong
来表示指针.因此,对于每种数组类型,我们都需要一个函数,该函数返回一个给定数组的指针并释放另一个指针.这些是私有的并且是模块类的一部分 - 除了模块之外没有人需要知道它是如何工作的.
This implements it for two array types, there's a small finite number and you could use fragments or macros to help with this too. Internally SWIG uses a jlong
to represent pointers. So for each array type we need a function that returns a pointer for a given array and another one to release it. These are private and part of the module class - nobody other than the module needs to know how this works.
然后有两个函数接受 Object
并使用 instanceof
(丑陋,但 Java 中的数组没有任何其他公共基础或接口,泛型没有help) 并调用正确的函数来获取/释放指针.
There's then two functions which take the Object
and use instanceof
(ugly, but arrays in Java don't have any other common base or interface and generics don't help) and call the correct function to get/release the pointers.
有了这些,只需两个类型映射即可设置 SWIG 以将其用于所有 void *arr
参数.在这些情况下,jstype 类型映射指示 SWIG 将 Object
用于 void*
.javain 类型映射安排一个临时局部变量来保存指针(在 long
中),然后将其用于进行调用并在调用成功或失败后进行清理.
With these then it's just two typemaps to set up SWIG to use it for all void *arr
arguments. The jstype typemap instructs SWIG to use Object
for void*
in these cases. The javain typemap arranges for a temporary local variable to hold the pointer (in a long
) and then for it to be used to make the call and to be cleaned up once the call has succeed or failed.
相关文章