【问题标题】:Python callback from C++来自 C++ 的 Python 回调
【发布时间】:2014-03-14 14:18:43
【问题描述】:

我有一个 C++ 类

class EventHandler {
virtual long readFromDaemon(void *buf, size_t count) = 0;
};

以及使用它的 Python 程序

class Handler(EventHandler):
    def readFromDaemon(self, buf, count):
    ...

C++ 代码调用 EventHandler::readFromDaemon()。 SWIG 转换参数并调用 Python 的 readFromDaemon():

long SwigDirector_EventHandler::readFromDaemon(void *buf, size_t count) {
  ...
  swig::SwigVar_PyObject obj0;
  obj0 = SWIG_NewPointerObj(SWIG_as_voidptr(buf), SWIGTYPE_p_void,  0 );

  swig::SwigVar_PyObject obj1;
  obj1 = SWIG_From_size_t(static_cast< size_t >(count));
  ...
  swig::SwigVar_PyObject swig_method_name = SWIG_Python_str_FromChar((char *)"readFromDaemon");
  swig::SwigVar_PyObject result = PyObject_CallMethodObjArgs(swig_get_self(), (PyObject *) swig_method_name ,(PyObject *)obj0,(PyObject *)obj1, NULL);

我想将 (void *buf, size_t count) 转换为 PyBytesObject

def readFromDaemon(self, buf):
    # buf is bytes

但我没有找到实现它的方法。

%typemap(in) (void *buf, size_t count) { ... } does not help.

【问题讨论】:

    标签: python c++ swig


    【解决方案1】:

    这是一个读取和写入 C++ 数据缓冲区的示例。我的示例中的 C++ 函数是:

    void read(void* buf, size_t count);
    void write(const void* buf, size_t count);
    

    SWIG 接口文件:

    %module x
    
    %include <exception.i>
    
    // Handle an input-only (const) buffer.
    // A single Python input is translated into a buffer and a size.
    %typemap(in) (const void *buf, size_t count) (Py_ssize_t tmp) %{
        if(PyBytes_AsStringAndSize($input,(char**)&$1,&tmp) == -1)
            return NULL;
        $2 = tmp;
    %}
    
    // Handle an output-only (non-const) buffer.
    // The single integer Python input is allocated in a temporary
    // buffer and the pointer and its size are created as C++ parameters.
    %typemap(in,numinputs=1) (void *buf, size_t count) (long tmp) %{
        if(!PyLong_Check($input))
            SWIG_exception(SWIG_TypeError,"expected integer");
        tmp = PyLong_AsLong($input);
        if(tmp < 1 || tmp > 65535)
            SWIG_exception(SWIG_ValueError,"expected value 1-65535");
        $2 = tmp;
        $1 = new char[$2];
    %}
    
    // The pair of output arguments are translated into a single
    // Python bytes object and appended to any existing return value.
    %typemap(argout) (void *buf, size_t count) (PyObject* po) %{
        po = PyBytes_FromStringAndSize((char*)$1,$2);
        $result = SWIG_Python_AppendOutput($result,po);
    %}
    
    // Free the temporary buffer created in the "in" typemap.
    %typemap(freearg) (void* buf, size_t count) %{
        delete $1;
    %}
    
    // SWIG will wrap these two functions prototypes.
    void read(void* buf, size_t count);
    void write(const void* buf, size_t count);
    
    // Implementation
    %{
    #include <iostream>
    void write(const void* buf, size_t count)
    {
        char* tmp = (char*)buf;
        for(size_t i = 0; i < count; i++)
            std::cout << i << ": " << (int)tmp[i] << std::endl;
    }
    
    void read(const void* buf, size_t count)
    {
        char* tmp = (char*)buf;
        for(size_t i = 0; i < count; i++)
            tmp[i] = (char)i;
    }
    %}
    

    演示:

    >>> import x
    >>> x.read(10)
    b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t'
    >>> x.read(-1)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ValueError: expected value 1-65535
    >>> x.read(1000000)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ValueError: expected value 1-65535
    >>> x.write(b'abcdefg')
    0: 97
    1: 98
    2: 99
    3: 100
    4: 101
    5: 102
    6: 103
    >>> x.write(12)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: expected bytes, int found
    

    【讨论】:

    • 谢谢!我为 (void* buf, size_t count) 尝试了 %typemap(in) 和 %typemap(in) ,它们对于独立函数工作正常。但问题是 swig 没有将此类型映射应用于导演类,我没有找到如何强制它这样做的方法。注意 SwigDirector_EventHandler::readFromDaemon(void *buf, size_t count)
    • 您能否将您的问题简化为一个说明问题的小示例?我不知道您尝试了什么,您的问题中没有 .i 文件。
    • 感谢您的帮助,我找到了问题的答案,但又出现了新问题。请阅读我的新帖子。
    【解决方案2】:

    我在这里找到了另一个主题的决定。

    %typemap(directorin) (void *buf, size_t count) {
        $input = PyBytes_FromStringAndSize(static_cast<char*>($1), $2);
    }
    

    但是还有另一个问题。内存缓冲区从 C++ 复制到 Python 对象。如何将字节从 python 对象复制回 C++? python 2.7以后就有memoryview了,但是2.6及更早的版本怎么办?

    为了清楚起见,这是调用 EventHandler::readFromDaemon 的 C++ 代码

    std::vector<char> buf(maxlen);
    long cnt = m_evt_handler->readFromDaemon(&buf[0], buf.size());
    

    这里是包装代码

    long SwigDirector_EventHandler::readFromDaemon(void *buf, size_t count) {
      long c_result;
      swig::SwigVar_PyObject obj0;
      {
        obj0 = PyBytes_FromStringAndSize(static_cast<char*>(buf), count);
      }
      ...
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-12-04
      • 1970-01-01
      • 2018-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多