Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using shared_ptr with SWIG Directors for Java

Tags:

swig

I'm starting to get the hang of SWIG, and the latest version(v3.0) of SWIG seems to handle just about everything I need out of the box, including C++11 features, but I have hit a snag when it comes to using shared_ptr with my director classes.

I have been able to get shared_ptr to work with normal proxy classes great, but now on my directors, it seems to not be supported out of the box. It is giving me the auto-generated type like SWIGTYPE_p_std__shared_ptrT_MyDataType_t and is generating a broken interface because it isn't using the same types that the proxy classes use.

I have a simplified example of what I'm trying to do (run with swig -c++ -java Test.i on swig 3.0):

Test.i

%module(directors="1") test
%{

%}

%include <std_shared_ptr.i>

%shared_ptr(MyDataType)

class MyDataType {
public:
    int value;
};

class NonDirectorClass {
public:
    std::shared_ptr<MyDataType> TestMethod();
};

%feature("director") CallbackBaseClass;

class CallbackBaseClass {
public:
    virtual ~CallbackBaseClass() {};
    virtual std::shared_ptr<MyDataType> GetDataFromJava() {};
};

Basically what I'm going to be doing is extending the CallbackBaseClass in Java and I want to be able to pass around my shared_ptr wrapped types. The non-director class generates the shared_ptr types just fine. The director class proxy files get generated correctly, but the SwigDirector_ methods in the wrapper reference the incorrect types.

It seems like I could manually repair the files by changing the type of SWIGTYPE_p_std__shared_ptrT_MyDataType_t to MyDataType everywhere, but I'm hoping someone with more swig knowledge can answer the question so this can be generated correctly.

The best clue I have is here, but I'm still trying to figure out how to correctly use these type maps, especially for shared_ptr and not basic primitives.

UPDATE:

The documentation says:

Note: There is currently no support for %shared_ptr and the director feature.

Though it gives no indication as to why. I'd like to know if this is an impossibility with swig directors, if there is a good reason why not to use shared_ptr in directors. It seems like it makes sense to use the same types you use everywhere else. I hope the answer is it is still possible.

like image 214
Kekoa Avatar asked Apr 28 '14 05:04

Kekoa


1 Answers

The latest version of the SWIG documentation now reads:

"There is somewhat limited support for %shared_ptr and the director feature and the degress of success varies among the different target languages. Please help to improve this support by providing patches with improvements."

To make your example work we seem to need to add four missing typemaps, directorin, directorout, javadirectorin and javadirectorout:

%module(directors="1") test

%include <std_shared_ptr.i>

%{
#include <memory>
#include <iostream>
%}

%shared_ptr(MyDataType)

%feature("director") CallbackBaseClass;

%typemap(javadirectorin) std::shared_ptr<MyDataType> "new $typemap(jstype, MyDataType)($1,true)";
%typemap(directorin,descriptor="L$typemap(jstype, MyDataType);") std::shared_ptr<MyDataType> %{
  *($&1_type*)&j$1 = new $1_type($1);
%}

%typemap(javadirectorout) std::shared_ptr<MyDataType> "$typemap(jstype, MyDataType).getCPtr($javacall)";
%typemap(directorout) std::shared_ptr<MyDataType> %{
  $&1_type tmp = NULL;
  *($&1_type*)&tmp = *($&1_type*)&$input;
  if (!tmp) {
    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "Attempt to dereference null $1_type");
    return NULL;
  }
  $result = *tmp;
%}

%inline %{
class MyDataType {
public:
    int value;
};

class NonDirectorClass {
public:
    std::shared_ptr<MyDataType> TestMethod() { return std::make_shared<MyDataType>(); }
};

class CallbackBaseClass {
public:
    virtual ~CallbackBaseClass() {};
    virtual std::shared_ptr<MyDataType> GetDataFromJava() = 0;
};

void frobinate(CallbackBaseClass& cb) {
    std::cout << "In C++: " << cb.GetDataFromJava()->value << "\n";
}
%}

Even though you only ever use the directorout case in your sample the directorin typemap is still required to make the lookup in director_connect succeed, because it depends upon having the correct descriptor.

These four typemaps are equivilent to the in, javain and javaout typemaps in functionality, but reversed because of their role in directors.

They're not complete enough to handle all cases, but they work in your example. The $typemap call inside the descriptor needs a version of SWIG 3 newer than the one included in Ubuntu 14.04 - in the form I wrote it the only version I tested with was checked out from the trunk. You can write the descriptor by hand (which would just be descriptor="LMyDataType;"), but clearly that's less generic. The advantage of writing it as above is that it will handle %rename directives correctly too. This won't handle packages properly though either so you would have to write it manually again in that case too.

I was able to test and run the example, I added the following run.java:

public class run extends CallbackBaseClass {
  public MyDataType GetDataFromJava() {
    MyDataType val = new MyDataType();
    val.setValue(123);
    return val;
  }

  public static void main(String[] argv) {
    System.loadLibrary("test");
    run r = new run();

    System.out.println("In Java: " + r.GetDataFromJava().getValue());
    test.frobinate(r);
  }
}

And compiled and ran it with:

~/swig-trunk/preinst-swig -Wall -c++ -java  test.i  
clang++-3.6 -stdlib=libc++ -Wall -Wextra -std=c++1y test_wrap.cxx -o libtest.so -I/usr/lib/jvm/default-java/include/ -I/usr/lib/jvm/default-java/include/linux -shared -fPIC 
javac run.java
LD_LIBRARY_PATH=. java run

Which when ran gave:

In Java: 123
In C++: 123

I would guess that in the case of Java with shared_ptr+directors the subtleties around getting the descriptor right are probably the main blocker to having this "just work" out the box.

like image 121
Flexo Avatar answered Nov 12 '22 07:11

Flexo