Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using SWIG to convert C++ char* as char[] in Java instead of String

Tags:

c++

swig

I am attempting to wrap the following C++ function with Java:

char* MyClass::to_cstring();

This output of this function is returned as a Java String object. I would like it to return as a char[] Java array. I am currently using "typemaps.i" and "std_string.i". Is there a way to override the behavior such that std::string is still returned as a Java String, but char* is returned as a Java char array?

How about using Java byte[] instead of char[] so there is not need to worry about translating between 8-bit C++ chars and Java's 16-bit Unicode?

like image 874
dcdo Avatar asked Oct 22 '22 06:10

dcdo


1 Answers

To do this you'll need to replace the default SWIG supplied typemaps with one of your own. The simplest way to do this requires just writing some Java "glue":

%module test

%typemap(jstype) char *to_cstring() "byte[]";
%typemap(javaout) char *to_cstring() {
  return $jnicall.getBytes();
}

%inline %{
char *to_cstring() {
  static char ret[] = "hello world";
  return ret;
}
%}

Does exactly what you want by calling getBytes() behind the scenes on the default returned String.

You can also do this with some JNI of your own to return it as a byte array all the way through from your native code:

%module test

%typemap(jstype) char *to_cstring() "byte[]";
%typemap(jtype) char *to_cstring() "byte[]";
%typemap(javaout) char *to_cstring() {
  return $jnicall;
}
%typemap(jni) char *to_cstring() "jbyteArray";
%typemap(out) char *to_cstring() {
  const size_t len = strlen($1);
  $result = JCALL1(NewByteArray, jenv, len);
  // TODO: check that this succeeded
  JCALL4(SetByteArrayRegion, jenv, $result, 0, len, (const jbyte*)$1);
}

%inline %{
char *to_cstring() {
  static char ret[] = "hello world";
  return ret;
}
%}

The difference here being that the mapping to byte[] happens in the generated JNI, not in the Java glue. The glue now just proxies directly to the JNI unchanged.

I was able to test and verify these typemaps with the following Java:

public class run {
  public static void main(String[] argv) {
    System.loadLibrary("test");
    byte[] ret = test.to_cstring();
    System.out.println(ret.length);
    System.out.println((char)ret[0]);
  }
}

In both these examples the typemaps match on both the return type (char *) and the function to_cstring(). You can adjust this typemap matching to be more or less selective. It currently doesn't change most normal usage, you can also use %apply to copy the typemap to other cases which don't exactly match.

like image 112
Flexo Avatar answered Nov 15 '22 04:11

Flexo