C std :: string作为带有SWIG的Java中的输出参数

时间:2012-08-14 15:40:55

标签: java c++ swig

我需要用SWIG包装一个C ++库,以便与Java一起使用。

我已经有一些方法正常工作,但我遇到了一个我不知道如何解决它的情况。

我有几个像这样的方法:

void method1(std::string & name, std::string & result);

bool method2(std::string & name, std::string & alias, std::string & resurnValue, std::string & returnType);

注意:实际上这是名为MyClass的类的成员方法。

我可以更改第一个方法来返回std::string而不是void,这应该有效;但我不知道如何处理第二种方法,其中最后两个参数是输出参数。我已经看到了几个问题,即char *输出参数(Passing multiple parameters and allocating strings in C using Swig/Python),但在我的情况下应该是std::string,SWIG的文档中没有提到这种情况{{3 }}。此外,我可能会遇到更多返回3个或更多输出参数的方法,可能使用不同的类型。

最后我对接口有一点控制权,我也在开发一个充当库的入口点的类,但它只是将调用传递给真正的实现。

例如,我已设法将method3(std::string & s)之类的方法更改为method3(const std::string & s),因此我可以使用普通String的Java来使用它。

因此可以修改一些方法签名,但是如果一个本机方法返回n个输出参数,我应该返回所有这些参数(我不能创建新的方法来返回每个参数)。

更新 我一直在寻找Flexo给出的解决方案并且工作得很好,但是我正在考虑做一个包装std :: string并使用它来与返回的字符串进行交互的类,这是一个非常类似于Flexo的第二个解决方案的方法,但是使用此StringWrapper而不是使用java String数组,基本上如下所示:

/*
* The MyClass.i file
*/
%module example

%include "std_string.i"

%{
    class StringPtr{

    private:
        stdString str;

    public:
        StringPtr(){

    }

        StringPtr(const stdString & str){
        this->str = stdString(str);
        }

    stdString & getStrRef(){
        return (this->str);
        }

        stdString getStrVal(){
        return stdString(this->str);
        }

        ~StringPtr(){

        }
    };


%}

/////////////////// Export StringPtr to Java

class StringPtr{

    public:
        StringPtr();

    StringPtr(const stdString & str);

    stdString getStrVal();

    ~StringPtr();

};

// I think this is nor necessary
%rename ("$ignore", fullname=1) "StringPtr::getStrRef";

%extend MyClass {

    void method1(cons std::string & name, StringPtr & result){
        $self->method1(name, result.getStrRef());
    }

    bool method2(cons std::string & name, cons std::string & alias, StringPtr & returnValue, StringPtr & returnType){
        $self->method2(name, alias, returnValue.getStrRef(), returnType.getStrRef());
    }

};

%rename ("$ignore", fullname=1) "MyClass::method1";
%rename ("$ignore", fullname=1) "MyClass::method2";

%include "MyClass.h"

所以我想知道,从性能的角度来看,女巫更好,结构解决方案(通过Flexo),Flexo的字符串数组或这个指针(就像只有一个成员的结构。

3 个答案:

答案 0 :(得分:6)

假设您想要在不修改现有头文件的情况下包装它,有两种方法可以想到。鉴于我用于测试的头文件:

#include <string>

inline bool method2(const std::string & name, const std::string & alias, std::string & resurnValue, std::string & returnType) {
  resurnValue = name;
  returnType = alias;
  return true;
}

包装它的最简单方法是使用%inline创建一个包装所有输出的重载:

%module test

%include <std_string.i>

%{
#include "test.h"
%}

%inline %{
  struct Method2Result {
    bool b;
    std::string s1;
    std::string s2;
  };

  Method2Result method2(const std::string& in1, const std::string& in2) {
    Method2Result ret;
    ret.b = method2(in1,in2,ret.s1,ret.s2);
    return ret;
  }
%}

// Optional: don't wrap the original form of method2 at all:
%ignore method2;

%include "test.h"

这适用于:

public class run {
  public static void main(String[] args) {
    System.loadLibrary("test");
    Method2Result ret = test.method2("foo", "bar");
    System.out.println(ret.getB() + " - " + ret.getS1() + ", " + ret.getS2());
  }
}

您可以将std::pairboost::tuple%template一起使用,但我怀疑包装boost::tuple是非平凡的,并且像这样,您可以将成员命名为适合用户的内容您的图书馆将理解而不仅仅是firstsecond,而不使用%rename,这比仅仅在%inline内编写自定义结构更加冗长。


或者,SWIG提供OUTPUT类型映射,您可以使用%apply创建输出argumnets。这些被包装为1个元素的数组 - 传递数组的语义与输出参数的语义相匹配。不幸的是,typemaps.i中没有std::string,因此我们必须编写自己的。{1}}。理想情况下,我已经重用了该文件中的OUTPUT_TYPEMAP宏,并且稍微修改了argout类型映射,但它得到了#undef而不可能。幸运的是,复制和修改这种情况相当简单:

%module test

%{
#include "test.h"
%}

%typemap(jstype) std::string& OUTPUT "String[]"
%typemap(jtype) std::string& OUTPUT "String[]"
%typemap(jni) std::string& OUTPUT "jobjectArray"
%typemap(javain)  std::string& OUTPUT "$javainput"
%typemap(in) std::string& OUTPUT (std::string temp) {
  if (!$input) {
    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
    return $null;
  }
  if (JCALL1(GetArrayLength, jenv, $input) == 0) {
    SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
  }
  $1 = &temp;
}
%typemap(argout) std::string& OUTPUT {
  jstring jvalue = JCALL1(NewStringUTF, jenv, temp$argnum.c_str()); 
  JCALL3(SetObjectArrayElement, jenv, $input, 0, jvalue);
}

%apply  std::string& OUTPUT { std::string & resurnValue }
%apply  std::string& OUTPUT { std::string & returnType }

%include "test.h"

这可以像:

一样使用
public class run {
  public static void main(String[] args) {
    String[] out1 = new String[1];
    String[] out2 = new String[1];
    boolean retb = test.method2("foo", "bar", out1, out2);
    System.out.println(retb + " - " + out1[0] + ", " + out2[0]);
  }
}

这两个都在我的系统上进行了测试和工作。对于这个例子,我喜欢%inline方法。 (如果它是成员函数,则使用%extend代替)。在一般情况下,可以应用OUTPUT类型映射,而无需编写任何额外的代码。

答案 1 :(得分:3)

如果您拥有C ++ 11支持,则可以返回bool std::stringstd::string和{{1}}。

否则,您可以创建嵌套的std::tuple

答案 2 :(得分:3)

只是为了踢,这就是我们用JavaCPP做到的方法:

public static native boolean method2(@StdString String name,
        @StdString @Cast("char*") BytePointer alias,
        @StdString @Cast("char*") BytePointer returnValue,
        @StdString @Cast("char*") BytePointer returnType);

public static void main(String[] args) {
    BytePointer alias = new BytePointer();
    BytePointer returnValue = new BytePointer();
    BytePointer returnType = new BytePointer();
    method2("Unknown", alias, returnValue, returnType);
    alias.getString();
    returnValue.getString();
    returnType.getString();
}

我很想知道SWIG是一个更好的解决方案!