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.
If you have C++11 support, you could return an std::tuple of bool
, std::string
and std::string
.
Otherwise, you could create nested std::pairs.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With