Reputation: 2323
I have a C extension I want to call from Python. The C extension takes a pointer to an array of pointers and three pointers to arrays of int64_t values. I have called this C extension using a wrapper written in C and here is how I do it in C (in relevant part):
int64_t ptr_array_out[3];
int64_t *ptr_array_ptr = ptr_array_out;
ptr_array_out[0] = (int64_t)file_ptr1;
ptr_array_out[1] = (int64_t)file_ptr2;
int64_t type_array_out[3];
int64_t *ptr_type_array_ptr = type_array_out;
type_array_out[0] = 4; // double_precision
type_array_out[1] = 4; // double_precision
int64_t length_array_out[3];
int64_t *length_array_ptr = length_array_out;
length_array_out[0] = file_info[0];
length_array_out[1] = file_info[1];
int64_t s_data_out[3];
int64_t *s_array_ptr = s_data_out;
s_data_out[0] = 300
Now I am replicating it in ctypes. The only difference is that the first array (ptr_array_ptr) passes an array of pointers to arrays in memory instead of file pointers. Here is what I have done so far:
ptr_array_ptr = (ctypes.c_double * len(q))(*q), (ctypes.c_double * len(z))(*z)
ptr_type_array_ptr = (ctypes.c_longlong(4)),(ctypes.c_longlong(4))
length_array_ptr = (ctypes.c_longlong(len(q))), (ctypes.c_longlong(len(z)))
s_array_ptr = (ctypes.c_longlong(maxval1)), (ctypes.c_longlong(maxval2))
hShObj = ctypes.CDLL("(pathto shared obj)/SharedObjName.so")
CallName = hShObj.Main_Entry_fn
CallName.argtypes = [ctypes.POINTER(ctypes.c_double),ctypes.POINTER(ctypes.c_longlong),ctypes.POINTER(ctypes.c_longlong),ctypes.POINTER(ctypes.c_longlong)]
CallName.restype = ctypes.POINTER(ctypes.c_int64)
ret_ptr = CallName(ptr_array_ptr, ptr_type_array_ptr, length_array_ptr, s_array_ptr)
But I get this error from Python:
ctypes.ArgumentError: argument 1: <class 'TypeError'>: expected LP_c_double instance instead of tuple
I have researched this and found several answers on Stack Overflow and elsewhere in various contexts, but nothing that addresses this specific situation. I’m not a ctypes novice but I haven’t worked with it in several years so I’m not sure how to solve this.
Thanks very much for any help with this.
Upvotes: 1
Views: 840
Reputation: 41137
Listing [Python.Docs]: ctypes - A foreign function library for Python.
You're creating tuples (due to commas (,)) for the 4 elements. Check [Python.Docs]: Data Structures - Tuples and Sequences.
A fix would be to create a 2D array. Unfortunately, I am not aware of an elegant initialization method, so I used 2 (plain old) nested for loops:
>>> import ctypes as ct >>> >>> >>> q = [1, 2, 3] >>> z = [4, 5, 6, 7] >>> >>> ptr_array_ptr = (ct.c_double * len(q))(*q), (ct.c_double * len(z))(*z) # @TODO - cfati: this is a tuple >>> ptr_array_ptr (<__main__.c_double_Array_3 object at 0x000001444D9A7EC0>, <__main__.c_double_Array_4 object at 0x000001444D9C2340>) >>> type(ptr_array_ptr) <class 'tuple'> >>> [type(e) for e in ptr_array_ptr] [<class '__main__.c_double_Array_3'>, <class '__main__.c_double_Array_4'>] >>> >>> ptr_array_ptr[0][0], ptr_array_ptr[1][0] (1.0, 4.0) >>> >>> # Start over >>> lists = (q, z) >>> max_len = max(len(e) for e in lists) >>> max_len 4 >>> Arr = (ct.c_double * max_len) * len(lists) # Array type >>> Arr <class '__main__.c_double_Array_4_Array_2'> >>> >>> arr = Arr() # array >>> arr <__main__.c_double_Array_4_Array_2 object at 0x000001444D9C22C0> >>> arr[0][0], arr[1][0], arr[0][2], arr[1][2], arr[0][3], arr[1][3] (0.0, 0.0, 0.0, 0.0, 0.0, 0.0) >>> >>> for i0, l in enumerate(lists): ... for i1, c in enumerate(l): ... arr[i0][i1] = c ... >>> >>> arr[0][0], arr[1][0], arr[0][2], arr[1][2], arr[0][3], arr[1][3] (1.0, 4.0, 3.0, 6.0, 0.0, 7.0)
Upvotes: 1