如何公开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))
根据评论,返回pythonic
bytes
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());
}