RTC222
RTC222

Reputation: 2323

Ctypes “expected LP_c_double instance instead of tuple”

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

Answers (1)

CristiFati
CristiFati

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

Related Questions