我有一个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类型映射都要求我给包装函数提供一个字符串。所有的分配类型图都需要一个char**
。
我正在寻找与使用OUTPUT
作为outparam名称类似的行为,但是缓冲区/大小对是(单个)outparam。
我无权更改API。我只是想使其易于使用。
已经有用于此的类型映射,或者您可以帮助我构造一个吗?
我让它起作用(不测试性能或内存使用情况)。
%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的修补程序来解决此问题。
答案 0 :(得分:1)
由于您特别要求的解决方案不需要特定的语言支持,因此建议您使用%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。请注意,您需要使用-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
这里整洁的界面没有太多选择,因为您的要求非常严格:
考虑到这两个因素,可以使用的东西并不多。
就我个人而言,尽管我倾向于编写一些特定于Python的C,即使它会使Python用户看到的界面更加自然,即使这意味着同一界面不能以另一种语言进行100%复制。对于一些额外的特定于语言的工作,这里有很多更整洁的解决方案,通常会奏效。
我认为SWIG的功能不是“写一次就可以导入到任何地方”,而是可以帮助您抽象化和模块化直观界面的特定于语言的部分。