[英]How to import ostringstream using ctypes in python3?
I am writing analyser for natural language and I have a wrapper of c++ code in python 3 created with swig
. 我正在为自然语言编写分析器,并且用
swig
创建了python 3中的c ++代码包装。 I'd like to use a function which is some kind of stream writer and it takes std::ostream & os
as the parameter. 我想使用一个函数,它是某种流编写器,它以
std::ostream & os
作为参数。 So I guess it would work if I somehow import ostringstream
(read as which lib.so
I should use in my ctypes.CDLL
) in my python code then pass it to this function, lest call it create_stream_writer(stream)
, and then use stream.str() to get string. 所以我想如果我以某种方式在我的python代码中导入
ostringstream
(在我的ctypes.CDLL
应该使用的lib.so
,应读为ctypes.CDLL
)然后将其传递给此函数,以免将其称为create_stream_writer(stream)
,然后使用流.str()以获取字符串。 Is it any way to do this using ctypes or any other library? 是否可以使用ctypes或其他任何库来执行此操作? I am using docker container running Ubuntu 18.04, python3.6
我正在使用运行Ubuntu 18.04,python3.6的Docker容器
code should look like this I guess: 我猜代码应该像这样:
def analyse(text, config):
reader = PlainTextReader.create_string_reader(text, config)
stream = ctypes.ostringstream() # some magic hear
writer = TokenWriter.create_stream_writer('plain', stream, reader.tagset())
for sentence in sentences(reader):
writer.write_sentence(sentence)
return stream.str()
You can do this (and make it nice for Python developers too). 您可以执行此操作(也使它对Python开发人员也很好)。 This answer is essentially a Python 3 version of my older answer on wrapping iostreams .
这个答案本质上是我对包装iostream的较早答案的Python 3版本。
To simplify things here I used boost's iostreams library. 为了简化本文,我使用了boost的iostreams库。 If you can't/don't use boost then you can write this all from standard C++ library components, it's just far more verbose.
如果您不能/不使用boost,那么您可以使用标准C ++库组件编写所有内容,只是冗长得多。
I've also aimed higher than mapping io.StringIO
to std::stringstream
and instead gone for mapping any 'file like' Python object to any iostream
. 我的目标还不只是将
io.StringIO
映射到std::stringstream
,而是将任何“类似于文件”的Python对象映射到任何iostream
。 That is to say we use aim to use duck typing on the Python object to just call read()
and write()
sensibly as and when needed for our C++ stream objects. 这就是说,我们的目标是在Python对象上使用鸭子类型,以便在C ++流对象需要时适当地调用
read()
和write()
。
%module test
%{
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/categories.hpp>
// This is just a helper that we can use with boost iostreams to proxy everything to a Python object
struct python_stream_device {
typedef char char_type;
typedef boost::iostreams::bidirectional_device_tag category;
std::streamsize read(char* s, std::streamsize n) {
PyObject *p = PyObject_CallMethod(o, "read", "l", static_cast<long int>(n));
if (PyErr_Occurred()) {
// TODO: throw a C++ exception to back out of wherever we are and then re-throw the Python one...
assert(false);
}
assert(p);
char *ptr = nullptr;
Py_ssize_t len = 0;
PyObject *str = PyUnicode_AsUTF8String(p);
PyBytes_AsStringAndSize(str, &ptr, &len);
if (PyErr_Occurred()) {
assert(false); // Let's just pretend this is error handlng...
}
memcpy(s, ptr, len);
Py_DECREF(str);
Py_DECREF(p);
return len;
}
std::streamsize write(const char* s, std::streamsize n) {
PyObject *ret = PyObject_CallMethod(o, "write", "s#", s, static_cast<Py_ssize_t>(n));
if (PyErr_Occurred()) {
// See above
assert(false);
}
std::streamsize r = PyLong_AsSsize_t(ret);
Py_DECREF(ret);
return r;
}
// Using this means we can rely on the default synthesised operator= + copy ctor etc. and saves us some code.
swig::SwigPtr_PyObject o;
python_stream_device(PyObject *o) : o(o) {}
};
typedef boost::iostreams::stream<python_stream_device> python_stream;
%}
// Here is the stuff that wraps it neatly
%typemap(in) std::iostream& (python_stream tmp) {
// Writing the typemap this way lets us get RAII semantics despite the goto in the SWIG macros in the simplest way
tmp.open(python_stream_device($input));
$1 = &tmp;
}
// We can just use the same typemaps for other cases too:
%apply std::iostream& { std::istream&, std::ostream& };
// Below is just for testing:
%{
#include <iostream>
%}
%inline %{
// This is the function you want to call
void fun1(std::ostream& out) {
assert(out.good());
out << "Hello world, from C++";
assert(out.good());
}
// This one is here for completeness because once you've got this far you may as well support this too.
void fun2(std::istream& in) {
std::string tmp;
//in >> tmp;
std::getline(in, tmp);
assert(in.good());
std::cout << "fun2 got: " << tmp << std::endl;
}
%}
This is enough that you can then use some Python like this: 这样就足够了,然后您可以使用如下所示的Python:
import io
import test
i=io.StringIO()
test.fun1(i)
print('After fun1: %s' % i.getvalue())
i=io.StringIO('hello world, from Python!\n')
test.fun2(i)
As Mark Tolonen pointed in comments it is impossible to do this with ctypes. 正如Mark Tolonen在评论中指出的那样,使用ctypes不可能做到这一点。 So I just wrote c++ function that do everything I need, and then created a wrapper using SWIG.
因此,我只编写了满足我所有需求的c ++函数,然后使用SWIG创建了包装器。 Because using typemap in SWIG to map StringIO(Python) to ostreingstream(C++) looks like black magic and I couldn't find the way to do this.
因为在SWIG中使用typemap将StringIO(Python)映射到ostreingstream(C ++)看起来像是魔术,我找不到实现此目的的方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.