我见过将python.io对象转换为std :: istream的相关答案。无论如何,这可以通过boost :: iostream :: sink用于std :: ostream吗? 在我的情况下,我有一个C ++函数
void writeToStream(std :: ostream&)
如何将此函数公开给python?
答案 0 :(得分:1)
正如在this回答中所做的那样,应该考虑实现Boost.IOStream使用的设备类型来执行委派而不是转换为std::ostream
。在这种情况下,Boost.IOStream的Device概念需要支持Sink概念。它还可以通过对Flushable概念建模来扩展以支持其他功能,例如刷新缓冲区。
概念指定了类型必须提供的内容。在Sink概念的情况下,模型可以定义如下:
struct Sink
{
typedef char char_type;
typedef sink_tag category;
std::streamsize write(const char* s, std::streamsize n)
{
// Write up to n characters from the buffer
// s to the output sequence, returning the
// number of characters written
}
};
Flushable概念在文档中不那么直接,可以在检查flush()
函数语义时确定。在这种情况下,模型可以定义如下:
struct Flushable
{
typedef flushable_tag category;
bool flush()
{
// Send all buffered characters downstream. On error,
// throw an exception. Otherwise, return true.
}
};
这是一个基本类型,它使用duck typing对Sink和Flushable概念进行建模并委托给Python对象。 python对象是:
write(str)
方法返回None
或写入的字节数。flush()
方法。/// @brief Type that implements the Boost.IOStream's Sink and Flushable
/// concept for writing data to Python object that support:
/// n = object.write(str) # n = None or bytes written
/// object.flush() # if object.flush exists, then it is callable
class PythonOutputDevice
{
public:
// This class models both the Sink and Flushable concepts.
struct category
: boost::iostreams::sink_tag,
boost::iostreams::flushable_tag
{};
explicit
PythonOutputDevice(boost::python::object object)
: object_(object)
{}
// Sink concept.
public:
typedef char char_type;
std::streamsize write(const char* buffer, std::streamsize buffer_size)
{
namespace python = boost::python;
// Copy the buffer to a python string.
python::str data(buffer, buffer_size);
// Invoke write on the python object, passing in the data. The following
// is equivalent to:
// n = object_.write(data)
python::extract<std::streamsize> bytes_written(
object_.attr("write")(data));
// Per the Sink concept, return the number of bytes written. If the
// Python return value provides a numeric result, then use it. Otherwise,
// such as the case of a File object, use the buffer_size.
return bytes_written.check()
? bytes_written
: buffer_size;
}
// Flushable concept.
public:
bool flush()
{
// If flush exists, then call it.
boost::python::object flush = object_.attr("flush");
if (!flush.is_none())
{
flush();
}
// Always return true. If an error occurs, an exception should be thrown.
return true;
}
private:
boost::python::object object_;
};
请注意,为了支持多个概念,创建了一个嵌套的category
结构,它继承了模型实现的多个类别标记。
使用Boost.IOStream设备,最后一步是公开一个辅助函数,该函数将使用Python对象创建流,然后调用现有的writeToStream()
函数。
/// @brief Use an auxiliary function to adapt the legacy function.
void aux_writeToStream(boost::python::object object)
{
// Create an ostream that delegates to the python object.
boost::iostreams::stream<PythonOutputDevice> output(object);
writeToStream(output);
};
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::def("writeToStream", &aux_writeToStream);
}
这是一个完整的最小例子:
#include <iosfwd> // std::streamsize
#include <iostream>
#include <boost/python.hpp>
#include <boost/iostreams/concepts.hpp> // boost::iostreams::sink
#include <boost/iostreams/stream.hpp>
/// @brief Legacy function.
void writeToStream(std::ostream& output)
{
output << "Hello World";
output.flush();
}
/// @brief Type that implements the Boost.IOStream's Sink and Flushable
/// concept for writing data to Python object that support:
/// n = object.write(str) # n = None or bytes written
/// object.flush() # if flush exists, then it is callable
class PythonOutputDevice
{
public:
// This class models both the Sink and Flushable concepts.
struct category
: boost::iostreams::sink_tag,
boost::iostreams::flushable_tag
{};
explicit
PythonOutputDevice(boost::python::object object)
: object_(object)
{}
// Sink concept.
public:
typedef char char_type;
std::streamsize write(const char* buffer, std::streamsize buffer_size)
{
namespace python = boost::python;
// Copy the buffer to a python string.
python::str data(buffer, buffer_size);
// Invoke write on the python object, passing in the data. The following
// is equivalent to:
// n = object_.write(data)
python::extract<std::streamsize> bytes_written(
object_.attr("write")(data));
// Per the Sink concept, return the number of bytes written. If the
// Python return value provides a numeric result, then use it. Otherwise,
// such as the case of a File object, use the buffer_size.
return bytes_written.check()
? bytes_written
: buffer_size;
}
// Flushable concept.
public:
bool flush()
{
// If flush exists, then call it.
boost::python::object flush = object_.attr("flush");
if (!flush.is_none())
{
flush();
}
// Always return true. If an error occurs, an exception should be thrown.
return true;
}
private:
boost::python::object object_;
};
/// @brief Use an auxiliary function to adapt the legacy function.
void aux_writeToStream(boost::python::object object)
{
// Create an ostream that delegates to the python object.
boost::iostreams::stream<PythonOutputDevice> output(object);
writeToStream(output);
};
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::def("writeToStream", &aux_writeToStream);
}
交互式使用:
>>> import example
>>> import io
>>> with io.BytesIO() as f:
... example.writeToStream(f)
... print f.getvalue()
...
Hello World
>>> with open('test_file', 'w') as f:
... example.writeToStream(f)
...
>>>
$ cat test_file
Hello World