如何在Cython中的新型缓冲区对象中包装C指针和长度? [英] How to wrap a C pointer and length in a new-style buffer object in Cython?

查看:86
本文介绍了如何在Cython中的新型缓冲区对象中包装C指针和长度?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用Cython编写Python 2.7扩展模块. 我如何创建一个实现新式缓冲区接口的Python对象,该接口包装由C库提供给我的内存块?多维数组.我给了一个const void *指针和一个长度,以及一些有关指针保持有效时间的详细信息.

I'm writing a Python 2.7 extension module in Cython. How do I create a Python object implementing the new-style buffer interface that wraps a chunk of memory given to me by a C library? The chunk of memory is just a string of bytes, not a structure or multidimensional array. I'm given a const void * pointer and a length, and some details about how long the pointer stays valid.

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

I can't copy the memory—that would kill performance for my application.

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

With the old-style buffer objects I could simply use PyBuffer_FromMemory(), but I can't seem to find a similarly easy way to produce a new-style buffer object.

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

Do I have to create my own class that implements the buffer interface? Or does Cython provide an easy way to do this?

我已阅读 Unicode和传递字符串Typed Memoryviews"页面页面,但该文档不够精确且不够完整,没有任何示例看起来与我想要的相似

I've read the Unicode and Passing Strings and Typed Memoryviews pages from the Cython documentation, but the documentation is imprecise and not very complete and there are no examples that look similar to what I want to do.

这是我尝试过的(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正确回答的那样,您想要的是一个实现缓冲区协议的类,并具有管理内存的合适析构函数.

As @RichardHansen correctly observes in his self-answer, what you want is a class that implements the buffer protocol, and has a suitable destructor that manages the memory.

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

Cython actually provides a fairly lightweight class built into it in the form of cython.view.array so there's no need to create your own. It's actually documented in the page you linked but for the sake of providing a quick example that fits your case:

# 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功能.

Just to draw attention to a couple of bits: allocate_buffer is set to False since you're allocating your own in cstr. Setting callback_free_data ensures that the standard library free function is used.

这篇关于如何在Cython中的新型缓冲区对象中包装C指针和长度?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆