当使用boost :: python时,将python.io对象转换为std :: istream [英] Converting python.io object to std::istream when using boost::python

查看:135
本文介绍了当使用boost :: python时,将python.io对象转换为std :: istream的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在编写我的第一个django应用程序时,我遇到了以下问题: boost :: python 。从python代码,我需要传递 io.BytesIO 到C ++类,它采用 std :: istream



我有一个旧的C ++库用于读取某种格式的文件。让我们来调用 somelib 。此库的接口使用 std :: istream 作为输入。这样的东西:

  class SomeReader 
{
public:
bool read_from_stream(std: :istream&);
};

并且我想包装它,以便我可以使用我的lib从python以下面的方式:

  reader = somelib.SomeReader()
print>> Pyhton:从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
{
//
//我们如何实现?
//
throw std :: runtime_error(不是文件,不知道如何阅读这个!
}
}
};


BOOST_PYTHON_MODULE(somelib)
{
class_< SomeReaderWrap,boost :: noncopyable>(SomeReader)
.def(read & SomeReaderWrap :: read);
}

是否有一种或多或少的通用方法将python IO对象转换为C ++



提前感谢。






我的实验结果我创建了一个小 github repo ,说明了此问题。

解决方案

而不是转换Python io.BytesIO 对象,请考虑实现Boost.IOStreams的模型 io中读取的.boost.org / doc / libs / 1_55_0 / libs / iostreams / doc / concepts / source.htmlrel =nofollow> Source 概念.BytesIO object。这将允许您构建 boost :: iostreams :: stream ,并且可以通过 SomeReader :: read_from_stream()



教程演示如何创建和使用自定义Boost.IOStream源。总的来说,这个过程应该相当简单。只需要根据 read() #io.BufferedIOBase.readrel =nofollow> io.BufferedIOBase.read()



实现Boost.IOStream的Source概念的类型,用于从支持读取(大小)的Python对象中读取
///数据

class PythonInputDevice
:public boost :: iostreams :: source //使用方便类。
{
public:

explicit
PythonInputDevice(boost :: python :: object object)
:object_(object)
{}

std :: streamsize read(char_type * buffer,std :: streamsize buffer_size)
{
namespace python = boost :: python;
//通过Python对象的API读取数据。以下是
//等价于:
// data = object_.read(buffer_size)
boost :: python :: object py_data = object_.attr(read)(buffer_size );
std :: string data = python :: extract< std :: string>(py_data);

//如果字符串为空,则已到达EOF。
if(data.empty())
{
return -1; //指示每个源概念的序列结束。
}

//否则,将数据复制到缓冲区中。
copy(data.begin(),data.end(),buffer);
return data.size();
}

private:
boost :: python :: object object_;
};

然后创建一个 boost :: iostreams :: stream 使用源设备:

  boost :: iostreams :: stream< PythonInputDevice> input(py_object); 
SomeReader读者;
reader.read_from_stream(input);

As PythonInputDevice code> object.read(),鸭式打字允许 PythonInputDevice 用于支持具有相同前置和后置条件的 read()方法的任何Python对象。这包括内置的Python 文件 对象,使得它不再需要基于 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():<内容<< std :: endl;
return true;
}
};

///实现Boost.IOStream源概念模型的类型
///用于从支持以下内容的Python对象中读取数据:
/// data = object。读(大小)。
class PythonInputDevice
:public boost :: iostreams :: source //使用方便类。
{
public:

explicit
PythonInputDevice(boost :: python :: object object)
:object_(object)
{}

std :: streamsize read(char_type * buffer,std :: streamsize buffer_size)
{
namespace python = boost :: python;
//通过Python对象的API读取数据。以下是
//等价于:
// data = object_.read(buffer_size)
boost :: python :: object py_data = object_.attr(read)(buffer_size );
std :: string data = python :: extract< std :: string>(py_data);

//如果字符串为空,则已到达EOF。
if(data.empty())
{
return -1; //指示每个源概念的序列结束。
}

//否则,将数据复制到缓冲区中。
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>输入(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)
;
}

互动用法:



< pre class =lang-python prettyprint-override> $ echo -ntest file> test_file
$ python
>>> import example
>>>如下所示:open('test_file')as f:
... reader = example.SomeReader()
... reader.read(f)
...
SomeReader: :read_from_stream():test file
True
>>> import io
>>>>与io.BytesIO(Hello Stack Overflow)as f:
... reaader = example.SomeReader()
... reader.read(f)
...
SomeReader :: read_from_stream():Hello Stack Overflow
True


While writing my first django application I've faced the following problem with boost::python. From python code, I need to pass io.BytesIO to the C++ class which takes std::istream.

I have a legacy C++ library for reading files of certain format. Let's call is somelib. The interface of this library uses std::istream as an input. Something like this:

class SomeReader
{
public:
    bool read_from_stream(std::istream&);
};

And I want to wrap it, so that I can use my lib from python in the following way:

reader = somelib.SomeReader()
print ">>Pyhton: reading from BytesIO"
buf = io.BytesIO("Hello Stack Overflow")
reader.read(buf)

I found out how to do it for actual python file object. But it is not clear how to do it for arbitrary file-like object. This is definition of the python bindings I have so far:

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);
}

Is there a more or less generic way of converting python IO object to the C++ stream?

Thank you in advance.


As a result of my experiments I've created a small github repo which illustrates this problem.

解决方案

Instead of converting the Python io.BytesIO object, consider implementing a model of the Boost.IOStreams Source concept that is capable of reading from a Python io.BytesIO object. This would allow one to construct a boost::iostreams::stream and be useable by SomeReader::read_from_stream().

This tutorial demonstrates how to create and use a custom Boost.IOStream Source. Overall, the process should be fairly straight forward. One just needs to implement the Source concept's read() function in terms of io.BufferedIOBase.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_;
};

then create a boost::iostreams::stream using the Source device:

boost::iostreams::stream<PythonInputDevice> input(py_object);
SomeReader reader;
reader.read_from_stream(input);

As PythonInputDevice is implemented in terms of object.read(), duck typing allows PythonInputDevice to be used with any Python object supporting a read() method that has the same pre and post-conditions. This include's the built-in Python file object, making it no longer necessary to have conditional branching based on type within SomeReaderWrap::read().


Here is a complete minimal example based on the original code:

#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)
    ;
}

Interactive usage:

$ 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.io对象转换为std :: istream的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆