Pybind11/Nanobind中std::istream&参数的Python适配问题
问题
我正在用Pybind11/Nanobind给C库编写Python绑定,其中一个C函数接收std::istream &类型参数,代码示例如下:
std::string cPXGStreamReader::testReadStream(std::istream &stream) { std::ostringstream contentStream; std::string line; while (std::getline(stream, line)) { contentStream << line << '\n'; // 将行追加到内容字符串 } return contentStream.str(); // 将contentStream转为字符串并返回 }
请问Python中需要传入什么类型的参数才能匹配?
我试过传入以下方式创建的s:
s = open(r"test_file.pxgf", "rb") # 以及 s = io.BytesIO(b"some initial binary data: \x00\x01")
但都无效,报错信息如下:
TypeError: test_read_file(): incompatible function arguments. The following argument types are supported: 1. (self: pxgf.PXGStreamReader, arg0: std::basic_istream<char,std::char_traits<char> >) -> str Invoked with: <pxgf.PXGStreamReader object at 0x000002986CF9C6B0>, <_io.BytesIO object at 0x000002986CF92250> Did you forget to `#include <pybind11/stl.h>`? Or <pybind11/complex.h>, <pybind11/functional.h>, <pybind11/chrono.h>, etc. Some automatic
解决方案
Pybind11/Nanobind不会自动将Python文件对象(包括BytesIO)映射到std::istream,得手动做适配或修改绑定逻辑:
先确认引入必要头文件
绑定代码里必须包含<pybind11/stl.h>,它提供STL类型的基础绑定,但仅靠这个还不够支持Python文件对象转std::istream。自定义适配类实现映射
写一个std::streambuf的子类,把Python的类文件对象(支持read方法)包装起来,再用它构造std::istream。示例代码:#include <pybind11/pybind11.h> #include <pybind11/stl.h> #include <iostream> #include <streambuf> #include <string> namespace py = pybind11; class PyStreamBuf : public std::streambuf { private: py::object py_file; char buffer[1024]; protected: int underflow() override { if (gptr() < egptr()) { return traits_type::to_int_type(*gptr()); } py::bytes data = py::cast<py::bytes>(py_file.attr("read")(sizeof(buffer))); char* data_ptr = PYBIND11_BYTES_AS_STRING(data.ptr()); ssize_t data_len = PYBIND11_BYTES_SIZE(data.ptr()); if (data_len == 0) { return traits_type::eof(); } setg(buffer, buffer, buffer + data_len); memcpy(buffer, data_ptr, data_len); return traits_type::to_int_type(*gptr()); } public: explicit PyStreamBuf(py::object file) : py_file(std::move(file)) {} }; std::string testReadStreamWrapper(cPXGStreamReader& self, py::object py_file) { PyStreamBuf buf(py_file); std::istream stream(&buf); return self.testReadStream(stream); } PYBIND11_MODULE(pxgf, m) { py::class_<cPXGStreamReader>(m, "PXGStreamReader") .def(py::init<>()) .def("test_read_file", &testReadStreamWrapper); // 用包装函数绑定,替代原函数 }简化方案:文本模式传参
不想写适配类的话,可以修改绑定逻辑,把Python传入的文本模式文件对象直接读取为字符串,再构造std::istringstream传给原函数:std::string testReadStreamWrapper(cPXGStreamReader& self, py::object input) { std::string content; if (py::isinstance<py::str>(input)) { content = py::cast<std::string>(input); } else if (input.attr("read").is()) { content = py::cast<std::string>(input.attr("read")()); } else { throw py::type_error("参数应为字符串或类文件对象"); } std::istringstream stream(content); return self.testReadStream(stream); }这种情况下Python端可以这么传:
# 文本模式打开文件 s = open(r"test_file.pxgf", "r") # 或者直接传字符串 s = "测试文本内容"
内容的提问来源于stack exchange,提问作者Tommy Wolfheart
相关产品推荐
相关产品推荐

