gpt4 book ai didi

c++ - 使用 boost::python 时将 python.io 对象转换为 std::ostream

转载 作者:行者123 更新时间:2023-11-28 06:40:40 25 4
gpt4 key购买 nike

我看过有关将 python.io 对象转换为 std::istream 的答案。无论如何,这可以使用 boost::iostream::sink 为 std::ostream 实现吗?在我的例子中,我有一个 C++ 函数

void writeToStream(std::ostream&)

如何将此函数公开给 python?

最佳答案

正如在 this 中所做的那样回答,应该考虑实现一种设备类型,供 Boost.IOStream 使用来执行委托(delegate),而不是转换为 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.
}
};

这是一个基本类型,它对 Sink 和 Flushable 概念进行建模,并使用 duck typing 委托(delegate)给 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

关于c++ - 使用 boost::python 时将 python.io 对象转换为 std::ostream,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26033781/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com