SWIG声明以char*/size_为输出参数的类型映射

2024-10-01 15:43:13 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个C API,它使用字符串的输出参数。(为了保护无辜,真正的签名已经改变了。这是一个简单的例子。)

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

bufbuflen是有效的输出参数,其中buflenbuf2len是这些缓冲区(已经)分配的大小。你知道吗

在调用代码中,我不想传入任何参数。相反,我希望返回字符串。你知道吗

result1,result2 = func()

我不希望将buffer/size传递给包装器函数,而是让包装器分配它,将其转换为Python字符串,并在返回Python字符串之前释放它。你知道吗

我看到的大多数与此相关的cstring.i类型映射都要求我给包装器函数一个字符串。allocate类型映射都需要一个char**。你知道吗

我正在寻找类似于使用OUTPUT作为输出参数名称的行为,但是缓冲区/大小对是(单个)输出参数。你知道吗

我没有能力修改API。我只是想让它简单易用。你知道吗

这已经有一个typemap了,或者你能帮我建一个吗?你知道吗

试用版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的修复程序。你知道吗


Tags: 函数字符串api类型参数sizebuffer缓冲区
1条回答
网友
1楼 · 发布于 2024-10-01 15:43:13

既然您特别要求的解决方案不需要任何特定语言的支持,我建议使用%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 buf1buf2数组做更多的工作,以使Python用户更自然地使用它,但这可以通过使用结构上的carrays.i或%extend来完成,以便将其全部保存在C/SWIG中,而不是特定于Python。你知道吗


您还可以使用多参数类型映射来执行与正在包装的函数无关的操作。为了避免特定于Python,可以使用%append_output从一个函数返回多个项。不过,这不适用于Java或C等静态类型的语言,但应该适用于大多数/所有动态类型的语言。你知道吗

为了进一步避免需要额外的、特定于语言的代码,我们可以使用carrays.i,它为我们定义的每种类型生成一些额外的函数。特别地,我们使用ByteArray_castnew_ByteArraydelete_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);
}
%}

在我的测试中,这与python3.7的预期效果一样。请注意,您需要使用-builtin参数运行swig来获得您在这里要查找的确切行为,或者需要额外的用户代码来解决此问题:

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。我只是想让它简单易用。你知道吗

考虑到这两个因素,没有多少可以使用。你知道吗

尽管我个人倾向于编写一些特定于Python的C,如果它能让Python用户看到的界面更自然,即使这意味着同一个界面不能100%地用另一种语言复制。这里有很多更简洁的解决方案,只需要一点额外的语言特定的工作,这通常是有回报的。你知道吗

我认为SWIG的功能并不是“一次编写,随时随地导入”,而是帮助您抽象和模块化直观界面中特定于语言的部分。你知道吗

相关问题 更多 >

    热门问题