Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C std::string as output parameters in Java with SWIG [duplicate]

Tags:

java

c++

swig

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.

like image 888
Javier Mr Avatar asked Dec 12 '22 23:12

Javier Mr


2 Answers

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.

like image 168
Flexo Avatar answered Dec 14 '22 11:12

Flexo


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.

like image 37
juanchopanza Avatar answered Dec 14 '22 12:12

juanchopanza