SWIG Поддержка типов возврата void * C

У меня есть функция C в моем файле интерфейса SWIG (включенном из файла заголовка), который в настоящее время возвращает char *, который использует SWIG для генерации возвращаемого типа String Java для метода fetchFromRow. Мне интересно, изменил ли я возврат к void * на стороне C, что сделает SWIG на стороне Java? Функция C fetchFromRow возвращает структуру (sockaddr_in), String или int. Как настроить файл интерфейса SWIG для поддержки этого? Есть ли способ заставить сгенерированный Java fetchFromRow иметь возвращаемый тип объекта, чтобы я мог просто нарисовать на стороне 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(******** 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 %{
******* 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 ответа

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

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

struct type1 {
 type1(int foo) : foo(foo) {}
 int foo;
};
struct type2 {
 type2(****** bar) : bar(bar) {}
 ****** 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 typemap)

Второй аргумент для каждого из конструкторов, рассматриваемый здесь как $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 на моем сайте.


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

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

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

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

Удачи.

licensed under cc by-sa 3.0 with attribution.