Swig typemap将变量的地址作为参数传递?

时间:2017-06-15 13:13:19

标签: java c++ swig

我为以下c ++代码创建了JNI包装器。

add.h

class MyClass
    {
    public:   
        int add(int x, int y, int &z); 
        int sub(int x, int y);
    };

上面提到的代码是.h文件

add.cpp

int MyClass::add(int x, int y, int &sum)
{
    sum=x+y;
    return 0;
}

int MyClass::sub(int x, int y)
{
        return x - y;
}

swig.i

%module algo

%{
    #define SWIG_FILE_WITH_INIT
    #include "add.h"
%}
%include "arrays_java.i"
%include "typemaps.i"

%include "add.h"

对于上面提到的.cpp文件,我需要生成JNI包装器并在Java代码上使用它。当我尝试执行swig命令时,我将获得SWIGTYPE_p_int.java文件以及JNI文件。任何人都可以帮我解决这个问题吗?

1 个答案:

答案 0 :(得分:5)

要包装C ++“通过非const引用返回”函数,有很多选项可以打开。简而言之,您可以执行以下操作之一:

  1. 使用%extend提供返回的重载。 (可选择隐藏原文)
  2. 使用类型地图将其转换为返回值(并可选择将其映射到
  3. 更多地使用SWIGTYPE_p_int。 (可选择在Java内部构建一个重载)
  4. 使用现有的SWIG OUTPUT类型映射来获取带数组的引用语义
  5. 使用结构来通过引用语义进行传递
  6. 我将在此处展示每个选项的示例。

    1。使用%extend提供重载

    这里的基本想法是,我们将在这里编写一个全新的重载,SWIG包含一些简单的内容。使用下面的%extend语法,我们可以创建一个新的,仅包装器的重载,它使用临时值存储值,然后如果一切顺利则返回它。

    %module algo
    
    %{
        #define SWIG_FILE_WITH_INIT
        #include "add.h"
        #include <exception>
    %}
    
    %extend MyClass {
        int add(int x, int y) {
            int z;
            const int result = $self->add(x,y,z);
            if (0 == result) return z;
            throw std::exception();
            // TODO: use SWIG's exception support
        }
    }
    
    %ignore MyClass::add(int,int,int&); // Optional: hide the original overload
    %include "add.h"
    

    由于原始返回的int似乎表明函数本身的成功/失败,我们可以更自然地将它映射到Java中的异常。 (这里省略了详细信息,请参阅我在XXX的回答了解更多信息)

    2。使用类型映射返回

    此解决方案的效果与前一个类似,但以不同的方式实现。在这里,我们使用in typemap与numinputs=0来设置一个临时变量,我们可以在调用发生时使用它(tmpz)。 out typemap然后只检查来自真实函数调用的返回代码,argout typemap将tempoary复制到结果中。我包含了比这里真正需要的更多的类型图,因为偶然的z和现有的函数返回是相同的类型,但如果不是这样的话我们就需要它们。

    %module algo
    
    %{
        #define SWIG_FILE_WITH_INIT
        #include "add.h"
        #include <exception>
    %}
    
    // These aren't actually needed here because the fuction already returns in
    %typemap(jni) int MyClass::add "jint";
    %typemap(jtype) int MyClass::add "int";
    %typemap(jstype) int MyClass::add "int";
    %typemap(javaout) int MyClass::add {
      return $jnicall;
    }
    
    // These create a temporary and map it to the return value for us
    %typemap(argout) int& z {
      $result = (jint)tmpz$argnum;
    }
    %typemap(out) int MyClass::add {
      if ($1 != 0) {
         throw std::exception();
         // TODO: exceptions as in other examples
      }
    }
    %typemap(in,numinputs=0) int& z (int tmpz) {
      $1 = &tmpz;
    }
    %include "add.h"
    

    3。使用SWIG指针功能

    在这里,我们将使用SWIG的cpointer.i库来帮助我们使用SWIGTYPE_p_int对象,并为Java用户透明地执行此操作。为此,我们使用javacode类型映射为z创建一个临时变量,然后将其传递给原始的SWIG生成函数(可以将其设置为私有以隐藏它)。与其他示例一样,我们可以通过抛出异常来处理返回值指示错误的情况,这次虽然已经是Java代码正在执行抛出,但稍微简单一点。

    %module algo
    
    %{
        #define SWIG_FILE_WITH_INIT
        #include "add.h"
    %}
    %include "cpointer.i"
    %pointer_class(int,IntPointer);
    %typemap(javacode) MyClass %{
        public int add(int x, int y) {
            IntPointer z = new IntPointer();
            int ret = this.add(x,y,z.cast());
            if (ret != 0) throw new Exception();
            return z.value();
        }
    %}
    %javamethodmodifiers MyClass::add(int,int,int&) "private" // Optional
    %include "add.h"
    

    4。使用OUTPUT类型地图

    这在功能上与之前的解决方案非常相似,改变了不使用SWIG提供的辅助类型来处理int指针,而是使用数组并利用Java的“数组传递引用”语义来实现同样的结果。

    %module algo
    
    %{
        #define SWIG_FILE_WITH_INIT
        #include "add.h"
    %}
    %include <typemaps.i>
    %apply int *OUTPUT { int & z };
    %typemap(javacode) MyClass %{
        public int add(int x, int y) {
            int[] z = new int[1];
            int ret = this.add(x,y,z);
            if (ret != 0) throw new Exception();
            return z[0];
        }
    %}
    %javamethodmodifiers MyClass::add(int,int,int&) "private"
    %include "add.h"
    

    5。使用我们自己的结构进行参考

    您可以使用%inline添加其他类型。然后欺骗SWIG使用它而不是int&引用。如果我们允许隐式转换,这很好。

    %module algo
    
    %{
        #define SWIG_FILE_WITH_INIT
        #include "add.h"
    %}
    %inline %{
        struct IntWrapper {
            int value;
            // Enable transparent conversion for the SWIG argument
            operator int&() {
              return value;
            }
        };
    %}
    class MyClass
        {
        public:   
            int add(int x, int y, IntWrapper& z); // lie to SWIG, but we'll make it up later with implict conversion
            int sub(int x, int y);
        };
    //%include "add.h" // Don't do this now because we need the lie above
    

    与前面的示例一样,我们可以选择使用重载和方法修饰符用法从Java用户隐藏此实现细节。

    有关上述几个示例中提出的例外情况的更多信息,请参阅herehere