1

The definition of my C function is -

foo(int64_t* total_length,
const int8_t* fromtags,
int64_t fromtagsoffset,
const int32_t* fromindex,
int64_t fromindexoffset,
int64_t length,
int64_t** offsetsraws,
int64_t* offsetsoffsets)

I can load it via ctypes in Python as -

import ctypes
lib = ctypes.CDLL("....")
name = "foo"
funcC = getattr(lib, name)

I can set the types of arguments it takes -

funcC.argtypes = (ctypes.POINTER(ctypes.c_int64), ctypes.POINTER(ctypes.c_int8), ctypes.c_int64, ctypes.POINTER(ctypes.c_int32), ctypes.c_int64, ctypes.c_int64, ctypes.POINTER(ctypes.POINTER(ctypes.c_int64)), ctypes.POINTER(ctypes.c_int64))

I assign the values that are to be passed to funcC like -

temparr = [0]*30
total_length = ((ctypes.c_int64)*30)(*temparr)
temparr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
fromtags = ((ctypes.c_int8)*len(temparr))(*temparr)
fromtagsoffset = 1
temparr = [1, 2, 6, 6, 3, 7, 3, 8, 3, 8, 8]
fromindex = ((ctypes.c_int32)*len(temparr))(*temparr)
fromindexoffset = 0
length = 3
temparr = [0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
offsetsoffsets = ((ctypes.c_int64)*len(temparr))(*temparr)

For the argument with type pointer to pointer, I assign the values like -

temparr = [1, 2, 1, 2, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]
offsetsraws = ctypes.pointer((ctypes.c_int64 * len(temparr))(*temparr))

Then I create a list with the arguments -

testsc = [total_length, fromtags, fromtagsoffset, fromindex, fromindexoffset, length, offsetsraws, offsetsoffsets]

I finally pass the arguments to funcC -

funcC(*testsc)

but I get an error -

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ctypes.ArgumentError: argument 7: <class 'TypeError'>: expected LP_LP_c_long instance instead of LP_c_long_Array_16

How should I fix this error?

Is it because I am making a mistake when assigning the offsetsraws argtype as ctypes.POINTER(ctypes.POINTER(ctypes.c_int64))?

I found a similar question - Python and ctypes: how to correctly pass "pointer-to-pointer" into DLL? but the difference is that in that question, the pointer to pointer being passed does not have an assigned value during input and more importantly, is not represented as a 2D array.

Pratyush Das
  • 460
  • 7
  • 24

1 Answers1

1

Listing [Python 3.Docs]: ctypes - A foreign function library for Python.

If you have a pointer to an unidimensional array (~90%+ of the cases), a simple cast from int64[] to int64* will do.
If however, you have a 2 dimensional array, things get more complicated, as you have to handle each individual unidimensional array (row) separately (e.g. [SO]: How can i cast a double pointer ctype to numpy array? (@CristiFati's answer) (and other URLs it references)).

Here's an example for the former case.

>>> import ctypes as ct
>>>
>>>
>>> gtc = ct.windll.kernel32.GetTickCount  # This function does NOT take arguments. I used it for DEMO PURPOSES ONLY !!! Basically, it's Undefined Behavior.
>>> gtc.argtypes = [ct.POINTER(ct.POINTER(ct.c_int64))]
>>>
>>> arr = [1, 2, 1, 2, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]
>>>
>>> arr_ct = ct.pointer((ct.c_int64 * len(arr))(*arr))  # Original variant
>>>
>>> gtc(arr_ct)  # Will throw exception
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ctypes.ArgumentError: argument 1: <class 'TypeError'>: expected LP_LP_c_longlong instance instead of LP_c_longlong_Array_16
>>>
>>> arr_ct = ct.pointer(ct.cast((ct.c_int64 * len(arr))(*arr), ct.POINTER(ct.c_int64)))  # cast the (inner) array to pointer
>>>
>>> gtc(arr_ct)
250935781
CristiFati
  • 38,250
  • 9
  • 50
  • 87