简体   繁体   English

如何在python3中使用ctypes导入ostringstream?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM