我需要用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的类的成员方法。
我可以将第一个方法更改为返回a std::string
而不是being
void
,它应该可以工作;但是我不知道如何处理第二种方法,其中最后两个参数是输出参数。我已经看到了几个有关char *
输出参数的问题(使用Swig/ Python在C中传递多个参数并分配字符串),但是在我的情况下应该是a
std::string
,SWIG文档中没有提到这种情况,请在此处输入链接描述。此外,我可能还会遇到更多返回3个或更多输出参数的方法,这些方法可能具有不同的类型。
最终,我对接口有了一些控制,我还在开发一个类,该类充当库的入口点,但是它只是将调用传递给了实际的实现。
例如,通过这个我设法改变的方法一样method3(std::string & s)
来method3(const std::string &s)
,所以我可以从Java与正常使用String
。
因此,可以对方法签名进行一些修改,但是如果本机方法返回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组成的字符串数组或该指针(就像只有一个成员的结构一样)。
假设您希望在不修改现有头文件的情况下包装它,就想到了两种方法。给定我用于测试的头文件:
#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
是不平凡的,因此您可以为成员命名一个适当的名称,库的用户将理解这些适当的名称,而不是just
first
和second
,而不必使用%rename
它而变得比仅在其中编写自定义结构更冗长%inline
。
另外,SWIG提供了可用于%apply
创建输出argumnet的OUTPUT类型映射。它们被包装为1个元素的数组-
传递数组的语义与输出参数的语义匹配。不幸的是std::string
,typemaps.i中没有适合的代码,因此我们必须编写自己的代码。理想情况下,我将重用该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类型映射,而无需编写任何额外的代码。
二.背景 我正在使用C代码库为Android开发一个实时通信应用程序。我使用SWIG生成一个JNI桥,以从Java访问本机代码。为了跟踪正在进行的通话,将用作句柄(指向包含正在进行的通话信息的地址)。以下函数头是如何使用它的示例:
问题内容: 使用第三方API,我观察到以下内容。 而不是使用 它使用类似 我得到分配的“输出”字符串。 我很好奇实现这种功能的原因。使用此类输出参数的优点是什么? 问题答案: 在您的示例中有不对的地方。 在上面的程序中,将输出字符串“ foo”, 而不是 “ Hello World”。 某些类型是可变的,在这种情况下,您可以修改传递给函数的对象。对于不可变的类型(例如),您必须构建某种包装类,而可
问题内容: 您将如何配置SWIG .i文件以处理C FILE *类型?下面的函数设置一个文件,以便可以将日志输出写入该文件。我需要从Java类中调用。当前,当我仅将C头文件包含在以下函数中时,SWIG将生成公共静态void setLogFile(SWIGTYPE_p_FILE fd)函数。有任何想法吗? C函数: 我尝试使用以下方法#1并得到以下异常: test.i: 例外: 问题答案: 给定的看
问题内容: 我必须为作业分配一个API,而我的讲师对API中的一种方法(基于Javadoc)使用了一种我不熟悉的符号。 “ …”是什么意思?稍后看来,我将需要使用单个字符串实际参数以及多个字符串实际参数来调用相同的方法… Java没有可选参数(据我所知),所以我在这里有点困惑… 问题答案: 这就是所谓的varargs;http://docs.oracle.com/javase/6/docs/tec
问题内容: 我正在阅读Wikipedia上的Singleton文章,并且遇到了以下示例: 虽然我真的很喜欢Singleton的行为方式,但是我看不到如何修改它以将参数合并到构造函数中。用Java进行此操作的首选方法是什么?我需要做这样的事情吗? 谢谢! 编辑:我想我对使用Singleton的渴望已经引发了一场争论的风暴。让我解释一下我的动机,并希望有人可以提出一个更好的主意。我正在使用网格计算框架
问题内容: 是否可以通过以参数作为参数的javascript函数? 例: 问题答案: 使用“关闭”: 这将创建一个匿名的临时函数包装器,该包装器知道该参数并将其传递给实际的回调实现。