使用boost :: python时,将python.io对象转换为std :: istream
作者:互联网
在编写我的第一个django应用程序时,我遇到了boost :: python的以下问题.从python代码,我需要将io.BytesIO传递给采用std :: istream的C类.
我有一个遗留的C库,用于读取某种格式的文件.我们打电话是somelib.该库的接口使用std :: istream作为输入.像这样的东西:
class SomeReader
{
public:
bool read_from_stream(std::istream&);
};
我想包装它,以便我可以通过以下方式使用python中的lib:
reader = somelib.SomeReader()
print ">>Pyhton: reading from BytesIO"
buf = io.BytesIO("Hello Stack Overflow")
reader.read(buf)
我发现了如何为实际的python文件对象做到这一点.但目前尚不清楚如何为任意文件类对象做这件事.这是我到目前为止的python绑定的定义:
using namespace boost::python;
namespace io = boost::iostreams;
struct SomeReaderWrap: SomeReader, wrapper<SomeReader>
{
bool read(object &py_file)
{
if (PyFile_Check(py_file.ptr()))
{
FILE* handle = PyFile_AsFile(py_file.ptr());
io::stream_buffer<io::file_descriptor_source> fpstream (fileno(handle), io::never_close_handle);
std::istream in(&fpstream);
return this->read_from_stream(in);
}
else
{
//
// How do we implement this???
//
throw std::runtime_error("Not a file, have no idea how to read this!");
}
}
};
BOOST_PYTHON_MODULE(somelib)
{
class_<SomeReaderWrap, boost::noncopyable>("SomeReader")
.def("read", &SomeReaderWrap::read);
}
有没有或多或少的通用方法将python IO对象转换为C流?
先感谢您.
由于我的实验,我创建了一个小型的github repo来说明这个问题.
解决方法:
不要转换Python io.BytesIO
对象,而应考虑实现Boost.IOStreams Source概念的模型,该模型能够从Python io.BytesIO对象中读取.这将允许构造一个boost::iostreams::stream
并且可以被SomeReader :: read_from_stream()使用.
此tutorial演示了如何创建和使用自定义Boost.IOStream源.总的来说,这个过程应该是相当直接的.只需要在io.BufferedIOBase.read()
中实现Source概念的read()函数:
/// Type that implements the Boost.IOStream's Source concept for reading
/// data from a Python object supporting read(size).
class PythonInputDevice
: public boost::iostreams::source // Use convenience class.
{
public:
explicit
PythonInputDevice(boost::python::object object)
: object_(object)
{}
std::streamsize read(char_type* buffer, std::streamsize buffer_size)
{
namespace python = boost::python;
// Read data through the Python object's API. The following is
// is equivalent to:
// data = object_.read(buffer_size)
boost::python::object py_data = object_.attr("read")(buffer_size);
std::string data = python::extract<std::string>(py_data);
// If the string is empty, then EOF has been reached.
if (data.empty())
{
return -1; // Indicate end-of-sequence, per Source concept.
}
// Otherwise, copy data into the buffer.
copy(data.begin(), data.end(), buffer);
return data.size();
}
private:
boost::python::object object_;
};
然后使用Source设备创建一个boost :: iostreams :: stream:
boost::iostreams::stream<PythonInputDevice> input(py_object);
SomeReader reader;
reader.read_from_stream(input);
由于PythonInputDevice是根据object.read()实现的,因此duck typing允许PythonInputDevice与任何支持read()方法的Python对象一起使用,该方法具有相同的前置条件和后置条件.这包括内置的Python file
对象,因此不再需要在SomeReaderWrap :: read()中基于类型进行条件分支.
这是一个基于原始代码的完整最小示例:
#include <algorithm> // std::copy
#include <iosfwd> // std::streamsize
#include <iostream>
#include <boost/python.hpp>
#include <boost/iostreams/concepts.hpp> // boost::iostreams::source
#include <boost/iostreams/stream.hpp>
class SomeReader
{
public:
bool read_from_stream(std::istream& input)
{
std::string content(std::istreambuf_iterator<char>(input.rdbuf()),
(std::istreambuf_iterator<char>()));
std::cout << "SomeReader::read_from_stream(): " << content << std::endl;
return true;
}
};
/// Type that implements a model of the Boost.IOStream's Source concept
/// for reading data from a Python object supporting:
/// data = object.read(size).
class PythonInputDevice
: public boost::iostreams::source // Use convenience class.
{
public:
explicit
PythonInputDevice(boost::python::object object)
: object_(object)
{}
std::streamsize read(char_type* buffer, std::streamsize buffer_size)
{
namespace python = boost::python;
// Read data through the Python object's API. The following is
// is equivalent to:
// data = object_.read(buffer_size)
boost::python::object py_data = object_.attr("read")(buffer_size);
std::string data = python::extract<std::string>(py_data);
// If the string is empty, then EOF has been reached.
if (data.empty())
{
return -1; // Indicate end-of-sequence, per Source concept.
}
// Otherwise, copy data into the buffer.
copy(data.begin(), data.end(), buffer);
return data.size();
}
private:
boost::python::object object_;
};
struct SomeReaderWrap
: SomeReader,
boost::python::wrapper<SomeReader>
{
bool read(boost::python::object& object)
{
boost::iostreams::stream<PythonInputDevice> input(object);
return this->read_from_stream(input);
}
};
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<SomeReaderWrap, boost::noncopyable>("SomeReader")
.def("read", &SomeReaderWrap::read)
;
}
互动用法:
$echo -n "test file" > test_file
$python
>>> import example
>>> with open('test_file') as f:
... reader = example.SomeReader()
... reader.read(f)
...
SomeReader::read_from_stream(): test file
True
>>> import io
>>> with io.BytesIO("Hello Stack Overflow") as f:
... reaader = example.SomeReader()
... reader.read(f)
...
SomeReader::read_from_stream(): Hello Stack Overflow
True
标签:boost-python,python,c,io 来源: https://codeday.me/bug/20190830/1770204.html