C std :: string作为具有SWIG的Java中的输出参数 [英] C std::string as output parameters in Java with SWIG

查看:300
本文介绍了C std :: string作为具有SWIG的Java中的输出参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要用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 ,这应该工作;但我不知道如何处理第二个方法,其中最后两个参数是输出参数。我已经看到一些问题ref.reements char * 输出参数(),但在我的情况应该是一个 std: :string ,并且SWIG的文档没有提及此情况在此输入链接说明。我也可能遇到更多的方法返回3个或更多的输出参数,可能有不同的类型。



最后我有一点点的控制接口,我也开发类作为库的入口点,但它只是传递调用到真正的实现。



例如,我已经设法改变了一个方法 method3(std :: string& s) method3(const std :: string& s),所以我可以使用它从一个正常的 String



所以修改一点点方法签名是可能的,但如果一个本地方法返回n输出参数,我应该返回所有的(我不能创建新的方法

更新:
我一直在寻找由Flexo给出的解决方案,并且工作伟大,但是'考虑做一个类来包装std :: string并使用它与返回的字符串交互,是一个非常类似的方法到Flexo的第二个解决方案,但使用这个StringWrapper而不是使用一个java字符串数组,基本上看起来像这样:

  / * 
* MyClass.i文件
* /
%模块示例

%includestd_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(){

}
};


%}

///////////////////导出StringPtr到Java

class StringPtr {

public:
StringPtr();

StringPtr(const stdString& str);

stdString getStrVal();

〜StringPtr();

};

//我认为这也不必要
%rename($ ignore,fullname = 1)StringPtr :: getStrRef;

%extend MyClass {

void method1(cons std :: string& name,StringPtr& result){
$ self-> method1 ,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;

%includeMyClass.h

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

解决方案

假设你想修改现有的头文件,我想要有两种方法:给定我用于测试的头文件:

  #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>

%{
#includetest.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;
}
%}

//可选:不要覆盖method2的原始形式:
%ignore method2;

%includetest.h

p>

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

您可以使用 std :: pair boost :: tuple %template c> boost :: tuple 是不重要的我怀疑,像这样你可以命名成员一些适当的,你的库的用户会理解,而不是只是第一第二,而不使用%rename c $ c>%inline 。






或者,SWIG提供OUTPUT类型版%apply 创建输出argumnets。这些被包装为1个元素的数组 - 传递数组的语义匹配输出参数的语义。不幸的是,在typemaps.i中没有 std :: string ,所以我们必须自己写。理想情况下,我会从该文件中重用 OUTPUT_TYPEMAP 宏,只是稍微修改argout类型,但它获得 #undef 没有这是可能的。幸运的是,对于这种情况,只需重复和修改就很简单:

 %module test 


#includetest.h
%}

%typemap(jstype)std :: string& OUTPUTString []
%typemap(jtype)std :: string& OUTPUTString []
%typemap(jni)std :: string& OUTPUTjobjectArray
%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 one 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}

%includetest.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类型映射,而不写任何额外的代码。


I need to wrap a C++ library with SWIG to use it with Java.

I have already some methods working but I have encountered a situation that I don't know how to solve it.

I have a couple of methods like this:

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

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

Note: Actually this are member methods of a class named MyClass.

I could change the first method to return a std::string instead of being void, and that should work; but I have no idea how to handle the second method where the two last parameters are output parameters. I have seen a couple of questions refereeing to char * output params (Passing multiple parameters and allocating strings in C using Swig/Python), but in my case should be a std::string and the Documentation of SWIG doesn't mention this situation enter link description here. Also I probably encounter more methods returning 3 or more output params, probably with different types.

Finally I have a little of control about the interface, I'm also developing a class that acts as an entry point to the library, but it just passes call to the real implementation.

For example by this I have managed to change a method like method3(std::string & s) to method3(const std::string & s), so I could use it from Java with a normal String.

So modifying a little bit the methods signatures is possible, but if a native method returns n outputs parameters I should return all of them (I can't create new methods to return each one).

Update: I have been looking at the solution given by Flexo and works great, however i'm considering doing a class to wrap std::string and using that to interact with returned strings, is a very similar approach to the second solution of Flexo, but using this StringWrapper instead of using a java String array, basically looks like this:

/*
* 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"

So i'm wondering, from a performance point of view, witch is better, the structs solution (by Flexo), the string array by Flexo or this pointer (just like a struct with only one member.

解决方案

Assuming you want to wrap this without modifying the existing header file there are two ways that come to mind. Given the header file I used for testing:

#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;
}

The simplest way to wrap it is to use %inline to create an overload that wraps all the outputs in one type:

%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"

This works with:

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());
  }
}

You could have used std::pair or boost::tuple with %template but wrapping boost::tuple is non-trivial I suspect and like this you get to name the members something appropriate that users of your library will understand rather than just first and second, without using %rename which becomes more verbose than just writing a custom struct within %inline.


Alternatively SWIG provides OUTPUT typemaps that you can use with %apply to create output argumnets. These get wrapped as an array of 1 element - the semantics of passing arrays matches that of output arguments. Unfortunately there isn't one for std::string in typemaps.i, so we have to write our own. Ideally I'd have reused the OUTPUT_TYPEMAP macro from that file and just modified the argout typemap slightly, but it gets #undefined without that being possible. Fortunately it's fairly simple to just duplicate and modify for this case:

%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"

This can be used like:

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]);
  }
}

Both of these were tested and worked on my system. For this instance I like the %inline approach. (If it were a member function you'd use %extend instead). In the general case the OUTPUT typemaps can be applied without writing any extra code though.

这篇关于C std :: string作为具有SWIG的Java中的输出参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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