Return ctypes.py_object in the callback

I am trying to wrap a C library using ctypes. One of the features of the library is the ondestroy callback, which is called when the handle returned by the library is about to be destroyed.

The callback has a signature:

void cb(f *beingdestroyed);

The API allows you to associate the user void *with f when it is returned by the library. Therefore, I can bind py_object, which is used to transfer it as user data. My plan is to have a field is_validand when the callback starts to retrieve user_data and set that field to false.

My problem is how to start extracting my higher level py_object; I can get user data just like ctypes.void_papplying to ctypes.py_object, but then I only have the Python C API. Can I back up to a high-level object that I can work with by writing user_object.is_valid = 0?

+3
source share
3 answers

To clarify Thomas Heller's answer:

  • The callback protocol must specify c_void_pfor the context parameter
  • Argipts for a library function must point py_objectfor a context parameter
  • This function should be called using py_object(my_python_context_object)
  • Python py_object : cast(context, py_object).value

. C DLL:

// FluffyBunny.c
// Compile on windows with command line
//      cl /Gd /LD FluffyBunny.c
// Result is FluffyBunny.DLL, which exports one function:
//      FluffyBunny() uses __cdecl calling convention.

#include <windows.h>

BOOL APIENTRY DllMain(HMODULE, DWORD, LPVOID) {
  return TRUE;
}

typedef int (*FLUFFYBUNNY_CALLBACK)(void *context);

__declspec(dllexport) int FluffyBunny(FLUFFYBUNNY_CALLBACK cb, void *context) {
  int result = 0;
  int count = 0;
  if (cb) {
    while (result == 0) {
      result = (*cb)(context);
      ++count;
    }
  }
  return count;
}

Python, DLL:

# FluffyBunny.py
from ctypes import *

# Declare a class that will be used for context info in calls to FluffyBunny()
class Rabbit:
    def __init__(self):
        self.count = 0

# FluffyBunny() wants a callback function with the following C prototype:
#     typedef int (*FLUFFYBUNNY_CALLBACK)(void *context);
FLUFFYBUNNY_CALLBACK = CFUNCTYPE(c_int, c_void_p)

# This DLL has been compiled with __cdecl calling convention.
FluffyBunny_dll = CDLL('FluffyBunny.dll')

# Get the function from the library. Its C prototype is:
#     int FluffyBunny(FLUFFYBUNNY_CALLBACK cb, void *context);
# Note that I use "py_object" instead of "c_void_p" for the context argument.
FluffyBunny          = FluffyBunny_dll.FluffyBunny
FluffyBunny.restype  = c_int
FluffyBunny.argtypes = [FLUFFYBUNNY_CALLBACK, py_object]

# Create Python version of the callback function.
def _private_enumerateBunnies(context):
    # Convert the context argument to a py_object, and extract its value.
    # This gives us the original Rabbit object that was passed in to 
    # FluffyBunny().
    furball = cast(context, py_object).value
    # Do something with the context object.
    if furball:
        furball.count += 1
        print 'furball.count =', furball.count
        # Return non-zero as signal that FluffyBunny() should terminate
        return 0 if (furball.count < 10) else -1
    else:
        return -1

# Convert it to a C-callable function.
enumerateBunnies = FLUFFYBUNNY_CALLBACK(_private_enumerateBunnies)

# Try with no context info.
print 'no context info...'
result = FluffyBunny(enumerateBunnies, None)
print 'result=', result

# Give it a Python object as context info.
print 'instance of Rabbit as context info...'
furball = Rabbit()
result = FluffyBunny(enumerateBunnies, py_object(furball))
print 'result=', result
+4

- .

, self user_data.

+1

Bob Pyron, , , , C ctypes-stuff. , _private_enumerateBunnies() python void *. :

FLUFFYBUNNY_CALLBACK = CFUNCTYPE (c_int, py_object)

And then all the C-funky things were hidden in the code that already dealt with it, and it did not extend to users of this API (that is, to callback providers). Of course, then you should pass a python object to it, but this is a pretty small limitation.

I really liked Bob's original answer (once I walked past all the bunnies), so thanks!

+1
source

Source: https://habr.com/ru/post/1754593/


All Articles