Как я могу заставить Swig правильно обернуть буфер char *, модифицированный в C как Java-то или другое? - PullRequest
7 голосов
/ 29 апреля 2010

Я пытаюсь обернуть некоторый унаследованный код для использования в Java, и я был очень рад видеть, что Swig смог обработать заголовочный файл, и он генерирует отличную оболочку, которая почти работает. Теперь я ищу глубокую магию, которая заставит его действительно работать.

В C у меня есть функция, которая выглядит следующим образом

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, что ему нужно знать. Какие-нибудь мазеры в доме?

Ответы [ 3 ]

7 голосов
/ 30 апреля 2010

Спасибо Томасу за толчок в правильном направлении.Решением этой проблемы было создание пользовательской карты типов, которая использует StringBuffer для возврата результата.Я нашел код в каталоге examples/java/typemap установки SWIG.Должно быть, я упустил это из виду, пока искал.

Я приложил приведенный ниже пример кода, в настоящее время я использую предложенный альтернативный подход.Тем не менее, первый подход к использованию таблицы типов 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);
6 голосов
/ 30 апреля 2010

Возможно эта часть документации 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 порой сходит с ума.

0 голосов
/ 16 июля 2012

Еще один простой и тупой обходной путь, если вы такой же ленивый, как и я, и если у вас есть возможность немного изменить прототип функции C / C ++. Измените тип вашего параметра (в файле интерфейса) на unsigned char * вместо char *, а затем SWIG отобразит его в SWIGTYPE_p_unsigned_char :-) вместо String в Java

...