python - SWIG typemap to return an output param char*/size_t
问题描述
I have an C API that uses output params for strings. (The real signature has been changed to protect the innocent. This is a simplistic example.)
void func( char* buf, size_t buflen, char* buf2, size_t buf2len );
buf
and buflen
are effectively output parameters, where buflen
and buf2len
are the (already) allocated size of those buffers.
In the calling code, I don't want to have to pass in any parameters. Rather, I want the strings to be returned.
result1,result2 = func()
I would prefer not to pass the buffer/size to the wrapper function, but rather have it allocated by the wrapper, turned into a Python string, and deallocated before returning the Python string.
Most of the cstring.i typemaps I see related to this require me to give the wrapper function a string. The allocate typemaps all want a char**
.
I'm looking for behavior similar to using OUTPUT
as an outparam name, but the buffer/size pair are the (single) outparam.
I don't have power to alter the API. I just want to make it easy to use.
Is there already a typemap for this, or can you help me construct one?
Trial 1 (Python Only)
I got this to function (no test of performance or memory usage).
%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);
}
I would prefer to solve this problem without resorting to lang-specific fixes.
解决方案
由于您特别要求提供不需要任何特定语言来支持的解决方案,因此我建议您使用它来提供具有您喜欢的语法%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
andbuf2
数组做更多的工作,以使其对 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 的预期一致。请注意,您需要使用-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
这里没有很多整洁接口的选择,因为您的要求非常严格:
- 无需求助于特定于语言的修复程序。
- 我无权更改 API。我只是想让它易于使用。
考虑到这两个,没有多少可以使用。
就个人而言,尽管我倾向于编写一些特定于 Python 的 C,如果它使 Python 用户看到的界面更自然,即使这意味着不能 100% 地用另一种语言复制相同的界面。对于一些额外的语言特定工作,这里有很多更简洁的解决方案,通常会得到回报。
我认为 SWIG 的力量不是“编写一次,随处导入”,而是帮助您抽象和模块化直观界面的语言特定部分。
推荐阅读
- regex - 在字符约束下使用正则表达式缩写名称
- java - android studio 3.6.1 调试找不到局部变量
- c - 复制字符串后 ARM7 Raspberry pi 程序集分段错误
- javascript - 添加元素后将焦点切换到表格
- javascript - 在Javascript中显示特定列的总和
- javascript - Javascript - 有选择地禁用 Key Down
- javascript - 如何手动创建 Git blob 对象,然后使用 node.js 读取内容,就像在 Git 内部的 ProGit 章节中一样?
- css - CSS过渡不透明度在手机上不起作用
- rust - 具有多个生命周期的迭代器
- javascript - 如何使用 Apify 和 Puppeteer 构建成功的网络抓取结果?