How to pass a Python list to a C function using Cython

I use Raspberry Pi to interact with custom hardware connected to GPIO. The management software is written in Python and the custom hardware interface is written in C, as this is a much faster implementation of C. Now I need to start calling my C functions from my Python and have recently learned how to wrap C in Cython. I have everything to work with the exception of passing a Python list to a C function.

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

Cython tutorials and other links that I read on the Internet are really simple and do not include how to pass lists to C, use numpy which I don't use, or use very complex code examples that don't have enough documentation for me to understand it is right.

Now I have:

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

Using the tutorials on cython.org , my getAll () and pop () functions work, but when I turn on the putAll () function (taken from the code example process_byte_data found by reference 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

Now I have a way around this - concatenate up to 32 bytes into an int and pass as a long int, and then split it into C, but this is very ugly.

, Cython , C, , Python.

.


()

. , , .

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

, int python/cython, char C. pyPutAll() pytest.pyc python. C- . , C, , , C.

, , - .

Matt

+4
2

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, 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
0

Python bytearray 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])
+1

All Articles