如何在Cython中的新型缓冲区对象中包装C指针和长度?


问题内容

我在Cython中编写Python 2.7扩展模块。 如何创建实现新型缓冲区接口的Python对象,该接口包装由C库提供给我的内存块?
内存块只是一串字节,而不是结构或多维数组。我得到了一个const void *指针和一个长度,以及一些有关指针保持有效时间的详细信息。

我无法复制内存,这会降低应用程序的性能。

使用旧样式的缓冲区对象,我可以简单地使用PyBuffer_FromMemory(),但是我似乎找不到找到类似的简单方法来生成新样式的缓冲区对象。

我是否必须创建自己的实现缓冲区接口的类?还是Cython提供了一种简便的方法?

我已经从Cython文档中阅读了Unicode和Passing
Strings
Typed
Memoryviews
页面,但是该文档不够精确,也不是很完整,并且没有类似于我想要的示例的示例。

这是我尝试过的(test.pyx):

from libc.stdlib cimport malloc
from libc.string cimport memcpy

## pretend that this function is in some C library and that it does
## something interesting.  (this function is unrelated to the problem
## I'm experiencing -- this is just an example function that returns a
## chunk of memory that I want to wrap in an object that follows the
## new buffer protocol.)
cdef void dummy_function(const void **p, size_t *l):
    cdef void *tmp = malloc(17)
    memcpy(tmp, "some test\0 bytes", 17)
    p[0] = tmp
    l[0] = 17

cpdef getbuf():
    cdef const void *cstr
    cdef size_t l
    dummy_function(&cstr, &l)

    ## error: test.pyx:21:20: Invalid base type for memoryview slice: void
    #cdef const void[:] ret = cstr[:l]

    ## error: test.pyx:24:9: Assignment to const 'ret'
    #cdef const char[:] ret = cstr[:l]

    ## error: test.pyx:27:27: Cannot convert 'void const *' to memoryviewslice
    #cdef char[:] ret = cstr[:l]

    ## this next attempt cythonizes, but raises an exception:
    ## $ python -c 'import test; test.getbuf()'
    ## Traceback (most recent call last):
    ##   File "<string>", line 1, in <module>
    ##   File "test.pyx", line 15, in test.getbuf (test.c:1411)
    ##   File "test.pyx", line 38, in test.getbuf (test.c:1350)
    ##   File "stringsource", line 614, in View.MemoryView.memoryview_cwrapper (test.c:6763)
    ##   File "stringsource", line 321, in View.MemoryView.memoryview.__cinit__ (test.c:3309)
    ## BufferError: Object is not writable.
    cdef char[:] ret = (<const char *>cstr)[:l]

    ## this raises the same exception as above
    #cdef char[:] ret = (<char *>cstr)[:l]

    return ret

问题答案:

正如@RichardHansen在他的自我回答中正确观察到的那样,您想要的是一个实现缓冲区协议的类,并具有管理内存的合适析构函数。

Cython实际上以的形式提供了一个内置的轻量级类,cython.view.array因此无需创建自己的类。它实际上记录在您链接的页面中,但是为了提供适合您情况的快速示例:

# at the top of your file
from cython.view cimport array

# ...

# after the call to dummy_function
my_array = array(shape=(l,), itemsize=sizeof(char), format='b',  # or capital B depending on if it's signed
                 allocate_buffer=False)
my_array.data = cstr
my_array.callback_free_data = free

cdef char[:] ret = my_array

只是为了提醒您注意以下几点:allocate_buffer设置为,False因为您要在中分配自己的位置cstr。设置callback_free_data可确保使用标准库free功能。