2015-01-20 100 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]) 
相關問題