用SWIG在Java中作为输出参数的C std::string 我需要用Sigg包一个C++库,用java来使用它。< /P>

用SWIG在Java中作为输出参数的C std::string 我需要用Sigg包一个C++库,用java来使用它。< /P>,java,c++,swig,Java,C++,Swig,我已经有了一些有效的方法,但是我遇到了一个我不知道如何解决的情况 我有以下几种方法: void method1(std::string & name, std::string & result); bool method2(std::string & name, std::string & alias, std::string & resurnValue, std::string & returnType); /* * The MyClass.

我已经有了一些有效的方法,但是我遇到了一个我不知道如何解决的情况

我有以下几种方法:

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

bool method2(std::string & name, std::string & alias, std::string & resurnValue, std::string & returnType);
/*
* 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"
注意:实际上,这是名为MyClass的类的成员方法

我可以将第一个方法更改为返回
std::string
,而不是
void
,这应该可以工作;但我不知道如何处理第二种方法,其中最后两个参数是输出参数。我看到了一些关于
char*
output params()的问题,但在我的例子中应该是
std::string
,SWIG的文档中没有提到这种情况。此外,我可能会遇到更多返回3个或更多输出参数的方法,这些方法可能具有不同的类型

最后,我对接口有一点控制,我还开发了一个类作为库的入口点,但它只是将调用传递给真正的实现

例如,通过这种方法,我成功地将
method3(std::string&s)
等方法更改为
method3(const std::string&s)
,因此我可以从Java将其与普通的
字符串一起使用

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

更新: 我一直在研究Flexo提供的解决方案,效果很好,但是我正在考虑创建一个类来包装std::string,并使用它与返回的字符串交互,这与Flexo的第二个解决方案非常相似,但是使用这个StringWrapper而不是使用java字符串数组,基本上如下所示:

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

bool method2(std::string & name, std::string & alias, std::string & resurnValue, std::string & returnType);
/*
* 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"

所以我想知道,从性能的角度来看,witch更好,structs解决方案(通过flex)、flex的字符串数组或这个指针(就像只有一个成员的struct一样)。

如果您支持C++11,您可以返回of
bool
std::string
std::string


否则,您可以创建嵌套的。

假设您希望在不修改现有头文件的情况下包装此文件,则会想到两种方法。考虑到我用于测试的头文件:

#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;
}
这适用于:

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::pair
boost::tuple
%template
一起使用,但包装
boost::tuple
并非易事,我怀疑这样您就可以为成员命名一些合适的名称,让库的用户能够理解,而不必使用
%rename
,这比仅在
%inline
中编写自定义结构更加冗长


或者,SWIG提供了输出类型映射,您可以将其与
%apply
一起使用,以创建输出参数网。这些参数被包装为一个由1个元素组成的数组-传递数组的语义与输出参数的语义匹配。不幸的是,类型映射中没有
std::string
的类型映射。i,因此我们必须编写自己的类型映射。理想情况下,我需要r使用该文件中的
OUTPUT\u TYPEMAP
宏,只是稍微修改了argout TYPEMAP,但它得到了
#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
)。在一般情况下,输出类型映射可以应用,而无需编写任何额外的代码。

只是为了好玩,我们可以用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是一个更好的解决方案!

你真的需要使用SWIG吗?我认为使用…@SamuelAudet可以更容易地做到这一点,实际上我必须使用SWIG。我们对JNA和SWIG进行了一点比较,SWIG的速度要快得多。JavaCPP的性能如何?你说“更快”是什么意思?编写代码更快还是性能更快?对于前者,我不确定,但对于后者,JavaCPP可能有更好的性能,因为与SWIG不同,它使用直接NIO缓冲区之类的东西。谢谢,我将看看std::tuple是如何工作的,以及它在SWIG中是否受C++11支持。不,我没有C11支持。boost可能是一个选项,但我可以而不是与其他库包含更多的依赖关系。@ JavierMr,然后可以使用嵌套的<代码> STD::配对< /COD>。据我所知,自从C++代码> C++0x之后,C++中添加了元组。@ RayPixar C++ 0x不是标准。谢谢你的答案的细节。Flexo。我正在尝试这个,它工作得很好。确实,我忘了提到它。问题,这些函数是一个类的成员函数,所以我使用%extend,也使用%ignore指令。但我有疑问,根据swig docs,它不会复制返回的变量,所以int第一个解决方案,在创建结构时,它不应该创建为进行内存保留吗?@JavierMr-第一个例子是这样的正确工作-它使用
Method2Result
的复制构造函数在堆上分配一个新实例。(如果您仍然不确定,请查看生成的包装器代码)。如果您想避免这种情况,您可以自己使用
new
并返回指针,而不是按值返回。在这种情况下,您需要使用
%newobject
让SWIG知道它仍然应该从Java端管理内存。@flex-谢谢,我仍然不习惯复制构造函数和重载运算符。我已经更新了我的q另一种方法是使用指向字符串的指针,类似于第二种解决方案。我认为比第一种解决方案更好,因为这样做不必为每个返回值声明typedef结构。非常类似于