java - 如何在 SWIG 生成的 Java 绑定(bind)中转换为 SWIGTYPE_p_void 类型?

标签 java c java-native-interface swig

我正在为 C 库开发一些 SWIG 生成的 Java 绑定(bind)。该库包含采用 void * 类型参数的函数。在 C 端,这些通常作为指向 float 类型数组或 int 类型转换为 void * 的指针传递。在生成的 Java 绑定(bind)中,这会导致采用 SWIGTYPE_p_void 类型参数的方法。

在 Java 绑定(bind)中构造 float /整数数组以便将它们作为类型 SWIGTYPE_p_void 传递给这些方法的最佳方法是什么?

目前我正在我的 example.i 文件中定义一个辅助函数:

void *floata_to_voidp(float f[])
{
    return (void *)f;
}

然后在 Java 端做这样的事情:

float foo[] = new float[2];
SWIGTYPE_p_void av = null;

// do something with foo

av = example.floata_to_voidp(foo);
example.myfunction(av);

这看起来相当丑陋,尤其是因为我还需要在我的 SWIG 接口(interface)文件中为我想要支持的每种类型转换添加 inta_to_voidp() 等。

有没有一种方法可以在没有辅助函数的情况下执行此操作,并且在 Java 端涉及较少的额外代码来转换数据类型?

更新(2012 年 6 月 17 日): 为问题提供更多细节:我想做的是采用一组 C 函数,原型(prototype)为 int foo( const float *data, int N, const void *argv, float *result) 并将它们映射到 Java 端的方法,其中可以将任意类型的数组作为 argv 传入。请注意,argvconst void * 而不是 void *

最佳答案

this answer 有一个替代方案,它非常不同,并且为这个问题提供了更自然的解决方案,更接近您最初寻找的内容。其他建议侧重于添加重载(繁琐,手动)或使 array_classes 以某种方式实现通用接口(interface)。

它忽略的是 Object 在大多数情况下与 Java 中的 void* 非常匹配。连arrays in Java are Objects .这意味着如果你有 SWIG map void*Object 它会接受你可能想要传入的任何数组作为输入。稍微小心一点和一些 JNI 我们可以然后获取指向该数组开头的指针以传递给函数。显然,我们需要拒绝非数组 Object,但有一个异常(exception)。

我们最终还是写了一些(私有(private)的)辅助函数来安排提取真正的底层指针并在完成后释放它,但是这个解决方案的好处是我们只需要这样做一次然后我们就结束了一个类型映射,可用于像这样将数组作为 void* 的任何函数。

我最终为这个解决方案得到了以下 SWIG 界面:

%module test

%{
#include <stdint.h>

void foo(void *in) {
  printf("%p, %d, %g\n", 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 **)&map;
  (*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 **)&map;
  (*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​​ 来表示指针。因此,对于每种数组类型,我们都需要一个函数来返回给定数组的指针和另一个函数来释放它。这些是私有(private)的并且是模块类的一部分 - 除了模块之外没有人需要知道它是如何工作的。

然后有两个函数接受 Object 并使用 instanceof(丑陋,但 Java 中的数组没有任何其他公共(public)基础或接口(interface),泛型也没有帮助)并调用正确的函数来获取/释放指针。

有了这些,只需两个类型映射即可设置 SWIG 以将其用于所有 void *arr 参数。在这些情况下,jstype 类型映射指示 SWIG 使用 Object 作为 void*。 javain 类型映射安排一个临时局部变量来保存指针(在 long 中),然后用于进行调用并在调用成功或失败后清除。

关于java - 如何在 SWIG 生成的 Java 绑定(bind)中转换为 SWIGTYPE_p_void 类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11005123/

相关文章:

java - 从 cmd 运行 Jar 相关的 .Java

java - 如何用数组列表替换数组?

java - Jersey 客户端语法错误请求错误

c - 如何通过scanf读取文件中的不同行输入

c - 在 C 中将文件发送到标准输出

java - 在 Java 对象中存储 native 指针的 'correct' 方法是什么?

java - 在 Android 应用程序的 ActionBar 中同时显示应用程序图标和后退箭头

java - 记录测试结果

c - 需要帮助在 C 中创建动态字符数组

java - 将一组垫传递给 native 代码