0

I have a C API that looks like this:

int my_function(char** assign_me_a_string);

I basically need the native code to tell me a value. If I do:

char* my_function();

It works fine. I get a String in return that I can use in Java. However I would prefer to use the first approach since all my functions returns an int by default (status value).

I have tried to use various.i and this typemap:

%apply char **STRING_ARRAY { char **assign_me_a_string }

Doing this I get a String[] generated for the Java API. Then I try to use it by:

String[] myStringToAssign = new String[1];
my_function(myStringToAssign);

But this seems to just crash.

So, is there a proper way to assign a value to a Java String from inside the C code? I am not trying to use an array, I just need to be able to dereference the char** and assign it a string in the native code that can then be used as a String object in Java.

user1816142
  • 1,199
  • 2
  • 9
  • 16

2 Answers2

3

Java String is immutable and references are passed by value so the following will not do what you think (pure Java):

// java: 
void someFunction(String yourString) {yourString = "bye";}
void test() {
    String test = "hi";
    someFunction(test);
    System.out.println(test); // prints "hi", not "bye"!
}

(For more on this, see for example Passing a String by Reference in Java?). Needless to say you can't do it using JNI either.

Look at section 24.10.5 of SWIG 2.0 docs: it shows how you could do this for char** via a typemap. However the typemap there would have to be modified to check the char* array on exit (the freearg typemap, perhaps) to replace the contents of the String[]. Your function could assume only one item.

Alternately you could wrap your Java String in a class, which will get passed by reference; however you again would have to use a typemap to copy any changes that have been made into the data member.

Probably the easiest is to provide a helper function that swaps the status code and string:

// C/C++ lib: the function you'd like to export but can't:
int my_function(char*& ) { 
    val = new char[20];
    ...put stuff in val, don't forget the terminating \0 char...
    return status;
}

Create wrapper:

// SWIG .i file: an "adapter" function, Java gives you no choice: 
%inline %{
char* my_function(int& err) {
    char * val;
    err = my_function(val);
    return val;
%}

SWIG will take care of copying the char* to the returned String:

// From Java you can then do: 
int status;
String result = my_function(status);
System.out.println(result);
Community
  • 1
  • 1
Oliver
  • 27,510
  • 9
  • 72
  • 103
  • Thanks for taking the time to propose an alternative solution as well. – user1816142 Oct 26 '13 at 12:52
  • The first example is incorrect. `yourString = "bye"` will work just fine because it doesn't mutate the string - it changes the `yourString` reference to point at a different string (`"bye"` in this case). – pburka Oct 26 '13 at 19:41
  • @pburka I perhaps wasn't clear but what you explain is precisely the problem: the call will succeed but the result will not be what user1816142 was expecting (after calling yourFunction(yourString), yourString will not be "bye" but rather whatever it was before call). – Oliver Oct 27 '13 at 03:40
  • @Schollii My apologies. I'd misinterpreted "will not work" to mean that it would crash or fail to compile. On a closer rereading I see what you meant. – pburka Oct 27 '13 at 04:07
  • @pburka no problem, I wasn't clear, I edited that part of the post to (hopefully) clarify – Oliver Oct 27 '13 at 04:18
1

No, Java Strings are immutable and cannot be assigned not even through JNI.

Klas Lindbäck
  • 33,105
  • 5
  • 57
  • 82