Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python ctypes sending pointer to structure as parameter to native library

I am trying to write a wrapper to a native library in Linux. Problem is this:

definition in c:

int mymethod(mystruct* ptr)

in python:

_lib.mymethod.argtypes = (ctypes.POINTER(mystruct),)                                                     
_lib.mymethod.restype = ctypes.c_int
s = mystruct()

_lib.mymethod(ctypes.byref(s))
# raises: expected LP_mystruct instance instead of pointer to mystruct

_lib.mymethod(ctypes.pointer(s))
# raises expected LP_mystruct instance instead of LP_mystruct

errors. How to pass a structure as a pointer to a native method ?

Thanks.

Mete

like image 562
mete Avatar asked Feb 03 '12 08:02

mete


2 Answers

The problem is that the higher level "POINTER" from ctypes is, in Python, a different object than "a generic pointer" (ctypes.CArgObject by ctypes.byref)which is returned or a single number representing a memory address (which is what is returned by ctype's adrresof) - you can either annotate your function to receive a `ctypes.c_voidp and call it with _lib.mymethod(ctypes.addressof(a)) instead -

Or if you want to work on the stronged-typed side to avoid errors that would crash Python (a type error raises a Python exception instead - a wrong parameter passed to a C unction would cause a segmentation fault on the Python interpreter itself), you have to create a variable to hold the new "type" which is a POINTER to your structure - and then create an instance of this type with the address of your structure:

mystruct_pointer = ctypes.POINTER(mystruct)
_lib.mymethod.argtypes = (mystruct_pointer,)
_lib.mymethod.restype = ctypes.c_int

s = mystruct()

_lib.mymethod(mystruct_pointer.from_address(ctypes.addressof(s)))
like image 176
jsbueno Avatar answered Nov 14 '22 22:11

jsbueno


(I know that this is an old question, but I think the accepted answer is an unnecessary workaround, so I want to leave this here for posterity.)

Actually ctypes should explicitly support using byref() to pass a pointer like that:

ctypes exports the byref() function which is used to pass parameters by reference. The same effect can be achieved with the pointer() function, although pointer() does a lot more work since it constructs a real pointer object, so it is faster to use byref() if you don’t need the pointer object in Python itself.

The likely cause of this is that you have defined your struct in more than one place (e.g. in different modules) - if the argtypes assignment sees one definition and the function call sees the other, this confusing error arises. In other words, ctypes tries to match two mystruct types that are (probably) identical in contents, and have the exact same name, but they are not the same type. As long as the base struct type is a single type object, it doesn't matter if you construct a pointer to it using pointer(), byref() or POINTER()() - ctypes will detect that the underlying (pointed-to) type is the same.

To verify if this is the case, try assert(_lib.mymethod.argtypes[0]._type_ == type(s)) right before calling the external function.

like image 37
Emil Styrke Avatar answered Nov 14 '22 22:11

Emil Styrke