Home > database >  Ctypes array access variables changed by C
Ctypes array access variables changed by C

Time:09-08

I have a function in C which is integrated into python as a library. The python looks something like this:

import ctypes
import numpy as np
lib=ctypes.cdll.LoadLibrary("./array.so")
lib.eq.argtypes=(ctypes.c_int,
                 ctypes.POINTER(ctypes.c_float),  
                 ctypes.POINTER(ctypes.c_float))
params = parameters.ctypes.data_as(ctypes.POINTER(ctypes.c_float))
results = np.zeros(5).ctypes.data_as(ctypes.POINTER(ctypes.c_float))
lib.get_results(ctypes.c_int(5),params,results)

And the C code in array.c looks something like:

void get_results(int size, float params[], float results[5])
{
   'do some calculations which use params'
   results[0] = ...
   results[1] = ...
   results[2] = ...
   results[3] = ...
   results[4] = ...
}

How would one access the array(pointer) results in python? I couldn't figure it out since the void function in C doesn't actually return any value.

CodePudding user response:

You were close. The input arrays need to be dtypes=np.float32 (or ct.c_float) to match the C 32-bit float parameters. results is changed in-place so print the updated results after the function call.

You can also use ndpointer to declare the exact type of arrays expected, so ctypes can check the the correct array type is passed. In your original code np.zeros(5) defaults to np.float64 type.

test.c

#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif

API void get_results(int size, float params[], float results[5])
{
    // Make some kind of calculation
    for(int i = 0; i < size;   i) {
        results[0]  = params[i];
        results[1]  = params[i] * 2;
        results[2]  = params[i] * 3;
        results[3]  = params[i] * 4;
        results[4]  = params[i] * 5;
    }
}

test.py

import ctypes as ct
import numpy as np

lib = ct.CDLL('./test')
# Since you are using numpy, we can use ndpointer to declare
# the exact type of numpy array expected.  In this case,
# params is a one-dimensional array of any size, and
# results is a one-dimensional array of exactly 5 elements.
# Both need to be of np.float32 type to match C float.
lib.get_results.argtypes = (ct.c_int,
                            np.ctypeslib.ndpointer(dtype=np.float32, ndim=1),
                            np.ctypeslib.ndpointer(dtype=np.float32, shape=(5,)))
lib.get_results.restype = None


params = np.array([1,2,3], dtype=np.float32)
results = np.zeros(5, dtype=np.float32)
lib.get_results(len(params), params, results)
print(results)

Output:

[ 6. 12. 18. 24. 30.]

CodePudding user response:

My suggestion:

import ctypes
import numpy as np
from os.path import abspath
from ctypes import cdll, c_void_p, c_int

params = np.ascontiguousarray(np.zeros(5, dtype=np.float64))
results = np.ascontiguousarray(np.zeros(5, dtype=np.float64))

lib = cdll.LoadLibrary(abspath('array.so'))      # loading the compiled binary shared C library, which should be located in the same directory as this Python script; absolute path is more important for Linux — not necessary for MacOS
f = lib.get_results       # assigning the C interface function to this Python variable "f"
f.arguments = [ c_int, c_void_p, c_void_p ]         # declaring the data types for C function arguments
f.restype = c_int      # declaring the data types for C function return value
res = f(params.size, c_void_p(params.ctypes.data), c_void_p(results.ctypes.data))   # calling the C interface function
print(res)
print(results)
int get_results(int size, double * params, double * results)
{
   /* do some calculations which use params */
   results[0] = ...;
   results[1] = ...;
   results[2] = ...;
   results[3] = ...;
   results[4] = ...;
   return whatever;
}
  • Related