Карта типов SWIG для возврата выходного параметра char * / size_t - PullRequest
1 голос
/ 24 апреля 2019

У меня есть C API, который использует выходные параметры для строк. (Реальная подпись была изменена для защиты невинных. Это упрощенный пример.)

void func( char* buf, size_t buflen, char* buf2, size_t buf2len );

buf и buflen - это эффективные выходные параметры, где buflenи buf2len - (уже) выделенный размер этих буферов.

В коде вызова я не хочу передавать какие-либо параметры.Скорее, я хочу, чтобы строки были возвращены.

result1,result2 = func()

Я бы предпочел не передавать буфер / размер в функцию-оболочку, а вместо этого распределять ее оболочкой, превращать в строку Python иосвобождается перед возвратом строки Python.

Большинство связанных с этим типовых карт cstring.i , которые, как я вижу, требуют, я должен дать функции-обертке строку.Все карты типов allocate хотят получить char**.

Я ищу поведение, подобное использованию OUTPUT в качестве имени outparam, но пара буфер / размер является (одиночной) outparam.

У меня нет полномочий изменять API.Я просто хочу сделать его простым в использовании.

Уже есть типографская карта для этого или вы можете помочь мне создать ее?

Пробная версия 1 (только Python)

Я получил это для работы (без проверки производительности или использования памяти).

%typemap(in,numinputs=0)(char* mutable_buffer, size_t mutable_buffer_size) {
    $1 = malloc(sizeof(char)*4096);
    $1[0] = 0x0;
    $2 = 4096;
}
%typemap(argout)(char* mutable_buffer, size_t mutable_buffer_size) {
#ifdef SWIGPYTHON
    PyObject *o;
    $1[4095] = 0x0; // null-terminate it, just in case
    o = PyUnicode_FromString($1);
    resultobj = SWIG_Python_AppendOutput(resultobj,o);
#endif
}
%typemap(freearg)(char* mutable_buffer, size_t mutable_buffer_size) {
    free($1);
}

Я бы предпочел решить эту проблему, не прибегая к специфическим для lang исправлениям.

1 Ответ

1 голос
/ 26 апреля 2019

Поскольку вы специально запросили решения, для которых не требуется ничего конкретного языка поддержки, я бы предложил использовать %inline для предоставления альтернативной формы func с синтаксисом, который вы предпочитаете.Что-то вроде этого должно сделать это:

%module test

%rename(func) func_adjusted; 
%newobject func_adjusted();

%inline %{
  struct func1_out {
    // can make tuple syntax work here if you would rather, but likely having names is clearer anyway
    char buf1[4096];
    size_t buf1len;
    char buf2[4096];
    size_t buf2len;
  };

  // with the rename we pretend this is the C function, but really it is specific to our wrapper module.
  struct func1_out *func_adjusted() {
    struct func1_out *ret = malloc(sizeof *ret);
    // could also dynamically allocate buf1 and buf2 instead of fixed max size.
    func(buf1, &buf1len, buf2, &buf2len);
    return ret;
  }
%}

%ignore func;
%include "blahblah.h"

Вы, вероятно, захотите немного больше поработать с массивами char buf1 и buf2 внутри этой структуры, чтобы сделать ее более естественной для пользователя Python, но это можно сделать с помощью carrays.i или %extend в структуре, чтобы сохранить все это в C / SWIG, а не на Python.


Вы также можете сделать что-то не специфичноек функции, которую вы оборачиваете, используя многопараметрические карты типов.Чтобы избежать специфики Python, вы можете использовать %append_output для возврата нескольких элементов из одной функции.Это не работает для статически типизированных языков, таких как Java или C #, но оно должно работать для большинства / всех динамически типизированных.

Чтобы в дальнейшем избежать необходимости в дополнительном, специфичном для языка коде, мы можем использовать carrays.i, которыйгенерирует некоторые дополнительные функции для каждого определяемого нами типа.В частности, мы используем ByteArray_cast, new_ByteArray и delete_ByteArray для обработки довольно большого количества случаев, с которыми мы можем столкнуться.Это должно работать как для C, так и для C ++.

%module test

%include <carrays.i>
%array_class(char, ByteArray);

%typemap(in,numinputs=0) (char* mutable_buffer, size_t mutable_buffer_size) (ByteArray *tmp=NULL) {
    // N.B. using new_ByteArray() here makes this work well with both C and C++ code
    tmp = new_ByteArray(4096);
    $1 = ByteArray_cast(tmp);
    $1[0] = 0x0;
    $2 = 4096;
}

%typemap(freearg) (char* mutable_buffer, size_t mutable_buffer_size) {
    // conditional is needed here as in some cases delete_ByteArray would dereference a null pointer
    if (tmp$argnum) delete_ByteArray(tmp$argnum);
}

%typemap(argout) (char* mutable_buffer, size_t mutable_buffer_size) {
   // Take ownership from in typemap
   %append_output(SWIG_NewPointerObj(SWIG_as_voidptr(tmp$argnum), $descriptor(ByteArray*), SWIG_POINTER_NEW));
   tmp$argnum = NULL;
}

%apply (char *mutable_buffer, size_t mutable_buffer_size) { (char *buf, size_t buflen), (char *buf2, size_t buf2len) };

%inline %{
void func(char* buf, size_t buflen, char* buf2, size_t buf2len) {
  strncpy(buf, "this is buffer1", buflen);
  strncpy(buf2, "this is buffer2", buf2len);
}
%}

Это работает, как и ожидалось, с Python 3.7 в моем тестировании.Обратите внимание, что вам нужно запустить swig с аргументом -builtin, чтобы получить точное поведение, которое вы ищете здесь, или потребовать дополнительный пользовательский код для обходного пути:

a,b = test.func()
# needed for the non builtin case, I can't see a way to avoid that without Python specific code
aa=test.ByteArray_frompointer(a)
# for the -builtin case it just works though

Здесь нетздесь нет большого выбора для аккуратных интерфейсов, потому что ваши требования довольно ограничительны:

  1. без использования специфичных для lang исправлений.
  2. У меня нет полномочий изменятьAPI.Я просто хочу сделать его простым в использовании.

С учетом этих двух факторов мало что остается открытым для использования.

Лично я предпочитаю писать некоторые специфичные для PythonC, если он делает интерфейс, который пользователи Python видят более естественным, даже если это означает, что тот же интерфейс не может быть дублирован на 100% на другом языке.Здесь есть гораздо более аккуратные решения для небольшой дополнительной языковой работы, которая обычно окупается.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...