如何让Swig正确地将在C中修改的char *缓冲区包装为Java Something-other?

我正在尝试包装一些用于Java的遗留代码,我很高兴看到Swig能够处理头文件并生成一个几乎可以工作的好包装器。 现在我正在寻找能让它真正发挥作用的深刻魔法。

在CI中有一个看起来像这样的function

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

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

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

所以,你可以看到它的发展方向,结果实际上是通过第三个参数返回的。 len也是令人困惑的,因为它可能是两个缓冲区的长度,它们总是在调用代码中被分配为相同的大小,但是考虑到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不仅仅是一个输入,它被function改变,应该作为输出返回。 它也不知道java字符串是真正的缓冲区,应该由适当大小的数组支持。

我认为Swig可以在这里做正确的事情,我只是无法从文档中弄清楚如何告诉Swig它需要知道什么。 房子里有任何类型图贴图吗?

感谢托马斯在正确的方向上轻推。 解决方案是创建一个使用StringBuffer来获取结果的自定义类型映射。 我在SWIG安装的examples/java/typemap目录中找到了代码。 在我搜索之前,我一定忽略了这一点。

我已经附上了下面的示例代码,我目前正在使用建议的替代方法。 但是,使用BYTE类型映射的第一种方法需要对我的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); 

也许这部分 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); 

还有一个关于包装数组的部分 。

对不起,这不是一个现成的,完整的答案。 SWIG有时令人费解。

另一个简单而愚蠢的解决方法,如果你像我一样懒惰,如果你有权自由地改变C / C ++函数的原型。 将参数类型(在接口文件中)更改为unsigned char *而不是char *,然后SWIG将其映射到SWIGTYPE_p_unsigned_char :-)而不是Java中的String