如何使Swig正确包装Char*缓冲区,该缓冲区在C中被修改为Java或其他方式?

 2023-01-21    263  

问题描述

我正在尝试包装一些遗留代码以供在Java中使用,我很高兴看到Swig能够处理标头文件,并且生成了几乎可以使用的出色包装器.现在,我正在寻找可以真正起作用的深层魔法.

在C中,我的功能看起来像

如何使Swig正确包装Char*缓冲区,该缓冲区在C中被修改为Java或其他方式?

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不仅是输入,还会因功能而改变,应返回作为输出.它也不知道Java字符串确实是缓冲区,应该由适当尺寸的阵列提供支持.

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

推荐答案

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

我已经附上了下面的示例代码,我目前正在使用建议的替代方法.但是,使用字节键入WIL的第一种方法需要在我的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 Document :

某些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*,然后将其映射到swigtype_p_unsigned_char :-)而不是java中的字符串

以上所述是小编给大家介绍的如何使Swig正确包装Char*缓冲区,该缓冲区在C中被修改为Java或其他方式?,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对77isp云服务器技术网的支持!

原文链接:https://77isp.com/post/26216.html

=========================================

https://77isp.com/ 为 “云服务器技术网” 唯一官方服务平台,请勿相信其他任何渠道。