数组传递到包装的函数指针为+尺寸或范围 [英] Pass an array to a wrapped function as pointer+size or range

查看:168
本文介绍了数组传递到包装的函数指针为+尺寸或范围的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给定一个像头:

#include <iostream>
#include <algorithm>
#include <iterator>

inline void foo(const signed char *arr, size_t sz) {
  std::copy_n(arr, sz, std::ostream_iterator<int>(std::cout, "\n"));
}

inline void bar(const signed char *begin, const signed char *end) {
  std::copy(begin, end, std::ostream_iterator<int>(std::cout, "\n"));
}

(我用c这里为了方便++ 11,这可能是C或C ++,如果你改变的实现虽然)

(I used C++11 here for convenience, this could be either C or C++ if you changed the implementations though)

我如何包装这些功能,从而利用在Java端只是一个数组,并使用数组的(已知)大小为这些功能提供了第二个参数?

How can I wrap these functions to take just an array on the Java side and use the (known) size of the array to provide the second parameter for these functions?

推荐答案

这样做的关键是,包要么这些功能你要使用的multi-argument类型映射

The crux of this is that to wrap either of these functions you'll want to use a multi-argument typemap.

在preamble是SWIG pretty标准。我用我个人最喜欢的prgama自动加载共享库没有接口需要知道的用户:

The preamble is pretty standard for SWIG. I used my personal favourite prgama to automatically load the shared library without the user of the interface needing to know:

%module test

%{
#include "test.hh"
%}

%pragma(java) jniclasscode=%{
  static {
    try {
        System.loadLibrary("test");
    } catch (UnsatisfiedLinkError e) {
      System.err.println("Native code library failed to load. \n" + e);
      System.exit(1);
    }
  }
%}

首先,虽然你需要使用一些的Java typemaps 指示痛饮使用字节[] 作为Java接口的两个部分的类型 - 和JNI调用它的包装。在生成模块文件,我们将使用JNI类型 jbyteArray 。我们直接从SWIG接口传递的输入,它生成的JNI。

First though you'll need use a few Java typemaps to instruct SWIG to use byte[] as the type of both parts of the Java interface - the JNI and the wrapper that calls it. In the generate module file we'll be using the JNI type jbyteArray. We're passing the input directly from the SWIG interface to the JNI it generates.

%typemap(jtype) (const signed char *arr, size_t sz) "byte[]"
%typemap(jstype) (const signed char *arr, size_t sz) "byte[]"
%typemap(jni) (const signed char *arr, size_t sz) "jbyteArray"
%typemap(javain) (const signed char *arr, size_t sz) "$javainput"

当做到这一点,我们可以写一个多参数类型映射:

When this is done we can write a multi-argument typemap:

%typemap(in,numinputs=1) (const signed char *arr, size_t sz) {
  $1 = JCALL2(GetByteArrayElements, jenv, $input, NULL);
  $2 = JCALL1(GetArrayLength, jenv, $input);
}

的类型映射中的任务是从我们所用JNI调用什么真正功能真的认为作为输入给出转换。我用 numinputs = 1 来表明这两个真正的函数参数只需要在Java端一个输入,但是这是默认值,无论如何,所以它的声明,不要求明确。

The job of the in typemap is to convert from what we're given by the JNI call to what the real function really expects as an input. I used numinputs=1 to indicate that the two real function arguments only take one input on the Java side, but this is the default value anyway, so it's not required to state that explicitly.

在此类型映射 $ 1 是类型映射的第一个参数,即我们在这种情况下,函数的第一个参数。我们设置通过要求一个指针到Java阵列的底层存储(其可以是或可以不是一个拷贝真)。我们制定 $ 2 ,第二个类型映射参数是数组的大小。

In this typemap $1 is the first argument of the typemap, i.e. the first argument of our function in this case. We set that by asking for a pointer to the underlying storage of the Java array (which may or may not be a copy really). We set $2, the second typemap argument to be the size of the array.

JCALLn 这里宏确保类型映射可以与C和C ++编译JNI。它扩展到适当的呼叫的语言。

The JCALLn macros here make sure that the typemap can compile with both C and C++ JNI. It expands to the appropriate call for the language.

我们需要另一种类型映射到清理一次真正的函数调用返回:

We need another typemap to clean up once the real function call has returned:

%typemap(freearg) (const signed char *arr, size_t sz) {
  // Or use  0 instead of ABORT to keep changes if it was a copy
  JCALL3(ReleaseByteArrayElements, jenv, $input, $1, JNI_ABORT); 
}

这要求 ReleaseByteArrayElements 来告诉我们的阵列做了JVM。它需要指针的的从我们得到它的Java数组对象。此外它需要一个参数,指示的内容进行复制回的当且仅当的他们进行了修改,我们得到的指针是摆在首位的副本。 (我们通过NULL的参数是一个可选指针 jboolean 这表明如果我们被赋予了复印件)。

This calls ReleaseByteArrayElements to tell the JVM we're done with the array. It needs the pointer and the Java array object we obtained it from. In addition it takes a parameter that indicates if the contents should be copied back iff they were modified and the pointer we got was a copy in the first place. (The argument we passed NULL is an optional pointer to a jboolean which indicates if we've been given a copy).

有关的第二变体的typemaps基本上类似

For the second variant the typemaps are substantially similar:

%typemap(in,numinputs=1) (const signed char *begin, const signed char *end) {
  $1 = JCALL2(GetByteArrayElements, jenv, $input, NULL);
  const size_t sz = JCALL1(GetArrayLength, jenv, $input);
  $2 = $1 + sz;
}

%typemap(freearg) (const signed char *begin, const signed char *end) {
  // Or use  0 instead of ABORT to keep changes if it was a copy
  JCALL3(ReleaseByteArrayElements, jenv, $input, $1, JNI_ABORT);
}

%typemap(jtype) (const signed char *begin, const signed char *end) "byte[]"
%typemap(jstype) (const signed char *begin, const signed char *end) "byte[]"
%typemap(jni) (const signed char *begin, const signed char *end) "jbyteArray"
%typemap(javain) (const signed char *begin, const signed char *end) "$javainput"

唯一的区别是使用局部变量 SZ 使用<$ C计算结束 arugment $ C>开始指针。

The only difference being the use of the local variable sz to compute the end arugment using the begin pointer.

唯一剩下要做的就是告诉SWIG包住头文件本身,使用我们刚刚写的typemaps:

The only thing left to do is to tell SWIG to wrap the header file itself, using the typemaps we've just written:

%include "test.hh"

我测试了这两种功能:

I tested both these functions with:

public class run {
  public static void main(String[] argv) {
    byte[] arr = {0,1,2,3,4,5,6,7};
    System.out.println("Foo:");
    test.foo(arr);
    System.out.println("Bar:");
    test.bar(arr);
  }
}

如预期哪些工作。

Which worked as expected.

为了方便起见,我分享我在我的网站 。在归档的每个文件的每一行可以按照这个答案依次重建。

For convenience I've shared the files I used in writing this on my site. Every line of every file in that archive can be reconstructed by following this answer sequentially.

有关的参考,我们可以做这件事没有任何JNI调用,使用%附注(JAVA)模块code 来产生我们使用转换过载输入(纯Java)进入由实函数预期的形式。该模块文件本来

For reference we could have done the whole thing without any JNI calls, using %pragma(java) modulecode to generate an overload that we use convert the input (in pure Java) into the form expected by the real functions. For that the module file would have been:

%module test

%{
#include "test.hh"
%}

%include <carrays.i>
%array_class(signed char, ByteArray);

%pragma(java) modulecode = %{
  // Overload foo to take an array and do a copy for us:
  public static void foo(byte[] array) {
    ByteArray temp = new ByteArray(array.length);
    for (int i = 0; i < array.length; ++i) {
      temp.setitem(i, array[i]);
    }
    foo(temp.cast(), array.length);
    // if foo can modify the input array we'll need to copy back to:
    for (int i = 0; i < array.length; ++i) {
      array[i] = temp.getitem(i);
    }
  }

  // How do we even get a SWIGTYPE_p_signed_char for end for bar?
  public static void bar(byte[] array) {
    ByteArray temp = new ByteArray(array.length);
    for (int i = 0; i < array.length; ++i) {
      temp.setitem(i, array[i]);
    }
    bar(temp.cast(), make_end_ptr(temp.cast(), array.length));
    // if bar can modify the input array we'll need to copy back to:
    for (int i = 0; i < array.length; ++i) {
      array[i] = temp.getitem(i);
    }
  }
%}

// Private helper to make the 'end' pointer that bar expects
%javamethodmodifiers make_end_ptr "private";
%inline {
  signed char *make_end_ptr(signed char *begin, int sz) {
    return begin+sz;
  }
}

%include "test.hh"

%pragma(java) jniclasscode=%{
  static {
    try {
        System.loadLibrary("test");
    } catch (UnsatisfiedLinkError e) {
      System.err.println("Native code library failed to load. \n" + e);
      System.exit(1);
    }
  }
%}

除了获取数据到正确的类型(没有平凡的方式,从去要求明显(二)副本的byte [] SWIGTYPE_p_signed_char )和背面这有一个缺点 - 它特有的功能 ,而我们前面写的不是针对特定的功能typemaps - 任何地方匹配,即使在同一个函数多次,如果你碰巧有一个函数,两个范围或两个指针+长度的组合,他们将被应用。做这种方式的一个优点是,如果你碰巧有其他包装函数,是给你的 SWIGTYPE_p_signed_char 那时你仍然可以重载,如果你使用欲望。即使在你的情况下的ByteArray %array_class 您仍然无法做到在Java中的指针运算需要产生结束为您服务。

Besides the obvious (two) copies required to get the data into the right type (there's no trivial way to go from byte[] to SWIGTYPE_p_signed_char) and back this has another disadvantage - it's specific to the functions foo and bar, whereas the typemaps we wrote earlier are not specific to a given function - they'll be applied anywhere they match, even multiple times on the same function if you happen to have a function that takes two ranges or two pointer+length combinations. The one advantage of doing it this way is that if you happen to have other wrapped functions that are giving you SWIGTYPE_p_signed_char back then you'll still have the overloads available to use if you desire. Even in the case where you have a ByteArray from the %array_class you still can't do the pointer arithmetic in Java needed to generate end for you.

中显示的原始的方式给出了Java中的简洁的界面,与不做过多的副本,是更具重用性的额外优势。

The original way shown gives a cleaner interface in Java, with the added advantages of not making excessive copies and being more reusable.

另一种替代的方法来包装是写在%直列过载和

Yet another alternative approach to wrapping would be to write a few %inline overloads for foo and bar:

%inline {
  void foo(jbyteArray arr) {
    // take arr and call JNI to convert for foo
  }
  void bar(jbyteArray arr) {
    // ditto for bar
  }
}

这是psented作为Java接口重载$ P $,但他们仍然模块的特定,另外这里需要的JNI是比较复杂的比原本需要的是 - 你需要安排获得<$的举行C $ C> jenv 不知何故,这是不是默认访问。选项​​是一个缓慢的呼吁得到它,或 numinputs = 0 类型映射,填补了参数自动。无论哪种方式,多参数类型映射似乎远更好。

These are presented as overloads in the Java interface, but they're still module specific and additionally the JNI required here is more complex than it would otherwise need to be - you need to arrange to get hold of jenv somehow, which isn't accessible by default. The options are a slow call to get it, or a numinputs=0 typemap that fills the parameter in automatically. Either way the multi-argument typemap seems far nicer.

这篇关于数组传递到包装的函数指针为+尺寸或范围的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆