Search code examples
pythoncpython-c-apipython-extensions

Python Function Capsules


I found this code snippet in the help of python 2.7.5, which is a chapter about exposing a C-API to other modules in the Extending Python with C and C++ section: Providing a C API for an Extension Module

/* C API functions */
#define PySpam_System_NUM 0
#define PySpam_System_RETURN int
#define PySpam_System_PROTO (const char *command)
// ...
static PySpam_System_RETURN PySpam_System PySpam_System_PROTO;
// ...
static void **PySpam_API;

#define PySpam_System \
(*(PySpam_System_RETURN (*)PySpam_System_PROTO) PySpam_API[PySpam_System_NUM])

This snipped is for function capsules. A capsule is used to pass functions between two modules. But what's the meaning of this snippet: [...] (PySpam_SystemRETURN (*)PySpam_System_PROTO) [...]. I think it's something like a static cast. Something like (int(*)(char s)). But what's the meaning of this construct?


Solution

  • As defined, the macro PySpam_System expands into:

    (*(int (*)(const char *command)) PySpam_API[0])
    

    Which is basically accessing PySpam_API[0], casting it to a pointer to function receiving a const char * and returning int, and dereferencing that pointer.

    It is equivalent to writing:

    int (*)(const char *command) function_ptr = (int (*)(const char *command)) PySpam_API[0]
    #define PySpam_System (*function_ptr)
    

    That is, it is equivalent to declaring a variable function_ptr which is a pointer to the same function pointed to by PySpam_API[0] casted to int (*)(const char *), and then using PySpam_System as a shortcut to dereference the pointer, which means that PySpam_System can be used as if it were a function, as in:

    PySpam_System("an example");
    

    This effectively calls the function pointed to by PySpam_API[0] with argument "an example". Note that the function must be compatible with the cast.

    Also, notice that the code defines a function called PySpam_System before defining the macro PySpam_System. This has the effect that if you use PySpam_System() before the #define, you will be calling this function:

    static PySpam_System_RETURN PySpam_System PySpam_System_PROTO;
    

    (Which expands to static int PySpam_System(const char *command);)

    If you use PySpam_System() after the #define, you will be calling a macro that calls the function pointed to by PySpam_API[0].