2014-09-25 58 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 
相關問題