Stupid C Tricks: Portably Passing Function Pointers via (void*)

March 2, 2010 on 5:15 pm | In General |

Hello, fellow C programmers!

(If you don’t program in C (or even know what that means) then the following won’t interest you in the slightest. If you are a C programmer, it might interest you in the slightest.)

It is common for C code to use the venerable void pointer, and (despite what our OO teachings might tell us), the void pointer isn’t the bad guy. Mis-use of void pointers is the bad guy. Because there are lots of ways to abuse a void pointer, void pointers are, at least in higher-level APIs, generally to be avoided. Lower-level APIs often cannot get by without them.

Today’s talk is not about void pointers in general (there’s an awful lot to say about void!), but specifically about using a void pointer to hold a function pointer.

Did you know that passing a function pointer via a void pointer, casting that void pointer to the original function pointer, and dereferencing (i.e. calling) the casted function results in undefined behaviour? That’s what the C standard says.

What does that mean? It means the following code is invokes undefined behavhiour in any country or providence which respects the C Standard:

typedef int (*my_callback)( void * state );
my_callback cb = some_callback_function;
void * ptr = cb;
((my_callback)ptr)( ... ); // <--- undefined behaviour!

The above code is rather contrived, but it demonstrates a feature which is seen from time to time in ancient C code. It is also used in some OS-level functions which open DLLs (e.g. based on dlopen() on POSIX systems). DLL-loading systems often look for a symbol with a specific name within the DLL right after they open it. If they find it, they treat it like a function and call it. Some DLL-loading systems provide a similar feature for closing a DLL. Higher-level “plugin” systems, common in today’s applications, often use this approach to fetch a factory function from the DLL, which is then used to create an instance of the plugin.

If such systems expect their magic symbol(s) to be a function (and in my experience they do!), then they are relying on undefined behaviour. And we abhor undefined behaviour, don’t we?

There is a portable, and only slightly inconvenient, workaround. In short, we add a level of indirection between the function we want to fetch from the DLL and the symbol name the DLL-opener expects to find. That level of indirection is a tiny struct:

typedef int (*my_callback)( void * state );
typedef struct my_callback_struct {
    my_callback callback;
} my_callback_struct;

And in our DLLs we use that indirection like so:

/* implements the my_callback() interface. */
static int private_callback( void * state );
/* The symbol our DLL/plugin system will look for: */
const my_callback_struct TheExpectedSymbolName =  {private_callback};

In the DLL opening code, we look for “TheExpectedSymbolName”, but instead of casting it to a function pointer, we cast it to a my_callback_struct pointer. From that object, we can invoke its member function without invoking undefined behaviour. Obviously, if the DLL contains a non-my_callback_struct with the expected symbol name, behaviour is undefined, but the function-pointer approach has the same problem.

This approach can easily be expanded to provide open() and close() routines for plugins, or any other application-specific plugin functionality. It can be combined with macros to allow clients to easily implement a plugin by calling the macro and passing pointers to their callback implementations. Since the DLL lookup symbols must unique, however, it means we have a limitation of one logical plugin per DLL file. (That is the norm in most plugin systems, but this limitation makes it an explicitproperty of such systems.)

In C++ (as opposed to C), the ability to to construct objects and call functions during the DLL’s static initialization phase (i.e. while it’s being opened, before the DLL opener gets it back) allows for an elegant solution to all of the above-mentioned problems and limitations. We won’t go into that here, but for interested readers there is a detailed article about it, called Classloading in C++, over at http://wanderinghorse.net/computing/papers/.

Happing hacking!

—– stephan beal, 2 March 2010

1 Comment »

RSS feed for comments on this post. TrackBack URI

Leave a comment

XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>


HINT: the code is G G N (without spaces!)

Powered by WordPress with Pool theme design by Borja Fernandez.
Entries and comments feeds. Valid XHTML and CSS. ^Top^