How do I convert to type SWIGTYPE_p_void in SWIG generated Java bindings?

前端 未结 2 571
余生分开走
余生分开走 2020-12-30 15:44

I am developing some SWIG-generated Java bindings for a C library. The library contains functions that take parameters of type void *. On the C side these would

2条回答
  •  忘掉有多难
    2020-12-30 16:31

    The simplest solution is to use SWIG's to make a type that wraps an array of float and an array of int and any other types you care about. These can be converted to SWIGTYPE_p_float etc. trivially using the cast() member function. The problem is that this can't automatically be converted to a SWIGTYPE_p_void from within Java. You could in theory call:

    new SWIGTYPE_p_void(FloatArray.getCPtr(myfloatarr));
    

    but for various reasons (not least it's cumbersome) that's less than ideal. (It also has issues with memory ownership and garbage collection).

    So instead I defined an interface:

    public interface VoidPtr {
      public long asVoidPtr();
    }
    

    That we can make the wrapped version of your library take as input and the array classes FloatArray, IntArray etc. implement for us.

    This ends up with the module file:

    %module test
    
    %include 
    
    %typemap(javainterfaces) FloatArray "VoidPtr"
    %typemap(javainterfaces) IntArray "VoidPtr"
    
    %typemap(javacode) FloatArray %{
      public long asVoidPtr() {
        return getCPtr(this);    
      }
    %}
    
    %typemap(javacode) IntArray %{
      public long asVoidPtr() {
        return getCPtr(this);
      }
    %}
    
    %array_class(float, FloatArray);
    %array_class(int, IntArray);
    
    %typemap(jstype) void *arr "VoidPtr"
    %typemap(javain) void *arr "$javainput.asVoidPtr()"
    
    void foo(void *arr);
    

    Which modifies void *arr to be treated as our VoidPtr type and automatically calls the asVoidPtr() method. You could use typemap copying or macros to make this less repetitive. (Note, there's a possible issue with premature garbage collection that might need to be addressed here depending on how you planned to use this)

    This allows us to write code like:

    public class run {
      public static void main(String[] argv) {
        FloatArray arr = new FloatArray(100);
        test.foo(arr);    
      }
    }
    

    I think this is the easiest, cleanest solution. There are several other ways you could solve this though:

    1. It's also possible to write some code that would take an actual Java array rather than just the SWIG array_class and implement this interface by calling a JNI function to obtain the underlying pointer. You'd have to write a version of this for every primitive type though, just like the above.

      An interface file could then look something like:

      %module test
      
      %{
      void foo(void *arr);
      %}
      
      %typemap(in,numinputs=0) JNIEnv *env "$1 = jenv;"
      
      %rename(foo) fooFloat;
      %rename(foo) fooInt;
      %inline %{
      void fooFloat(JNIEnv *env, jfloatArray arr) {
        jboolean isCopy;
        foo((*env)->GetFloatArrayElements(env, arr, &isCopy));
        // Release after call with desired semantics
      }
      
      void fooInt(JNIEnv *env, jintArray arr) {
        jboolean isCopy;
        foo((*env)->GetIntArrayElements(env, arr, &isCopy));
        // Release after call
      }
      %}
      
      void foo(void *arr);
      

      Which then gives you overloads of foo which take float[] and int[] as well as SWIGTYPE_p_void.

    2. You could use a trick with a union:

      %inline %{
        union Bodge {
          void *v;
          float *f;
          int *i;
        };
      %}
      

      although this is considered bad form, it does generate you a Java interface that can be used to convert from SWIGTYPE_p_int to SWIGTYPE_p_void.

    3. I think it's possible to make FloatArray inherit from SWIGTYPE_p_void, something like the following compiled but untested code:

      %module test
      
      %include 
      
      %typemap(javabase) FloatArray "SWIGTYPE_p_void"
      %typemap(javabody) FloatArray %{
        private long swigCPtr; // Minor bodge to work around private variable in parent
        private boolean swigCMemOwn;
        public $javaclassname(long cPtr, boolean cMemoryOwn) {
          super(cPtr, cMemoryOwn);
          this.swigCPtr = SWIGTYPE_p_void.getCPtr(this);
          swigCMemOwn = cMemoryOwn;
        }
      %}
      
      %array_class(float, FloatArray);
      
      void foo(void *arr);
      

      This duplicates the pointer on the Java side, but nothing changes that (currently) in either the void pointer or array classes so that's not as big a problem as it first seems. (You could also make it protected in the base class with an alternative typemap I think, or use a modified version of carrays.i that gets swigCPtr via the getCPtr function instead)

提交回复
热议问题