Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swig typemap to pass address of variable as a parameter?

Tags:

java

c++

swig

I created JNI wrapper for the following c++ code.

add.h

class MyClass
    {
    public:   
        int add(int x, int y, int &z); 
        int sub(int x, int y);
    };

The above mentioned code is the .h file

add.cpp

int MyClass::add(int x, int y, int &sum)
{
    sum=x+y;
    return 0;
}

int MyClass::sub(int x, int y)
{
        return x - y;
}

swig.i

%module algo

%{
    #define SWIG_FILE_WITH_INIT
    #include "add.h"
%}
%include "arrays_java.i"
%include "typemaps.i"

%include "add.h"

For the above mentioned .cpp file , I need to generate JNI wrapper and use it on the Java code. When I am trying to execute the swig command , I get the SWIGTYPE_p_int.java file along withe JNI file. Can anyone help me to overcome this problem?

like image 877
Yuvaraj S Avatar asked Jun 15 '17 13:06

Yuvaraj S


1 Answers

To wrap C++ "return by non-const reference" functions there are quite a few options open. In brief you can do one of:

  1. Use %extend to provide an overload that returns instead. (Optionally hide the original)
  2. Use typemaps to convert it to a return value (and optionally map it to
  3. Work with SWIGTYPE_p_int more. (Optionally build an overload inside of Java)
  4. Use the existing SWIG OUTPUT typemaps to get pass by reference semantics with an array
  5. Use a struct to get pass by reference semantics

I'll show an example of each option here.


1. Use %extend to provide an overload

The basic idea here is that we're going to write an entirely new overload here that SWIG wraps trivially. Using the %extend syntax below we can create a new, wrapper only, overload that uses a temporary to store the value and then returns it if all goes well.

%module algo

%{
    #define SWIG_FILE_WITH_INIT
    #include "add.h"
    #include <exception>
%}

%extend MyClass {
    int add(int x, int y) {
        int z;
        const int result = $self->add(x,y,z);
        if (0 == result) return z;
        throw std::exception();
        // TODO: use SWIG's exception support
    }
}

%ignore MyClass::add(int,int,int&); // Optional: hide the original overload
%include "add.h"

Since the original returned int seems to indicate success/failure of the function itself we can map that more naturally onto exceptions in Java. (Details omitted here, see my answer at XXX for more)

2. Use typemaps to return it

The effect of this solution is similar to the previous one, but implemented in a different way. Here we're using the in typemap with numinputs=0 to set up a temporary variable that we can use (tmpz) when the call happens. The out typemap then just checks the return code from the real functioncall, with the argout typemap copying the tempoary into the result. I included more typemaps than really needed here, because by chance z and the existing function return are the same type but we'd need them if that weren't the case.

%module algo

%{
    #define SWIG_FILE_WITH_INIT
    #include "add.h"
    #include <exception>
%}

// These aren't actually needed here because the fuction already returns in
%typemap(jni) int MyClass::add "jint";
%typemap(jtype) int MyClass::add "int";
%typemap(jstype) int MyClass::add "int";
%typemap(javaout) int MyClass::add {
  return $jnicall;
}

// These create a temporary and map it to the return value for us
%typemap(argout) int& z {
  $result = (jint)tmpz$argnum;
}
%typemap(out) int MyClass::add {
  if ($1 != 0) {
     throw std::exception();
     // TODO: exceptions as in other examples
  }
}
%typemap(in,numinputs=0) int& z (int tmpz) {
  $1 = &tmpz;
}
%include "add.h"

3. Use SWIG pointer functions

Here we're going to use SWIG's cpointer.i library to help us work with SWIGTYPE_p_int objects and do that transparently for our Java users. To do so we've used the javacode typemap to create a temporary variable for z and then passed that into the original SWIG generated function (which can be made private to hide it). As with the other examples we can handle the case where the return value indicates an error by throwing an exception, this time though it's already Java code that's doing the throwing which is slightly simpler.

%module algo

%{
    #define SWIG_FILE_WITH_INIT
    #include "add.h"
%}
%include "cpointer.i"
%pointer_class(int,IntPointer);
%typemap(javacode) MyClass %{
    public int add(int x, int y) {
        IntPointer z = new IntPointer();
        int ret = this.add(x,y,z.cast());
        if (ret != 0) throw new Exception();
        return z.value();
    }
%}
%javamethodmodifiers MyClass::add(int,int,int&) "private" // Optional
%include "add.h"

4. Use OUTPUT typemaps

This is functionally very similar to the previous solution, with the change that instead of using a SWIG supplied helper type for handling the int pointer we use an array and exploit Java's "arrays pass by reference" semantics under the hood to achieve the same result.

%module algo

%{
    #define SWIG_FILE_WITH_INIT
    #include "add.h"
%}
%include <typemaps.i>
%apply int *OUTPUT { int & z };
%typemap(javacode) MyClass %{
    public int add(int x, int y) {
        int[] z = new int[1];
        int ret = this.add(x,y,z);
        if (ret != 0) throw new Exception();
        return z[0];
    }
%}
%javamethodmodifiers MyClass::add(int,int,int&) "private"
%include "add.h"

5. Use our own structure for pass by reference

You can add another type using %inline. And then trick SWIG into using that instead of the int& reference. Provided we allow an implicit conversion this is fine.

%module algo

%{
    #define SWIG_FILE_WITH_INIT
    #include "add.h"
%}
%inline %{
    struct IntWrapper {
        int value;
        // Enable transparent conversion for the SWIG argument
        operator int&() {
          return value;
        }
    };
%}
class MyClass
    {
    public:   
        int add(int x, int y, IntWrapper& z); // lie to SWIG, but we'll make it up later with implict conversion
        int sub(int x, int y);
    };
//%include "add.h" // Don't do this now because we need the lie above

As with the previous examples we could choose to hide this implementation detail from Java users with an overload and method modifier usage.

For more on the exceptions points raised in several examples above see here, or here.

like image 138
Flexo Avatar answered Sep 21 '22 01:09

Flexo