使用 swig,如何将二进制数据从 python 传递到 C/C++?

Using swig, how do I pass binary data from python to C/C++?

如果我的 my_module.h 中有这样的 C 或 C++ 函数:

void my_function(const char* data, int len);

和my_module.c:

#include "my_module.h"

#include <unistd.h>

void my_function(const char* data, int len)
{
    // do fancy things with data
    write(1, data, len);
}

我会像这样为 swig 创建一个 my_module.i:

%inline %{
#include "my_module.h"
%}
%include "my_module.h"

我是这样建造的:

swig -python -module my_module my_module.i

gcc -shared -fPIC my_module_wrap.c my_module.c -I/usr/include/python3.8 -lpython3.8 -o _my_module.so

现在 python 我想这样做:

import my_module

in_file = open("my_binary_file", "rb")
bytes = in_file.read()
in_file.close()

my_module.my_function(bytes, len(bytes))

但我得到:

TypeError: in method 'find_mistakes', argument 1 of type 'char const *'

我检查了变量的类型bytes

>>> type(bytes)
<class 'bytes'>

我不知道那是什么意思。我如何才能将原始数据传递给 C?

我不想将字节转换为字符串,因为它不是文本。当我尝试转换它时,C 端收到了对二进制文件而不是原始二进制文件进行编码的文本。看起来像这样的东西:

b'\x\x00\n'

编辑:

阅读 manual 我发现了这个:

The char * datatype is handled as a NULL-terminated ASCII string. SWIG maps this into a 8-bit character string in the target scripting language. SWIG converts character strings in the target language to NULL terminated strings before passing them into C/C++. The default handling of these strings does not allow them to have embedded NULL bytes. Therefore, the char * datatype is not generally suitable for passing binary data. However, it is possible to change this behavior by defining a SWIG typemap. See the chapter on Typemaps for details about this.

所以 swig 说 char* 适用于文本,但不适用于二进制数据。 char* 的替代方案是什么?

它建议使用类型映射更改 swig 的默认行为。这是唯一的方法吗?如果是这样,它是如何完成的?每种语言会有不同的技巧吗?

经过几个小时的挫折,我终于做到了。希望这个答案可以为其他人节省一些时间和挫败感。

请注意,此答案适用于 python 3。我不知道如何使用 python 2.

来自docs

In some cases, users may wish to instead handle all byte strings as bytes objects in Python 3. This can be accomplished by adding SWIG_PYTHON_STRICT_BYTE_CHAR to the generated code:

这意味着您只需要在您的界面文件中:

%begin %{
#define SWIG_PYTHON_STRICT_BYTE_CHAR
%}

This will modify the behavior so that only Python 3 bytes objects will be accepted and converted to a C/C++ string, and any string returned from C/C++ will be converted to a bytes object in Python.

奖金:

如果您不想同时传递二进制文件和来自 python 的长度,您可以在接口文件中执行此操作:

%include "typemaps.i"

// change "(const char* data, int len)" to match your functions declaration
%apply (char *STRING, size_t LENGTH) { (const char* data, int len) }

%include "my_module.h"

现在从 python 开始,您可以这样做:

my_module.my_function(bytes) 而不是 my_module.my_function(bytes, len(bytes))

所以我最终的界面文件是这样的:

%module my_module

%begin %{
#define SWIG_PYTHON_STRICT_BYTE_CHAR
%}

%inline %{
#include "my_module.h"
%}

%include "typemaps.i"

%apply (char *STRING, size_t LENGTH) { (const char* data, int len) }

%include "my_module.h"