代码之家  ›  专栏  ›  技术社区  ›  Ukko

如何让Swig正确地包装一个char*缓冲区,这个缓冲区在C中被修改为Java之类的东西?

  •  7
  • Ukko  · 技术社区  · 16 年前

    在C中,我有一个函数

    DLL_IMPORT int DustyVoodoo(char *buff, int len,  char *curse);
    

    此函数返回的整数是一个错误代码,以防失败。论点是

    • buff 是字符缓冲区
    • len
    • curse 包含调用DustyVoodoo的结果的另一个字符缓冲区

    伦恩 因为这可能是两个缓冲区的长度,所以它们在调用代码时总是被分配为相同的大小,但是给定了什么 DustyVoodoo 我不认为他们需要一样吗。为了安全起见,实际上两个缓冲区的大小应该相同,比如512个字符。

    为绑定生成的C代码如下:

    SWIGEXPORT jint JNICALL Java_pemapiJNI_DustyVoodoo(JNIEnv *jenv, jclass jcls, jstring 
    
    jarg1, jint jarg2, jstring jarg3) {
      jint jresult = 0 ;
      char *arg1 = (char *) 0 ;
      int arg2 ;
      char *arg3 = (char *) 0 ;
      int result;
    
      (void)jenv;
      (void)jcls;
      arg1 = 0;
      if (jarg1) {
        arg1 = (char *)(*jenv)->GetStringUTFChars(jenv, jarg1, 0);
        if (!arg1) return 0;
      }
      arg2 = (int)jarg2; 
      arg3 = 0;
      if (jarg3) {
        arg3 = (char *)(*jenv)->GetStringUTFChars(jenv, jarg3, 0);
        if (!arg3) return 0;
      }
      result = (int)PemnEncrypt(arg1,arg2,arg3);
      jresult = (jint)result; 
      if (arg1) (*jenv)->ReleaseStringUTFChars(jenv, jarg1, (const char *)arg1);
      if (arg3) (*jenv)->ReleaseStringUTFChars(jenv, jarg3, (const char *)arg3);
      return jresult;
    }
    

    cursed 不仅仅是一个输入,它由函数更改,应该作为输出返回。它也不知道java字符串实际上是缓冲区,应该由一个大小合适的数组来支持。

    我认为Swig可以在这里做正确的事情,我只是不知道如何从文档中告诉Swig它需要知道什么。家里有打字机吗?

    3 回复  |  直到 16 年前
        1
  •  7
  •   Ukko    16 年前

    多亏托马斯朝着正确的方向前进。解决方法是创建一个自定义类型映射,使用StringBuffer返回结果。我在电话里找到了密码 examples/java/typemap SWIG安装的目录。我以前在寻找的时候一定忽略了这一点。

    我已经附上下面的示例代码,我目前正在使用建议的替代方法。然而,第一种使用BYTE-typemap的方法需要对Java代码进行一些更改,但从长远来看可能更有意义。

    谢谢你的帮助,现在我要看看我是否能接受我自己的答案。。。

    /* 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);
    
        2
  •  6
  •   Thomas    16 年前

    也许 this part SWIG文档的内容很有帮助:

    一些C程序中的一个常见问题是处理作为简单指针或引用传递的参数。例如:

    void add(int x, int y, int *result) {
        *result = x + y;
    }
    

    [...]

    在这种情况下,typemaps.i库文件会有所帮助。例如:

    %module example
    %include "typemaps.i"
    
    void add(int, int, int *OUTPUT);
    

    还有一个 section on wrapping arrays .

    很抱歉,这不是一个现成的,完整的答案。大口大口地喝有时会使人心神不宁。

        3
  •  0
  •   Bart Tomer W    14 年前

    另一个简单而笨拙的解决方法,如果你和我一样懒惰,如果你有自由改变C/C++函数的原型。 将参数的类型(在接口文件中)更改为unsigned char*,而不是char*,然后SWIG将其映射为SWIGTYPE\p\u unsigned\u char:-),而不是Java中的字符串