C std :: string作为具有SWIG的Java中的输出参数 [英] C std::string as output parameters in Java with 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
,这应该工作;但我不知道如何处理第二个方法,其中最后两个参数是输出参数。我已经看到一些问题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 #undef
ined 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屋!