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.