如何使用用Cython通过Python列表到C函数 [英] How to pass Python list to C function using Cython

查看:1158
本文介绍了如何使用用Cython通过Python列表到C函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用的是树莓派与连接到GPIO定制硬件接口。控制软件是用Python编写,界面的定制硬件是写在C,因为它是一个更快的C实现。我现在需要开始从我的Python调用我的C函数,并且最近已经在学习如何在用Cython包装℃。我已经得到的一切工作,除了传递一个Python列表C函数。

I am using a Raspberry Pi to interface with custom hardware connected to the GPIO. The controlling software is written in Python, and the interface to the custom hardware is written in C, as it is a much faster C implementation. I now need to start calling my C functions from my Python, and have recently been learning how to wrap C in Cython. I have got everything to work, except passing a Python list to a C function.

需要我的定制的硬件可以从1至32字节,因此,使用的阵列的任何地方发送。

My custom hardware needs to be sent anywhere from 1 to 32 bytes, hence the use of an array.

在用Cython教程和其他参考资料我已阅读在线要么是非常简单的,不包括如何列表传递给C,使用numpy的,这我不使用,或使用非常复杂的code示例,缺乏足够的文件对我来说,正确地理解它。

The Cython tutorials and other references I have read online either are really simple, and do not include how to pass lists to C, use numpy, which I am not using, or use very complicated code examples that lack sufficient documentation for me to understand it properly.

我现在是:

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

test.h

char a[4];

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

pytest.pyx

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

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例如code,在统一code和字符串传递采取>在Python code接受字符串),我得到这个错误:

Using the tutorials at cython.org, my getAll() and pop() functions work, but when I include the putAll() function (taken from the process_byte_data example code found at the link, under Unicode and passing strings > Accepting strings from Python code), I get this error:

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拉开它 - 但它是很丑陋

Now, I have a way around this - combining up to 32 bytes into an int and passing as a long int, and then pulling it apart in C - but it is very ugly.

另外,我不需要用Cython任何性能提升,比用C语言实现的库与我的定制硬件接口VS一个Python实现一个其他。

Also, I do not require Cython for any performance gains, other than that of using the C implemented library for interfacing with my custom hardware vs a Python implemented one.

任何帮助将大大AP preciated。

Any help would be greatly appreciated.

(编辑)解决方案

我设法得到这个工作。这里是code现在我的人谁需要它。

I managed to get this working. Here is the code I now have for anyone who needs it.

pytest.pyx

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的

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);
}

这code是不是最佳的,因为它使用整型在Python /用Cython code,然后将其转换的C函数为char。在pytest.pyc的 pyPutAll()函数接受一个普通的Python列表。然后,它创建一个C指针和分配内存。通过列表迭代,每个值放入C数组,然后最后传递指针的C函数。

This code is not optimal, as it uses ints in the python/cython code, then converts it to char in the C function. The pyPutAll() function in pytest.pyc accepts an ordinary python list. It then creates a C pointer and allocates memory. Iterating through the list, each value is put into a C array, and then finally passes the pointer to the C function.

它能够完成任务,但我敢肯定,别人可以给一个更有效的解决方案。

It gets the job done, but I'm sure someone else can give a much more efficient solution.

推荐答案

ctypes的 是更适合你正在尝试做的。

ctypes is better suited to what you are trying to do.

例如:(test.py)

For instance: (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字符)。

The above is for python 3 . If you're running python 2 then you can substitute bytes for str, but the function is less flexible. In addition, be aware that create_string_buffer creates a C-string (adds an additional NUL character on the end of the string).

要编译你需要做下面的共享库:

To compile the shared library you need to do the following:

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

这篇关于如何使用用Cython通过Python列表到C函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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