5

我正在为 C 库开发一些 SWIG 生成的 Java 绑定。该库包含接受类型参数的函数void *。在 C 端,这些通常作为指向类型数组的指针传递floatint强制转换为void *. 在生成的 Java 绑定中,这会导致方法采用 type 参数SWIGTYPE_p_void

在 Java 绑定中构造浮点数/整数数组以便它们可以作为类型传递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);

inta_to_voidp()这看起来相当难看,特别是因为对于我想要支持的每种类型转换,我还需要在我的 SWIG 接口文件中添加一个 etc。

有没有办法在没有辅助函数的情况下做到这一点,并且在 Java 端涉及更少的额外代码来转换数据类型?

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

4

2 回答 2

3

这个答案有一个替代方案,它非常不同,并且为这个问题提供了一个更自然的解决方案,更接近你最初寻找的东西。其他建议集中在添加重载(繁琐、手动)或使array_classes 以一种或另一种方式实现公共接口。

它忽略了大部分时间在 Java中Object是一个很好的匹配。void*甚至Java 中的数组也是Objects。这意味着,如果您有 SWIG 映射void*Object它,它将接受您可能想要传入的任何数组作为输入。稍加注意和一些 JNI,我们就可以获取指向该数组开头的指针以传递给函数。显然,我们需要拒绝非数组Object,但有一个例外。

我们最终仍然会编写一些(私有)辅助函数来安排提取真正的底层指针并在完成后释放它,但是这个解决方案的好处是我们只需要这样做一次,然后我们最终得到一个类型映射可用于任何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 使用 ajlong来表示指针。因此,对于每种数组类型,我们都需要一个函数,该函数返回一个给定数组的指针并释放另一个指针。这些是私有的并且是模块类的一部分——除了模块之外没有人需要知道它是如何工作的。

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

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

于 2012-06-14T18:11:11.863 回答
1

最简单的解决方案是使用 SWIG<carrays.i>创建一个类型,该类型包含一个数组float一个数组int以及您关心的任何其他类型。这些可以使用成员函数简单地转换为SWIGTYPE_p_float等。cast()问题是这不能SWIGTYPE_p_void从Java 中自动转换为a。理论上你可以调用:

new SWIGTYPE_p_void(FloatArray.getCPtr(myfloatarr));

但是由于各种原因(尤其是很麻烦),这并不理想。(它还存在内存所有权和垃圾收集问题)。

所以我定义了一个接口:

public interface VoidPtr {
  public long asVoidPtr();
}

我们可以使您的库的包装版本作为输入,并为我们实现数组类FloatArrayIntArray

这以模块文件结束:

%module test

%include <carrays.i>

%typemap(javainterfaces) FloatArray "VoidPtr"
%typemap(javainterfaces) IntArray "VoidPtr"

%typemap(javacode) FloatArray %{
  public long asVoidPtr() {
    return getCPtr(this);    
  }
%}

%typemap(javacode) IntArray %{
  public long asVoidPtr() {
    return getCPtr(this);
  }
%}

%array_class(float, FloatArray);
%array_class(int, IntArray);

%typemap(jstype) void *arr "VoidPtr"
%typemap(javain) void *arr "$javainput.asVoidPtr()"

void foo(void *arr);

修改void *arr为我们的VoidPtr类型并自动调用该asVoidPtr()方法。您可以使用类型映射复制或宏来减少重复性。(注意,过早的垃圾收集可能存在问题,可能需要在此处解决,具体取决于您计划如何使用它)

这允许我们编写如下代码:

public class run {
  public static void main(String[] argv) {
    FloatArray arr = new FloatArray(100);
    test.foo(arr);    
  }
}

我认为这是最简单、最干净的解决方案。不过,还有其他几种方法可以解决这个问题:

  1. 还可以编写一些代码,这些代码将采用实际的 Java 数组而不仅仅是 SWIG array_class,并通过调用 JNI 函数来获取底层指针来实现此接口。不过,就像上面一样,您必须为每种原始类型编写一个版本。

    接口文件可能看起来像:

    %module test
    
    %{
    void foo(void *arr);
    %}
    
    %typemap(in,numinputs=0) JNIEnv *env "$1 = jenv;"
    
    %rename(foo) fooFloat;
    %rename(foo) fooInt;
    %inline %{
    void fooFloat(JNIEnv *env, jfloatArray arr) {
      jboolean isCopy;
      foo((*env)->GetFloatArrayElements(env, arr, &isCopy));
      // Release after call with desired semantics
    }
    
    void fooInt(JNIEnv *env, jintArray arr) {
      jboolean isCopy;
      foo((*env)->GetIntArrayElements(env, arr, &isCopy));
      // Release after call
    }
    %}
    
    void foo(void *arr);
    

    然后,它会为您提供footakefloat[]int[]以及 的重载SWIGTYPE_p_void

  2. 您可以使用联合的技巧:

    %inline %{
      union Bodge {
        void *v;
        float *f;
        int *i;
      };
    %}
    

    尽管这被认为是不好的形式,但它确实为您生成了一个 Java 接口,可用于从 转换SWIGTYPE_p_intSWIGTYPE_p_void.

  3. 我认为可以FloatArray继承 from SWIGTYPE_p_void,类似于以下已编译但未经测试的代码:

    %module test
    
    %include <carrays.i>
    
    %typemap(javabase) FloatArray "SWIGTYPE_p_void"
    %typemap(javabody) FloatArray %{
      private long swigCPtr; // Minor bodge to work around private variable in parent
      private boolean swigCMemOwn;
      public $javaclassname(long cPtr, boolean cMemoryOwn) {
        super(cPtr, cMemoryOwn);
        this.swigCPtr = SWIGTYPE_p_void.getCPtr(this);
        swigCMemOwn = cMemoryOwn;
      }
    %}
    
    %array_class(float, FloatArray);
    
    void foo(void *arr);
    

    这在 Java 端复制了指针,但在 void 指针或数组类中(当前)没有任何改变,因此这不像最初看起来那么大的问题。(您也可以使用我认为的替代类型映射在基类中对其进行保护,或者使用通过函数carrays.i获取的修改版本)swigCPtrgetCPtr

于 2012-06-13T16:03:48.770 回答