如何公开C++;使用boost_python将数据序列化到python 我们决定将我们的一个IPC(进程间通信)模块用C++写到Python(我知道,这不是最聪明的想法)。我们使用的数据包可以序列化和反序列化到std::string(行为类似于协议缓冲区,只是效率不高),因此我们的IPC类也可以返回和接受std::string 将类暴露到Python的问题是: STD::String C++类型被转换为 STR Python类型,并且在返回的 STD::String 由不能解码到代码> UTF-8/COD>(大部分时间)的字符中,我得到了 UndoDebug错误Exp>
我设法为这个问题找到了两个解决办法(甚至是“解决方案”),但我对其中任何一个都不是特别满意如何公开C++;使用boost_python将数据序列化到python 我们决定将我们的一个IPC(进程间通信)模块用C++写到Python(我知道,这不是最聪明的想法)。我们使用的数据包可以序列化和反序列化到std::string(行为类似于协议缓冲区,只是效率不高),因此我们的IPC类也可以返回和接受std::string 将类暴露到Python的问题是: STD::String C++类型被转换为 STR Python类型,并且在返回的 STD::String 由不能解码到代码> UTF-8/COD>(大部分时间)的字符中,我得到了 UndoDebug错误Exp>,python,c++,boost-python,Python,C++,Boost Python,我设法为这个问题找到了两个解决办法(甚至是“解决方案”),但我对其中任何一个都不是特别满意 这是我的C++代码,用于复制 UndoDebug错误>代码>问题,并尝试解决: /* * boost::python string problem */ #include <iostream> #include <string> #include <vector> #include <boost/python.hpp> #include <boo
这是我的C++代码,用于复制<代码> UndoDebug错误>代码>问题,并尝试解决:
/*
* boost::python string problem
*/
#include <iostream>
#include <string>
#include <vector>
#include <boost/python.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
struct Packet {
std::string serialize() const {
char buff[sizeof(x_) + sizeof(y_)];
std::memcpy(buff, &x_, sizeof(x_));
std::memcpy(buff + sizeof(x_), &y_, sizeof(y_));
return std::string(buff, sizeof(buff));
}
bool deserialize(const std::string& buff) {
if (buff.size() != sizeof(x_) + sizeof(y_)) {
return false;
}
std::memcpy(&x_, buff.c_str(), sizeof(x_));
std::memcpy(&y_, buff.c_str() + sizeof(x_), sizeof(y_));
return true;
}
// whatever ...
int x_;
float y_;
};
class CommunicationPoint {
public:
std::string read() {
// in my production code I read that std::string from the other communication point of course
Packet p;
p.x_ = 999;
p.y_ = 1234.5678;
return p.serialize();
}
std::vector<uint8_t> readV2() {
Packet p;
p.x_ = 999;
p.y_ = 1234.5678;
std::string buff = p.serialize();
std::vector<uint8_t> result;
std::copy(buff.begin(), buff.end(), std::back_inserter(result));
return result;
}
boost::python::object readV3() {
Packet p;
p.x_ = 999;
p.y_ = 1234.5678;
std::string serialized = p.serialize();
char* buff = new char[serialized.size()]; // here valgrind detects leak
std::copy(serialized.begin(), serialized.end(), buff);
PyObject* py_buf = PyMemoryView_FromMemory(
buff, serialized.size(), PyBUF_READ);
auto retval = boost::python::object(boost::python::handle<>(py_buf));
//delete[] buff; // if I execute delete[] I get garbage in python
return retval;
}
};
BOOST_PYTHON_MODULE(UtfProblem) {
boost::python::class_<std::vector<uint8_t> >("UintVec")
.def(boost::python::vector_indexing_suite<std::vector<uint8_t> >());
boost::python::class_<CommunicationPoint>("CommunicationPoint")
.def("read", &CommunicationPoint::read)
.def("readV2", &CommunicationPoint::readV2)
.def("readV3", &CommunicationPoint::readV3);
}
在第一个解决方法中,不是返回std::string
I而是返回std::vector
。它工作正常,但我不喜欢这样的事实,即它迫使我公开额外的人工python类型UintVec
,该类型没有任何本机对转换为python字节的支持
第二种解决方法很好,因为它将我的序列化数据包公开为一个内存块,并支持转换为字节,但它会泄漏内存。我使用valgrind验证了内存泄漏:valgrind--suppressions=../valgrind-python.supp--leak check=yes-v--log file=valgrindLog.valgrind python3 UtfProblem.py
除了python库中的大量无效读取(可能是误报)之外,它还显示了我
1个块中的8个字节肯定会丢失
在为缓冲区分配内存时的行中。如果我在从函数返回之前删除内存,我将在python中得到一些垃圾
问题:
如何将序列化数据适当地公开给python?用C++来表示字节数组,我们通常使用代码> STD::String 或 const char */Case>,不幸的是,它并没有以良好的方式移植到Python。
如果我的第二个解决方法似乎适合您,那么如何避免内存泄漏
如果将返回值公开为std::string
通常是可以的,那么如何避免出现UnicodeDecodeError
其他信息:
- g++(Debian 6.3.0-18+deb9u1)6.3.0 20170516
- Python 3.5.3
- 升幅1.62
我建议你在C++中定义自己的返回类型类,并使用BooPython来公开它。例如,您可以让它实现缓冲协议。然后,您将有一个规则的C++析构函数,它将在适当的时候被调用——甚至可以使用类内的智能指针来管理分配内存的寿命。
完成后,下一个问题是:为什么不让返回的对象公开属性来访问字段,而不让调用方使用struct.unpack()
?那么您的调用代码就可以简单得多:
result = cp.readV5()
print('intVal: {} floatVal: {}'.format(result.x, result.y))
根据评论,返回pythonicbytes
object(使用pythonapi)效果非常好:
PyObject* read() {
Packet p;
p.x_ = 999;
p.y_ = 1234.5678;
std::string buff = p.serialize();
return PyBytes_FromStringAndSize(buff.c_str(), buff.size());
}
为什么不返回pythonic bytes对象而不是内存视图项?@AntiMatterDynamite,谢谢,它工作得很好,比我预期的简单得多。当我在SO和python文档上寻找解决方案时,每种方法都非常复杂。顺便说一句,为什么有人投了反对票?问题不清楚,没有用?我不能这样做,因为IPC不知道将发送什么类型的对象(只知道它由字节数组表示)。在这个示例中,我忽略了许多细节,实际上在客户端的生产代码中,我可以做:p=SomeSpecificPacket();p、 反序列化(ipc.read())
。当然,只有当read
函数返回字节数组时:);顺便说一句,反物质的评论解决了我的问题。
PyObject* read() {
Packet p;
p.x_ = 999;
p.y_ = 1234.5678;
std::string buff = p.serialize();
return PyBytes_FromStringAndSize(buff.c_str(), buff.size());
}