如何通过Swig封装的C++函数传递并返回Python bytes对象?
解决Python bytes与Swig封装C++函数的交互问题
我来帮你搞定这个问题!你之前用void*没成功,核心原因有两个:一是Swig默认不知道怎么把Python的bytes转换成C的void*(反之亦然),二是void*只存了内存地址,完全没记录数据的长度,C根本没法正确处理这块数据,甚至还可能因为Python的内存回收导致悬空指针崩溃。
下面是完整的解决方案,一步步来:
1. 修改C++代码:添加数据长度管理
首先得让你的C++类同时保存数据指针和长度,还要处理内存释放,避免内存泄漏:
example.hpp
class MyTestClass { public: // 修改set_data,传入数据指针和长度 void set_data(const void* data, size_t len); // 修改get_data,通过输出参数返回长度,返回数据指针 const void* get_data(size_t& len) const; // 析构函数释放内存 ~MyTestClass(); private: void* data = nullptr; size_t data_len = 0; };
example.cpp
#include "example.hpp" #include <cstring> void MyTestClass::set_data(const void* data, size_t len) { // 先释放旧数据 if (this->data) { delete[] static_cast<char*>(this->data); } // 复制新数据到C++管理的内存里 this->data_len = len; this->data = new char[len]; std::memcpy(this->data, data, len); } const void* MyTestClass::get_data(size_t& len) const { len = this->data_len; return this->data; } MyTestClass::~MyTestClass() { // 析构时释放内存 if (data) { delete[] static_cast<char*>(data); } }
2. 编写Swig接口文件:添加类型转换规则
Swig需要你告诉它怎么在Python bytes和C++的指针+长度之间转换,我们通过typemap来实现:
example.i
%module example %{ #include "example.hpp" %} // 处理Python bytes转C++的(const void*, size_t)输入参数 %typemap(in) (const void* data, size_t len) { // 检查输入是否是bytes类型 if (!PyBytes_Check($input)) { PyErr_SetString(PyExc_TypeError, "参数必须是bytes对象"); return NULL; } // 获取bytes的内容指针和长度 $1 = PyBytes_AsString($input); $2 = PyBytes_Size($input); } // 处理get_data的输出参数len,把返回的指针转成Python bytes %typemap(argout) size_t& len { // 从C++拿到数据和长度,生成Python bytes对象 PyObject* result_bytes = PyBytes_FromStringAndSize( static_cast<const char*>($result), $1 ); // 替换原返回值为bytes $result = result_bytes; } // 让Swig处理我们的类和函数 %include "example.hpp"
3. 编译并测试
编译步骤(以Linux为例)
- 生成Swig包装代码:
swig -python example.i
- 编译C++代码和包装代码:
# 替换成你的Python头文件路径,比如/usr/include/python3.8 g++ -c example.cpp example_wrap.cxx -I/usr/include/python3.x
- 生成共享库:
g++ -shared example.o example_wrap.o -o _example.so
Python测试代码
import example # 创建实例 test_obj = example.MyTestClass() # 传入bytes数据 test_data = b"Hello Swig! This is Python bytes." test_obj.set_data(test_data) # 获取返回的bytes result = test_obj.get_data() print(f"返回的数据:{result}") print(f"数据是否一致:{result == test_data}")
为什么之前的void*方案不行?
- 类型转换缺失:Swig没有默认的规则把Python bytes映射到
void*,反之亦然,必须手动通过typemap定义转换逻辑。 - 数据长度丢失:
void*只记录内存地址,C++不知道要读取多少字节,也就没法正确还原成Python bytes。 - 内存安全问题:如果直接存Python bytes的指针,一旦Python的垃圾回收机制回收了这块内存,C里的指针就变成悬空指针,访问会导致程序崩溃。我们现在的方案是把数据复制到C管理的内存里,彻底避免这个问题。
内容的提问来源于stack exchange,提问作者Jason
相关产品推荐
相关产品推荐

