c12
c12

Reputation: 9827

SWIG Support of void * C return types

I have a C function in my SWIG interface file (included from a header file) that currently returns a char * that SWIG uses to generate a String Java return type for fetchFromRow method. I'm wondering if I change the return to a void * on the C side, what will SWIG do on the Java side? The C fetchFromRow function returns a structure (sockaddr_in), String or int. How can I setup my SWIG interface file to support this? Is there a way to make the generated Java fetchFromRow have a return type of Object so that I can just cast on the Java side?

C Code:

extern char *
fetchFromRow(struct row_t *r_row,
        type_t type);

extern void *
fetchFromRow(struct row_t *r_row,
        type_t type);

When I generate the method using the void * in the header file (included in SWIG interface file), I get a java method with a SWIGTYPE_p_void return type. Any ideas on how to handle that?

Swig File:

%module Example
%include "typemaps.i"
%include "stdint.i"
%include "arrays_java.i"
void setPhy_idx(uint32_t value);
%include "arrays_java.i"
void setId(unsigned char *value);
%{
#include "Example1.h"
#include "Example2.h"
#include "Example3.h"
#include "Example4.h"
%}
%rename setLogFile setLogFileAsString;
%inline %{
void setLogFileAsString(const char *fn) {
  FILE *f = fopen(fn, "w");
  setLogFile(f);
}
%}
%typemap(jstype) void* "java.lang.Object"
%typemap(javaout) void* {
  long cPtr = $jnicall;
  Object result = null;
  if (type == type_t.TYPE_CATEGORY) {
    result = void2int8(cPtr);
  }
  return result;
}

%newobject fetch(struct result_row_t *result_row, type_t type, int32_t *length);
%inline %{

uint8_t void2int8(jlong v) {
  return (intptr_t)v;
}

%}
%apply char * { unsigned char * };
%apply char * { const void * }
%apply int32_t { int32_t * }
int createKey(const void* secret, int secret_len, const sdr_id_t *id, unsigned char key[20], int key_len);
session_t* createSession(const char* addr, int ort, const char *ddr, int rt, const der_id_t *id, unsigned char key[20], int key_len); 
%apply int32_t *OUTPUT { int32_t *length }
%ignore createKey;
%ignore setLogFile;
%ignore ecreateSession;
%include "Example1.h"
%include "Example2.h"
%include "Example3.h"
%include "Example4.h"
%pragma(java) jniclasscode=%{
  static {
    try {
        System.loadLibrary("Example");
    } catch (UnsatisfiedLinkError e) {
      System.err.println("Native code library failed to load. \n" + e);
      System.exit(1);
    }
  }
%}

Java Code to call fetch:

{
   //only type_t param shown here for simplicity
   Object category = Example.fetch(result_row, type_t.TYPE_CATEGORY, length);
   System.out.println("category=" + aLevel.toString());
   System.out.println("category=" + ((Short)aLevel).intValue());
   System.out.println("category=" + ((Short)aLevel).toString());
   //all 3 Sys outs show same value but when called again each show same value but different than the first execution
}

C Code Wrapper that I'm attempting to Replace with SWIG. This use to be called from Java but now I'm trying to call fetch directly (pseudo code):

char *
wrapFetch(struct row_t *result_row, type_t type)
{
int8_t *int8_valp = NULL;
switch (attribute) {
 case TYPE_CATEGORY:

        int8_valp = fetch(
                result_row, attribute, &length);
        if (length > 0 && int8_valp != NULL) {
            snprintf(smallbuf, sizeof(smallbuf), "%u", *int8_valp);
            return strdup(smallbuf);
        } else {
            return NULL;
        }
}

Upvotes: 3

Views: 4018

Answers (2)

This might be easier if you defined a type hierarchy for the return types, and used the base class as the return type for fetchFromRow. (It turns out that solution isn't as easy as I originally thought it would be and there's even an example in the documentation! This question also applies to Java+SWIG) As it stands though doing what you asked is possible, I've made a simpler example to illustrate the important points.

The example I'm working with here has a simple interface, in test.h (both the declarations and the definitions to keep things simpler here):

struct type1 {
  type1(int foo) : foo(foo) {}
  int foo;
};

struct type2 {
  type2(double bar) : bar(bar) {}
  double bar;
};

// TYPE3 is int32_t, TYPE4 is const char*

typedef enum { TYPE1, TYPE2, TYPE3, TYPE4 } type_t;

void* fetch(type_t type) {
  switch(type) {
  case TYPE1:
    return new type1(101);
  case TYPE2:
    return new type2(1.111);
  case TYPE3:
    return (void*)(-123); // One way of returning int32_t via void*!
  case TYPE4:
    return (void*)("Hello world"); // Cast because not const void*
  default:
    return NULL;
  }
}

Here we have four different types, paired with an enum value. These were picked for simplicity. They could be anything you want, provided you have a suitable typemap that can be applied. (If you want sockaddr_in specifically then apply the typemap from my answer to your previous question about wrapping sockaddr_in specifically).

There's also a function fetch which creates one of the types and returns it via void *. This illustrates what you asked about in the question. The tricky thing with fetch is that SWIG has no direct way of inferring what was behind the void* pointer before it got returned. We need to give SWIG a way of knowing more specifically what the type is, using our higher level knowledge about the meaning of the type_t parameter.

To wrap this we need a SWIG interface file, test.i which begins with the usual module stuff:

%module test
%{
#include "test.h"
%}

In order to wrap our fetch function we need to find a sensible way of exposing the closest thing to void* on the Java side. In this instance I think java.lang.Object is a good choice for the return type and it approximates the void* reasonably well here.

%typemap(jstype) void* "java.lang.Object"

This sets up what the return type from fetch on the Java side will be. (We've not changed the return type of the generated JNI intermediary using %typemap(jtype) though, this will still default to long as before).

Next we need to write another typemap to specify how the result of the actual call is going to get converted into the type we said the call would return on the Java side:

%typemap(javaout) void* {
  long cPtr = $jnicall;
  Object result = null;
  if (type == type_t.TYPE1) {
    result = new type1(cPtr, $owner);
  }
  else if (type == type_t.TYPE2) {
    result = new type2(cPtr, $owner);
  }
  else if (type == type_t.TYPE3) {
    result = void2int(cPtr);
    // could also write "result = new Integer(void2int(cPtr));" explicitly here
  }
  else if (type == type_t.TYPE4) {
    result = void2str(cPtr);
  }

  return result;
}

%newobject fetch(type_t type);

Here we create a Java object, of the appropriate the SWIG proxy types, or call a helper function we'll see shortly. We decide which to type to use by looking at the type_t that was used in the call. (I'm not 100% happy about referring to this type by name, i.e. type directly but there doesn't seem to be a better way of gaining access to the parameters a function was called with inside a javaout typemap)

The second argument to each of the constructors, seen here as $owner is important for memory management, it states who owns the allocation and we'd like to transfer ownership to Java to avoid leaking it. Its value is determined by the %newobject directive.

We need a helper function to convert the void* to more meaningful types for the int32_t and String cases. We supply this with %inline to ask SWIG to wrap and define it at the same time:

%inline %{
int32_t void2int(jlong v) {
  return (intptr_t)v;
}

const char *void2str(jlong v) {
  return (const char*)v;
}
%}

I used jlong here because on the Java side of the interface all pointers are represented as long. It ensures the functions are exactly compatible with what the JNI call is returning, without losing precision (on some platforms it's possible that jlong might be a long long). Basically these functions exist to make the conversion from the generic (void*) to the specific on the C side with as little manual work as possible. SWIG handles all of the types for us here.

Finally we finish up with %include for the header file (we want to wrap it all so that's the simplest way) and some code to cause the library to get loaded automatically:

%include "test.h"

%pragma(java) jniclasscode=%{
  static {
    try {
        System.loadLibrary("test");
    } catch (UnsatisfiedLinkError e) {
      System.err.println("Native code library failed to load. \n" + e);
      System.exit(1);
    }
  }
%}

I tested this wrapping by compiling:

swig -Wall -java -c++ test.i
javac *.java 
g++ -Wall -Wextra test_wrap.cxx -shared -I/usr/lib/jvm/java-6-sun/include -I/usr/lib/jvm/java-6-sun/include/linux/ -o libtest.so 

and running the following Java code to "exercise" it a bit.

public class main {
  public static void main(String[] argv) {
    Object o1 = test.fetch(type_t.TYPE1);
    Object o2 = test.fetch(type_t.TYPE2);
    Object o3 = test.fetch(type_t.TYPE3);
    Object o4 = test.fetch(type_t.TYPE4);

    if (!(o1 instanceof type1)) {
      System.out.println("Wrong type - o1");
    }
    else {
      System.out.println("o1.getFoo(): " + ((type1)o1).getFoo());
    }

    if (!(o2 instanceof type2)) {
      System.out.println("Wrong type - o2");
    }
    else {
      System.out.println("o2.getFoo(): " + ((type2)o2).getBar());
    }

    if (!(o3 instanceof Integer)) {
      System.out.println("Wrong type - o3");
    }
    else {
      System.out.println("o3.intValue(): " + ((Integer)o3).intValue());
    }

    if (!(o4 instanceof String)) {
      System.out.println("Wrong type - o4");
    }
    else {
      System.out.println("o4: " + (String)o4);
    }
  }
}

You can always recreate the whole example from the code shown here, test.i is shown and discussed sequentially, but for convenience I've also put a copy of test.i on my site.

Upvotes: 3

Miguel Grinberg
Miguel Grinberg

Reputation: 67509

The approach I like to use for tough cases like this one is to (a) simplify the C interface to get better results from SWIG, and (b) bring the complexity back in the target language as a wrapper to the simplified C functions.

In your case, I would add two additional functions to the C API:

char* fetchFromRowString(struct row_t *r_row);
int fetchFromRowInt(struct row_t *r_row);

These would be a piece of cake for SWIG to handle. Then on the Java side you can recreate the original fetchFromRow(), where the implementation calls the String or Int version of the C function depending on the type, and finally returns the result as Object.

Good luck.

Upvotes: 1

Related Questions