加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Python > 正文

如何使用Cython将Python列表传递给C函数

发布时间:2020-12-20 13:47:17 所属栏目:Python 来源:网络整理
导读:我正在使用Raspberry Pi与连接到GPIO的自定义硬件进行交互.控制软件是用 Python编写的,自定义硬件的接口是用C语言编写的,因为它是一个更快的C实现.我现在需要从我的Python开始调用我的C函数,并且最近一直在学习如何在Cython中包装C.除了将Python列表传递给C
我正在使用Raspberry Pi与连接到GPIO的自定义硬件进行交互.控制软件是用 Python编写的,自定义硬件的接口是用C语言编写的,因为它是一个更快的C实现.我现在需要从我的Python开始调用我的C函数,并且最近一直在学习如何在Cython中包装C.除了将Python列表传递给C函数之外,我已经完成了所有工作.

我的自定义硬件需要从1到32个字节发送,因此使用数组.

我在线阅读的Cython教程和其他参考文献都非常简单,不包括如何将列表传递给C,使用numpy,我没有使用,或使用非常复杂的代码示例,缺乏足够的文档供我理解正常.

我现在拥有的是:

test.c的

#include <stdio.h>
#include "test.h"
void pop(void) {
    a[0] = 0x55;
    a[1] = 0x66;
    a[2] = 0x77;
    a[3] = '';
}
void putAll(int n,char c[]) {
    memcpy(a,c,n);
}
char *getAll(void) {
    return &a[0];
}

test.h

char a[4];

void putAll(int n,char[]);
char *getAll(void);

pytest.pyx

cimport defns

# Populate C array with values
def pypop():
    defns.pop()

# Pass python list to C
def pyPutAll(int n,char[:] pyc):
    cdef char* c = pyc
    defns.putAll(n,c)

# Get array from C
def pyGetAll():
    cdef char* c = defns.getAll()
    cdef bytes pyc = c
    print pyc

defns.pxd

cdef extern from "test.h":
    char a[4]
    void pop()
    void putAll(int n,char c[])
    char *getAll()

使用cython.org中的教程,我的getAll()和pop()函数可以工作,但是当我包含putAll()函数时(取自在链接中找到的process_byte_data示例代码,在Unicode下传递字符串>从Python代码接受字符串) ),我收到此错误:

python setup.py build_ext -i

Error compiling Cython file:
------------------------------------------------------------
...

def pyputAll(int n,char[:] pyc):
                        ^
------------------------------------------------------------

pytest.pyx:13:25: Expected an identifier or literal

现在,我有办法解决这个问题 – 将最多32个字节组合成一个int并传递为一个long int,然后在C中将它拉开 – 但它非常难看.

此外,除了使用C实现的库与我的自定义硬件与Python实现的硬件接口之外,我不需要Cython来获得任何性能提升.

任何帮助将不胜感激.

(编辑)解决方案

我设法让这个工作.以下是我现在为需要它的人提供的代码.

pytest.pyx

...
def pyPutAll(int n,c):
    cdef int *ptr
    ptr = <int *>malloc(n*cython.sizeof(int))
    if ptr is NULL:
            raise MemoryError()
    for i in xrange(n):
            ptr[i] = c[i]
    defns.putAll(n,ptr)
    free(ptr)
...

test.c的

void putAll(int n,int c[])
{
    char d[n];
    int i;
    for (i=0;i<n;i++) {
            d[i] = c[i];
    }
    memcpy(addr,d,n);
}

这段代码不是最优的,因为它在python / cython代码中使用了int,然后在C函数中将它转换为char. pytest.pyc中的pyPutAll()函数接受一个普通的python列表.然后它创建一个C指针并分配内存.迭代列表,将每个值放入C数组,最后将指针传递给C函数.

它完成了工作,但我确信其他人可以提供更有效的解决方案.

马特

解决方法

ctypes更适合您要做的事情.

例如:(test.py)

from ctypes import create_string_buffer,c_char_p,c_int,CDLL

libtest = CDLL("./libtest.so")

_putAll = libtest.putAll
_putAll.restype = None
_putAll.argtypes = [c_int,c_char_p]

def putAll(values):
    """Expects a bytes object,bytearray,or a list of ints."""
    char_buffer = create_string_buffer(bytes(values))
    _putAll(len(char_buffer),char_buffer)

getAll = libtest.getAll
getAll.restype = c_char_p
getAll.argtypes = None

用法:

import test
test.putAll(b"hello world")
assert test.getAll() == b"hello world"
test.putAll(bytearray(b"another string"))
test.putAll([1,2,3,255])

以上是针对python 3的.如果您正在运行python 2,那么您可以将字节替换为str,但该函数的灵活性较低.另外,请注意create_string_buffer创建一个C字符串(在字符串的末尾添加一个额外的NUL字符).

要编译共享库,您需要执行以下操作:

gcc -fPIC -g -c -Wall test.c
gcc -shared -Wl,-soname,libtest.so.1 -o libtest.so test.o

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读