2015-01-20 110 views
4

我正在使用Raspberry Pi与连接到GPIO的自定义硬件进行接口。控制软件是用Python编写的,而定制硬件的接口是用C编写的,因为它是一个更快的C实现。我现在需要从我的Python开始调用我的C函数,并且最近一直在学习如何在Cython中打包C。除了将Python列表传递给C函数之外,我已经掌握了所有工作。如何使用Cython将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] = '\0'; 
} 
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和传球为长整型,然后用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代码,然后将其转换在C函数为char 。 pytest.pyc中的pyPutAll()函数接受一个普通的python列表。然后它创建一个C指针并分配内存。遍历列表中的每个值都放入C数组中,然后最终将指针传递给C函数。

它完成了工作,但我相信别人可以提供更有效的解决方案。

马特

+0

除了修改的问题提供解决方案,它的接受*和鼓励*回答你自己的问题,甚至将其标记为公认的答案。 – 2016-06-14 09:18:43

回答

0

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]) 

以上是蟒3。如果你正在运行python 2,那么你可以用bytes代替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 
+1

问:“我如何更换我车内的火花塞?” - 答:“骑自行车,而不是” – 2016-06-13 23:02:18

+1

我不同意。这个问题类似于“我如何用锤子敲钉子”。答案显示了如何实现将字符串传递给C库以及仅使用标准库。 Cython是一个强大的工具,但是它试图达到的目标是过度的。 – Dunes 2016-06-14 09:14:10

+1

但它*不回答OP的问题*。我通过谷歌结束了这里,因为我使用的是Cython,这个答案对我来说毫无用处。对我而言,使用ctypes是完全不可能的。 OP可能已经接受了这个答案,但这甚至不是他最终使用的解决方案。他应该写作并接受他自己的回答。 – 2016-06-14 09:16:44

1

您可以使用Python的字节组与用Cython,我觉得比清洁和更容易的ctypes:

test.py

larr = bytearray([4, 1, 2]) 
pyPutAll(3, larr) 

这适用于您原来的putAll C功能:

test.c的

... 
void putAll(int n, char c[]) { 
    memcpy(a, c, n); 
} 
... 

pytest.pyx

# Pass python bytearray to C 
def pyPutAll(int n, char[:] pyc): 
    defns.putAll(n, &pyc[0]) 

如果你需要传递一个清单,你就必须把它转换到PYX函数中的向量,并传递给一个参考,而不是:

pytest.pyx

def pyPutAllList(int n, list pyc): 
    cdef vector[char] vec = pyc 
    defns.putAll(n, &vec[0])