0

I have the following struct and function declaration in C:

typedef int (*callback)(struct instance *);

typedef struct instance {
    int x;
    callback f;
} instance;

What would be the proper way to define the callback in Python using ctypes?

I'm trying to do declare the struct in Python the following way:

class INSTANCE_STRUCT(ctypes.Structure):
       _fields_ = [("x", c_int),
                   ("f", c_void_p)]

So basically I'm using c_void_p to declare f as a void pointer and would like to cast it to a function.

I'm creating the structure in the C source on the heap with malloc, and then in Python I'm accessing it as follows:

instance = ctypes.cast(pointer_to_structure, ctypes.POINTER(INSTANCE_STRUCT))
print(instance.contents.x)
print(instance.contents.f)

Running the script gives me the following output:

Initializing struct x=[4] with function f() result=[8] // this happens in C and is correct
4 // value of instance.x
140027207110960 // address of instance.f (?)

Now having the address of instance.f() I assume I'd need to cast it somehow to a python method. I tried this:

def CALLBACK_FUNC(self, structure):
     pass

callback = ctypes.cast(instance.contents.f, ctypes.POINTER(CALLBACK_FUNC))

But it just throws the error:

Traceback (most recent call last):
  File "binding_test.py", line 19, in <module>
    callback = ctypes.cast(instance.contents.f, ctypes.POINTER(callback_function))
TypeError: must be a ctypes type

Does anybody know what would be the way to dereference the instance.f() function in Python, considering that the callback function should have the INSTANCE_STRUCT object itself as a parameter?

1 Answer 1

1

Function pointers can use CFUNCTYPE(retval,params...) to declare C function pointers. The code below is a guess based on the description and minimal code:

test.c

#if defined(_WIN32)
#   define API __declspec(dllexport)
#else
#   define API
#endif

#include <stdlib.h>

struct instance; // forward declaration...

typedef int (*callback)(struct instance *); // so callback can be defined...

typedef struct instance { // and structure declared.
    int x;
    callback f;
} instance;

int func(instance* p) {  // Callback
    return ++p->x;
}

API instance* get_instance(void) {
    instance* p = malloc(sizeof(instance));
    p->x = 5;
    p->f = func;
    return p;
}

API void free_instance(instance* p) {
    free(p);
}

test.py

from ctypes import *

# Forward declaration...
class Instance(Structure):
    pass

# so the callback parameter pointer can be declared...
CALLBACK = CFUNCTYPE(c_int,POINTER(Instance))

# and then the fields can be defined.
Instance._fields_ = (('x',c_int),
                     ('f',CALLBACK))

dll = CDLL('./test')
dll.get_instance.argtypes = ()
dll.get_instance.restype = POINTER(Instance)
dll.free_instance.argtypes = POINTER(Instance),
dll.free_instance.restype = None

instance = dll.get_instance()
try:
    print(instance.contents.x)
    print(instance.contents.f(instance))
    print(instance.contents.f(instance))
    print(instance.contents.f(instance))
finally:
    dll.free_instance(instance)

Output:

5
6
7
8

References:

Sign up to request clarification or add additional context in comments.

1 Comment

This is exactly what I was looking for, thank you!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.