Swig typemap java object

2020-04-27 01:44发布

I am trying to generate java bindings through swig for these two c++ functions

C++ functions

void SetUserData(void* data);
void* GetUserData() const;

On the java side I want it to look like this

setUserData(Object object);
getUserData() //return java.lang.Object

I would like the void* pointer to point to any java object I pass to it. In my swig file I tried to add these lines

//swig file
%typemap(jstype) void* "java.lang.Object";
%apply Object {void*};

I get a compile error: b2Fixture.swig(22) : Warning 453: Can't apply (Object). No typemaps are defined. I have no idea how to make typemaps for this. Any ideas?

标签: java swig
2条回答
疯言疯语
2楼-- · 2020-04-27 02:17

For completeness, here's another (simpler?) way to do it with JavaCPP. Assuming we have a GlobalData.h file like this in C++:

class Data {
public:
    JNIEnv* env;
    jobject data;

    Data(JNIEnv* env, jobject obj) : env(env), data(NULL) { }
    ~Data() { if (data != NULL) env->DeleteGlobalRef(data); }

    void SetUserData(jobject obj) { 
        if (data != NULL) env->DeleteGlobalRef(data);
        data = env->NewGlobalRef(obj);
    }
    jobject GetUserData() { return data; }
};

We can use it from Java this way:

import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.annotation.*;

@Platform(include="GlobalData.h")
public class GlobalData {
    static { Loader.load(); }

    public static class Data extends Pointer {
        public Data() { allocate(); }
        private native @Raw(withEnv=true) void allocate();

        public native void SetUserData(@Raw Object obj);
        public native @Raw Object GetUserData();
    }

    public static void main(String[] args) {
        Object someObject = new Object();
        Data myData = new Data();
        myData.SetUserData(someObject);
        Object sameObject = myData.GetUserData();

        System.out.println(someObject);
        System.out.println(sameObject);
    }
}

Where the output shows that someObject and sameObject point to the same object, for example:

java.lang.Object@7aa06577
java.lang.Object@7aa06577
查看更多
老娘就宠你
3楼-- · 2020-04-27 02:27

You probably want to do:

%apply jobject { void* };

because %apply copies the typemaps defined on one C++ type to another C++ type. Object is a Java type, not a C++ one so doesn't have any typemaps to be copied. jobject on the other hand is the JNI equivalent.

Additionally assuming you want to have 'normal' GC semantics (i.e. not be required to retain whatever data you pass in) you'll need to do some more work, for example:

%module test

%{
#include "test.hh"
%}

%apply jobject { void * };

%typemap(in) void * {
  void *old = getData();
  if (old) JCALL1(DeleteGlobalRef, jenv, (jobject)old);
  $1 = JCALL1(NewGlobalRef, jenv, $input);
}

%typemap(out) void * {
  $result = (jobject)($1);
}

%include "test.hh"

This makes a new Global Reference for your data, which could be the only thing keeping it from getting freed by the GC.

Given a header file:

void setData(void *);
void *getData();

and an implementation:

#include "test.hh"

namespace {
  void *d = nullptr;
}

void setData(void *d) {
  ::d = d;
}

void *getData() {
  return d;
}

That is sufficient to allow:

public class run {
  public static void main(String[] argv) {
    System.loadLibrary("test");
    Object example = "HELLO";
    test.setData(example);
    System.out.println(test.getData());
  }
}

to work correctly.

As written these typemaps are pretty ugly - they'll impact all usage of void *. So if you really used this you would want to use %apply or %clear a second time to limit their impact. You could also name the arguments in the header file and use that to limit where the typemap gets applied.

查看更多
登录 后发表回答