我需要用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 *输出参数(使用Swig/Python在C中传递多个参数和分配字符串)的问题,但在我的例子中,应该是std::string,而SWIG的文档没有提到这种情况在这里输入链接描述。此外,我可能会遇到更多返回3个或更多输出参数的方法,可能具有不同的类型。
最后,我对接口有一点控制,我还在开发一个类,它充当库的入口点,但它只是将调用传递给真正的实现。
例如,我已经成功地将像method3(std::string & s)这样的方法更改为method3(const std::string & s),这样我就可以将它与普通的String一起使用。
因此,修改一些方法签名是可能的,但是如果一个本地方法返回n个输出参数,我应该返回所有这些参数(我不能创建新的方法来返回每个参数)。
Update: --我一直在研究Flexo给出的解决方案,效果很好,但是我正在考虑做一个类来包装std::并使用它与返回的字符串交互,这与Flexo的第二个解决方案非常相似,但是使用这个StringWrapper而不是使用一个StringWrapper数组,基本上如下所示:
/*
* 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或这个指针(就像一个结构只有一个成员。
发布于 2012-08-15 10:36:32
假设您想在不修改现有头文件的情况下对其进行包装,那么有两种方法可供考虑。给定用于测试的头文件:
#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::pair或boost::tuple中使用%template,但包装boost::tuple并不简单,我想,就像这样,您可以为库的用户提供一些合适的名称,而不仅仅是first和second,而不需要使用%rename,这比在%inline中编写自定义结构要复杂得多。
或者,SWIG提供了可以与%apply一起使用的输出类型映射,以创建输出协商网。它们被包装为一个包含1个元素的数组--传递数组的语义与输出参数的语义相匹配。不幸的是,在to emaps.i中没有一个用于std::string,所以我们必须编写自己的。理想情况下,我应该从该文件中重用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 )。在一般情况下,输出类型映射可以在不编写任何额外代码的情况下应用。
发布于 2012-08-14 15:48:27
如果您有C++11支持,则可以返回std::元组 of bool、std::string和std::string。
否则,您可以创建嵌套的性病:成对。
发布于 2012-09-01 04:56:46
仅仅是为了刺激,我们可以用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();
}我很想听听酒杯是一个更好的解决方案!
https://stackoverflow.com/questions/11955962
复制相似问题