Consider this simple C source code which computes the mean of an array of int
, stores it in a structure and returns an error code:
#include <stdio.h>enum errors { NO_ERRORS, EMPTY_ARRAY};struct Result { double mean;};enum errors calculateMean(struct Result *result, int *array, int length) { if (length == 0) { return EMPTY_ARRAY; // Return EMPTY_ARRAY if the array is empty } int sum = 0; for (int i = 0; i < length; i++) { sum += array[i]; } result->mean = (double)sum / length; return NO_ERRORS; // Return NO_ERRORS if calculation is successful}
The code is compiled to a shared library named libtest_ctypes.so
.
I have the following ctypes interface:
import ctypesimport enumimport numpy as npimport enumlib_path = "./libtest_ctypes.so" lib = ctypes.CDLL(lib_path)# The Result structureclass Result(ctypes.Structure): _fields_ = [("mean", ctypes.c_double)]# The Errors enumclass Errors(enum.IntEnum): NO_ERRORS = 0 EMPTY_ARRAY = 1# Defining a signature for `calculateMean`calculate_mean = lib.calculateMeancalculate_mean.argtypes = [ctypes.POINTER(Result), ctypes.POINTER(ctypes.c_int), ctypes.c_int]calculate_mean.restype = Errors# Runs `calculateMean`def calculate_mean_interface(array): result = Result() length = len(array) c_array = array.ctypes.data_as(ctypes.POINTER(ctypes.c_int)) error = calculate_mean(ctypes.byref(result), c_array, length) return error, resultif __name__ == "__main__": array = np.array([1, 2, 3, 4, 5]) error, result = calculate_mean_interface(array) if error == Errors.EMPTY_ARRAY: print("Error: Empty array!") elif error == Errors.NO_ERRORS: print("Mean:", result.mean)
Running the python interface gives a wrong result 1.2
.To my undestanding this is due to a difference in types between the numpy array (64 bit integeres) and the C's int
on my machine.I can get the right result, 3.0
, casting the array to ctype.c_int
through numpy's .astype()
:
def calculate_mean_interface(array): result = Result() length = len(array) #** CAST ARRAY TO CTYPES.C_INT** array = array.astype(ctypes.c_int) c_array = array.ctypes.data_as(ctypes.POINTER(ctypes.c_int)) error = calculate_mean(ctypes.byref(result), c_array, length) return error, result
However numpy's casting requires extra memory and time.What is the best way to achieve a correct result without casting?I'd like this to be portable and, if possible, I don't want to specify a dtype when initializing the numpy array.