Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

call C code from Python via ctypes, use python object list

I want to

  1. write a function sum_list of sum a list in c, named sum_list.c
  2. make the sum_list.c file to sum_list.so
  3. sum_list = ctypes.CDLL('./sum_list.so')
  4. result = sum_list.sum_list([1, 2, 3])

It raise error in step 4:

ctypes.ArgumentError: argument 1: : Don't know how to convert parameter 1

when I write a function in c which add two numbers, it works ok.

so, the point is i don't know how to pass a list (python object) to c.


sum_list.c

#define PY_SSIZE_T_CLEAN
#include <Python.h>

long sum_list(PyObject *list)
{
    Py_ssize_t i, n;
    long total = 0, value;
    PyObject *item;

    n = PyList_Size(list);
    if (n < 0)
        return -1; /* Not a list */
    for (i = 0; i < n; i++) {
        item = PyList_GetItem(list, i); /* Can't fail */
        if (!PyLong_Check(item)) continue; /* Skip non-integers */
        value = PyLong_AsLong(item);
        if (value == -1 && PyErr_Occurred())
            /* Integer too big to fit in a C long, bail out */
            return -1;
        total += value;
    }
    return total;
}

python code

from ctypes import CDLL

sum_list = CDLL('./sum_list.so')

l = [1, 2, 3]
sum_list.sum_list(l)

I expect the var result in my python code is 6.

like image 365
kun Avatar asked Dec 31 '22 22:12

kun


1 Answers

The ctypes library is meant for calling functions in pure C libraries. The example that you found is for a function that would go into a Python extension module, and be usable without ctypes. Ctypes actually has a type for PyObject *, namely ctypes.py_object. You need to wrap the list in this - be careful to ensure that it will stay alive!

In any case you must almost always provide the correct prototype of the function using the restype and argtypes.

Here's a working example:

import ctypes

sum_list = ctypes.PyDLL('./sum_list.so')
sum_list.restype = ctypes.c_long
sum_list.argtypes = [ ctypes.py_object ]

l = [1, 2, 3]
print(sum_list.sum_list(ctypes.py_object(l)))

Note, as noticed by Mark, you must use PyDLL here instead, to ensure that the GIL is not released since the function does not acquire it!