Search code examples
javajava-native-interfaceswigvoid-pointers

SWIG (Java): How can I call a function with a void* out parameter?


Background

I am developing a real-time communication app for Android using a C++ code library. I generate a JNI bridge using SWIG to access the native code from Java. In order to keep track of an active call, a void* is used as a handle (which points to an adress containing information about the ongoing call). The following function headers are examples of how it is meant to be used:

void initiateCall(void** callHandleRef);

void closeCall(void* callHandle);

initateCall takes a void**, allocates a struct and writes its address (as a void*) to callHandleRef. It works as an "out" parameter in that you pass it as an argument and expect something to be written to it. When closeCall is called, the raw pointer is passed, allowing the C++ library to access the data associated with that specific call.

Problem description

I want to invoke initiateCall and closeCall from Java, using SWIG to generate the JNI code necessary to do so. However. SWIG generates types "SWIGTYPE_p_p_void" and "SWIGTYPE_p_void" respectively, and I am unable to use these in the way described above. How to I setup my SWIG interface file to allow me to do this?


Solution

  • Flexo's comment on the question turned out to work for me, as seen here. This is my solution, more or less copy-pasted from their answer.

    C++ header (call.h):

    typedef void* CALL_HANDLE;
    
    int initiateCall(const char* name, CALL_HANDLE* handle);
    int closeCall(CALL_HANDLE handle);
    
    

    SWIG interface (call.i):

    %module bridge
    
    %include <cpointer.i>
    
    %pointer_class(CALL_HANDLE, inst_ptr);
    %javamethodmodifiers initiateCall "private";
    %pragma(java) modulecode=%{
      public static SWIGTYPE_p_void initiateCall(String name) {
          inst_ptr ptr = new inst_ptr();
          final int err = initiateCall(name, ptr.cast());
          if (0 != err) {
              // Throw error or something like that
          }
          return ptr.value();
      }
    %}
    
    %include "include/call.h"
    
    extern int initiateCall(const char* name, CALL_HANDLE* handle);
    extern int closeCall(CALL_HANDLE handle);
    

    Main activity (MainActivity.kt):

    class MainActivity : AppCompatActivity() {
        private lateinit var callHandle: SWIGTYPE_p_void
    
        fun call(name: String) {
            callHandle = bridge.initiateCall(name)
        }
    
        fun hangup() {
            bridge.closeCall(callHandle)
        }
    }