2014-09-25 60 views
0

我已经看到与将python.io对象转换为std :: istream相关的答案。无论如何,这可以通过使用boost :: iostream :: sink来实现std :: ostream吗? 在我的情况下,我有一个C++函数 使用boost时将python.io对象转换为std :: ostream :: python

void writeToStream(std :: ostream &)

如何将此函数暴露给python?

回答

0

正如在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 
相关问题