4

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.

Community
  • 1
  • 1
Javier Mr
  • 2,130
  • 4
  • 31
  • 39
  • Do you really need to use SWIG? I think this could be more easily done with [JavaCPP](http://code.google.com/p/javacpp/)... – Samuel Audet Aug 19 '12 at 02:08
  • @SamuelAudet Actually i have to use SWIG. We did a little comparation between JNA and SWIG and SWIG was much faster. How well does JavaCPP perform? – Javier Mr Aug 27 '12 at 08:37
  • What do you mean "faster"? Faster to code or faster in performance? For the former, I'm not sure, but for the latter, JavaCPP probably has better performance, because unlike SWIG it uses direct NIO buffers and things like that. – Samuel Audet Aug 28 '12 at 01:26

3 Answers3

7

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 #undefined 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.

Flexo
  • 87,323
  • 22
  • 191
  • 272
  • Thanks for the detail of your answer @Flexo. I'm currently trying this out and it's working good. Indeed, i forgot to mention it in the question, those functions are member functions os a class, so I'm using %extend, also using the %ignore directive. How ever i have a doubt, according to swig docs it doesn't make copies a returned variables, so int the first solution, when the structure is created, shouldn't it be created doing memory reservation? – Javier Mr Aug 20 '12 at 10:14
  • @JavierMr - the first example does work correctly - it uses the copy constructor of `Method2Result` to allocate a new instance on the heap. (Take a peek at the generated wrapper code if you're still not convinced). If you want to you can avoid that by using `new` yourself and returning a pointer rather than by value. In that case you'll want to use `%newobject` to let SWIG know that it should still manage the memory from the Java side. – Flexo Aug 20 '12 at 10:33
  • @Flexo- Thanks, i'm still not used to copy constructors and overloaded operators. I have updated my question with other approach consisting of using a pointer to string, similar to your second solution. I think is better than the first solution because by doing this i don't have to declare a typedef struct for each returned value. Very similar to the second solution but avoiding the arrays, hopefully making it more intuitive. – Javier Mr Aug 27 '12 at 09:50
  • Your typemaps work great. thanks a lot. it is exactly what I'm looking for. – Behrouz.M Jun 17 '15 at 09:37
  • As a side note when you use %inline or %extend make sure white space is added after the word inline or extend. Using %inline%{ will result in an error. while %inline %{ will not. – gnash117 Jun 28 '19 at 17:12
3

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.

juanchopanza
  • 223,364
  • 34
  • 402
  • 480
3

Just for kicks, this is how we could do it with 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();
}

I would love to hear how SWIG is a better solution!

Samuel Audet
  • 4,964
  • 1
  • 26
  • 33