我已經看到與將python.io對象轉換爲std :: istream相關的答案。無論如何,這可以通過使用boost :: iostream :: sink來實現std :: ostream嗎? 在我的情況下,我有一個C++函數 使用boost時將python.io對象轉換爲std :: ostream :: python
void writeToStream(std :: ostream &)
如何將此函數暴露給python?我已經看到與將python.io對象轉換爲std :: istream相關的答案。無論如何,這可以通過使用boost :: iostream :: sink來實現std :: ostream嗎? 在我的情況下,我有一個C++函數 使用boost時將python.io對象轉換爲std :: ostream :: python
void writeToStream(std :: ostream &)
如何將此函數暴露給python?正如在this答案中所做的那樣,應考慮實施由Boost.IOStream用於執行委派而不是轉換爲std::ostream
的設備類型。在這種情況下,Boost.IOStream的Device概念將需要支持Sink概念。通過對Flushable概念進行建模,它也可以擴展爲支持其他功能,例如沖洗緩衝區。
一個概念指定了一個類型必須提供的內容。
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
}
};
可沖掉的概念是不太直接的文檔中的一個位,並且可在檢查flush()
功能語義確定:在水槽概念的情況下,一種模式可以被定義如下。在這種情況下,該模型可以被定義爲如下:
struct Flushable
{
typedef flushable_tag category;
bool flush()
{
// Send all buffered characters downstream. On error,
// throw an exception. Otherwise, return true.
}
};
下面是一個基本類型的模型的接收器和可沖洗概念和代表使用duck typing 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