SWIG Поддержка типов возврата void * C - PullRequest
3 голосов
/ 18 ноября 2011

У меня есть функция C в моем файле интерфейса SWIG (включенная из заголовочного файла), которая в настоящее время возвращает символ *, который SWIG использует для генерации возвращаемого типа String Java для метода fetchFromRow. Мне интересно, если я изменю возврат на void * на стороне C, что SWIG будет делать на стороне Java? Функция C fetchFromRow возвращает структуру (sockaddr_in), String или int. Как мне настроить файл интерфейса SWIG для поддержки этого? Есть ли способ заставить сгенерированный Java fetchFromRow иметь возвращаемый тип Object, чтобы я мог просто приводить его на стороне Java?

C код:

extern char *
fetchFromRow(struct row_t *r_row,
        type_t type);

extern void *
fetchFromRow(struct row_t *r_row,
        type_t type);

Когда я генерирую метод, используя void * в заголовочном файле (включенном в файл интерфейса SWIG), я получаю java-метод с возвращаемым типом SWIGTYPE_p_void. Есть идеи, как с этим справиться?

Файл Swig:

%module Example
%include "typemaps.i"
%include "stdint.i"
%include "arrays_java.i"
void setPhy_idx(uint32_t value);
%include "arrays_java.i"
void setId(unsigned char *value);
%{
#include "Example1.h"
#include "Example2.h"
#include "Example3.h"
#include "Example4.h"
%}
%rename setLogFile setLogFileAsString;
%inline %{
void setLogFileAsString(const char *fn) {
  FILE *f = fopen(fn, "w");
  setLogFile(f);
}
%}
%typemap(jstype) void* "java.lang.Object"
%typemap(javaout) void* {
  long cPtr = $jnicall;
  Object result = null;
  if (type == type_t.TYPE_CATEGORY) {
    result = void2int8(cPtr);
  }
  return result;
}

%newobject fetch(struct result_row_t *result_row, type_t type, int32_t *length);
%inline %{

uint8_t void2int8(jlong v) {
  return (intptr_t)v;
}

%}
%apply char * { unsigned char * };
%apply char * { const void * }
%apply int32_t { int32_t * }
int createKey(const void* secret, int secret_len, const sdr_id_t *id, unsigned char key[20], int key_len);
session_t* createSession(const char* addr, int ort, const char *ddr, int rt, const der_id_t *id, unsigned char key[20], int key_len); 
%apply int32_t *OUTPUT { int32_t *length }
%ignore createKey;
%ignore setLogFile;
%ignore ecreateSession;
%include "Example1.h"
%include "Example2.h"
%include "Example3.h"
%include "Example4.h"
%pragma(java) jniclasscode=%{
  static {
    try {
        System.loadLibrary("Example");
    } catch (UnsatisfiedLinkError e) {
      System.err.println("Native code library failed to load. \n" + e);
      System.exit(1);
    }
  }
%}

Java-код для вызова fetch:

{
   //only type_t param shown here for simplicity
   Object category = Example.fetch(result_row, type_t.TYPE_CATEGORY, length);
   System.out.println("category=" + aLevel.toString());
   System.out.println("category=" + ((Short)aLevel).intValue());
   System.out.println("category=" + ((Short)aLevel).toString());
   //all 3 Sys outs show same value but when called again each show same value but different than the first execution
}

C Code Wrapper, который я пытаюсь заменить на SWIG. Это использование вызывается из Java, но сейчас я пытаюсь вызвать fetch напрямую (псевдокод):

char *
wrapFetch(struct row_t *result_row, type_t type)
{
int8_t *int8_valp = NULL;
switch (attribute) {
 case TYPE_CATEGORY:

        int8_valp = fetch(
                result_row, attribute, &length);
        if (length > 0 && int8_valp != NULL) {
            snprintf(smallbuf, sizeof(smallbuf), "%u", *int8_valp);
            return strdup(smallbuf);
        } else {
            return NULL;
        }
}

Ответы [ 2 ]

3 голосов
/ 19 ноября 2011

Это может быть проще, если вы определили иерархию типов для возвращаемых типов и использовали базовый класс в качестве возвращаемого типа для fetchFromRow.(Оказывается, что решение не так просто, как я думал, и даже пример в документации ! Этот вопрос также относится к Java + SWIG ).Несмотря на то, что выполнение заданного вами вопроса возможно, я сделал более простой пример, чтобы проиллюстрировать важные моменты.

Пример, с которым я здесь работаю, имеет простой интерфейс, test.h (как объявления, так иопределения для упрощения):

struct type1 {
  type1(int foo) : foo(foo) {}
  int foo;
};

struct type2 {
  type2(double bar) : bar(bar) {}
  double bar;
};

// TYPE3 is int32_t, TYPE4 is const char*

typedef enum { TYPE1, TYPE2, TYPE3, TYPE4 } type_t;

void* fetch(type_t type) {
  switch(type) {
  case TYPE1:
    return new type1(101);
  case TYPE2:
    return new type2(1.111);
  case TYPE3:
    return (void*)(-123); // One way of returning int32_t via void*!
  case TYPE4:
    return (void*)("Hello world"); // Cast because not const void*
  default:
    return NULL;
  }
}

Здесь у нас есть четыре различных типа в сочетании со значением enum.Они были выбраны для простоты.Они могут быть чем угодно, если у вас есть подходящая карта типов, которую можно применить.(Если вы хотите sockaddr_in конкретно, тогда примените карту типов из моего ответа на ваш предыдущий вопрос о переносе sockaddr_in конкретно).

Также есть функция fetch, которая создает одну изтипы и возвращает его через void *.Это иллюстрирует то, что вы спросили в вопросе.Хитрость с fetch заключается в том, что SWIG не имеет прямого способа вывести то, что находилось за указателем void* до того, как он был возвращен.Нам нужно дать SWIG способ более точно узнать, что это за тип, используя наши знания более высокого уровня о значении параметра type_t.

Чтобы обернуть это, нам нужен файл интерфейса SWIG, test.iкоторый начинается с обычного модуля:

%module test
%{
#include "test.h"
%}

Чтобы обернуть нашу функцию fetch, нам нужно найти разумный способ выставить ближайшую вещь к void* на стороне Java.В этом случае я думаю, что java.lang.Object является хорошим выбором для возвращаемого типа, и он достаточно хорошо приближается к void*.

%typemap(jstype) void* "java.lang.Object"

Это устанавливает, какой тип возврата из fetch на Javaсторона будет.(Мы не изменили тип возвращаемого значения сгенерированного JNI-посредника, используя %typemap(jtype), однако по умолчанию он будет long, как и раньше).

Далее нам нужно написать еще одну карту типов, чтобы указать, как результатфактического вызова будет преобразован в тип, который мы сказали, что вызов вернется на стороне Java:

%typemap(javaout) void* {
  long cPtr = $jnicall;
  Object result = null;
  if (type == type_t.TYPE1) {
    result = new type1(cPtr, $owner);
  }
  else if (type == type_t.TYPE2) {
    result = new type2(cPtr, $owner);
  }
  else if (type == type_t.TYPE3) {
    result = void2int(cPtr);
    // could also write "result = new Integer(void2int(cPtr));" explicitly here
  }
  else if (type == type_t.TYPE4) {
    result = void2str(cPtr);
  }

  return result;
}

%newobject fetch(type_t type);

Здесь мы создаем объект Java, соответствующие прокси-типы SWIG, или вызываемвспомогательную функцию мы увидим в ближайшее время.Мы решаем, какой тип печатать, используя type_t, который использовался при вызове.(Я не на 100% доволен ссылкой на этот тип по имени, то есть type напрямую, но, похоже, нет лучшего способа получить доступ к параметрам, с которыми функция вызывается внутри javaout карты типов)

Второй аргумент для каждого из конструкторов, рассматриваемый здесь как $owner, важен для управления памятью, в нем указывается, кому принадлежит распределение, и мы хотели бы перенести владение на Java, чтобы избежать его утечки.Его значение определяется директивой %newobject.

Нам нужна вспомогательная функция для преобразования void* в более значимые типы для случаев int32_t и String.Мы поставляем это с %inline, чтобы попросить SWIG обернуть и определить его одновременно:

%inline %{
int32_t void2int(jlong v) {
  return (intptr_t)v;
}

const char *void2str(jlong v) {
  return (const char*)v;
}
%}

Я использовал jlong здесь, потому что на стороне Java интерфейса все указатели представлены как long.Это гарантирует, что функции точно совместимы с тем, что возвращает вызов JNI, без потери точности (на некоторых платформах возможно, что jlong может быть long long).В основном эти функции существуют для того, чтобы сделать преобразование из общего (void*) в специфическое на стороне C с минимальным ручным трудом, насколько это возможно.SWIG обрабатывает все типы для нас здесь.

Наконец мы заканчиваем с %include для заголовочного файла (мы хотим обернуть все это так, чтобы это было проще всего) и некоторый код, чтобы библиотека получилазагружен автоматически:

%include "test.h"

%pragma(java) jniclasscode=%{
  static {
    try {
        System.loadLibrary("test");
    } catch (UnsatisfiedLinkError e) {
      System.err.println("Native code library failed to load. \n" + e);
      System.exit(1);
    }
  }
%}

Я проверил эту упаковку, скомпилировав:

swig -Wall -java -c++ test.i
javac *.java 
g++ -Wall -Wextra test_wrap.cxx -shared -I/usr/lib/jvm/java-6-sun/include -I/usr/lib/jvm/java-6-sun/include/linux/ -o libtest.so 

и выполнив следующий Java-код, чтобы немного его «тренировать».

public class main {
  public static void main(String[] argv) {
    Object o1 = test.fetch(type_t.TYPE1);
    Object o2 = test.fetch(type_t.TYPE2);
    Object o3 = test.fetch(type_t.TYPE3);
    Object o4 = test.fetch(type_t.TYPE4);

    if (!(o1 instanceof type1)) {
      System.out.println("Wrong type - o1");
    }
    else {
      System.out.println("o1.getFoo(): " + ((type1)o1).getFoo());
    }

    if (!(o2 instanceof type2)) {
      System.out.println("Wrong type - o2");
    }
    else {
      System.out.println("o2.getFoo(): " + ((type2)o2).getBar());
    }

    if (!(o3 instanceof Integer)) {
      System.out.println("Wrong type - o3");
    }
    else {
      System.out.println("o3.intValue(): " + ((Integer)o3).intValue());
    }

    if (!(o4 instanceof String)) {
      System.out.println("Wrong type - o4");
    }
    else {
      System.out.println("o4: " + (String)o4);
    }
  }
}

Вы всегда можете воссоздать весь пример из кода, показанного здесь, test.i показывается и обсуждается последовательно, но для удобства я также разместил копию test.i на моем сайте .

1 голос
/ 19 ноября 2011

Подход, который я хотел бы использовать для таких сложных случаев, как этот, заключается в том, чтобы (а) упростить интерфейс C, чтобы получить лучшие результаты от SWIG, и (б) вернуть сложность на целевом языке в качестве оболочки для упрощенного Cfunctions.

В вашем случае я бы добавил две дополнительные функции в C API:

char* fetchFromRowString(struct row_t *r_row);
int fetchFromRowInt(struct row_t *r_row);

Это было бы просто для SWIG.Затем на стороне Java вы можете воссоздать исходную fetchFromRow(), где реализация вызывает String или Int версию функции C в зависимости от типа и, наконец, возвращает результат как Object.

Удачи.

...