How can I make Swig correctly wrap a char* buffer that is modified in C as a Java Something-or-other?

后端 未结 3 1987
春和景丽
春和景丽 2020-12-30 10:11

I am trying to wrap some legacy code for use in Java and I was quite happy to see that Swig was able to handle the header file and it generate a great wrapper that almost wo

3条回答
  •  被撕碎了的回忆
    2020-12-30 11:07

    Thanks to Thomas for the nudge in the correct direction. The solution to this was to create a custom typemap that uses a StringBuffer to get the result back. I found the code in the examples/java/typemap directory of the SWIG install. I must have overlooked that before while I was searching.

    I have attached the example code below, I am presently using the alternative approach suggested. However, the first approach of using the BYTE typemap wil require some changes in my Java code but might actually make more sense in the long run.

    Thanks for the help, and now I get to see if I can accept my own answer...

    /* File : example.i */
    %module example
    %{
    /*
       example of a function that returns a value in the char * argument
       normally used like:
    
       char buf[bigenough];
       f1(buf);
    */
    
    void f1(char *s) {
      if(s != NULL) {
        sprintf(s, "hello world");
      }
    }
    
    void f2(char *s) {
      f1(s);
    }
    
    void f3(char *s) {
      f1(s);
    }
    
    %}
    
    /* default behaviour is that of input arg, Java cannot return a value in a 
     * string argument, so any changes made by f1(char*) will not be seen in the Java
     * string passed to the f1 function.
    */
    void f1(char *s);
    
    %include various.i
    
    /* use the BYTE argout typemap to get around this. Changes in the string by 
     * f2 can be seen in Java. */
    void f2(char *BYTE);
    
    
    
    /* Alternative approach uses a StringBuffer typemap for argout */
    
    /* Define the types to use in the generated JNI C code and Java code */
    %typemap(jni) char *SBUF "jobject"
    %typemap(jtype) char *SBUF "StringBuffer"
    %typemap(jstype) char *SBUF "StringBuffer"
    
    /* How to convert Java(JNI) type to requested C type */
    %typemap(in) char *SBUF {
    
      $1 = NULL;
      if($input != NULL) {
        /* Get the String from the StringBuffer */
        jmethodID setLengthID;
        jclass sbufClass = (*jenv)->GetObjectClass(jenv, $input);
        jmethodID toStringID = (*jenv)->GetMethodID(jenv, sbufClass, "toString", "()Ljava/lang/String;");
        jstring js = (jstring) (*jenv)->CallObjectMethod(jenv, $input, toStringID);
    
        /* Convert the String to a C string */
        const char *pCharStr = (*jenv)->GetStringUTFChars(jenv, js, 0);
    
        /* Take a copy of the C string as the typemap is for a non const C string */
        jmethodID capacityID = (*jenv)->GetMethodID(jenv, sbufClass, "capacity", "()I");
        jint capacity = (*jenv)->CallIntMethod(jenv, $input, capacityID);
        $1 = (char *) malloc(capacity+1);
        strcpy($1, pCharStr);
    
        /* Release the UTF string we obtained with GetStringUTFChars */
        (*jenv)->ReleaseStringUTFChars(jenv,  js, pCharStr);
    
        /* Zero the original StringBuffer, so we can replace it with the result */
        setLengthID = (*jenv)->GetMethodID(jenv, sbufClass, "setLength", "(I)V");
        (*jenv)->CallVoidMethod(jenv, $input, setLengthID, (jint) 0);
      }
    }
    
    /* How to convert the C type to the Java(JNI) type */
    %typemap(argout) char *SBUF {
    
      if($1 != NULL) {
        /* Append the result to the empty StringBuffer */
        jstring newString = (*jenv)->NewStringUTF(jenv, $1);
        jclass sbufClass = (*jenv)->GetObjectClass(jenv, $input);
        jmethodID appendStringID = (*jenv)->GetMethodID(jenv, sbufClass, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
        (*jenv)->CallObjectMethod(jenv, $input, appendStringID, newString);
    
        /* Clean up the string object, no longer needed */
        free($1);
        $1 = NULL;
      }  
    }
    /* Prevent the default freearg typemap from being used */
    %typemap(freearg) char *SBUF ""
    
    /* Convert the jstype to jtype typemap type */
    %typemap(javain) char *SBUF "$javainput"
    
    /* apply the new typemap to our function */
    void f3(char *SBUF);
    

提交回复
热议问题