I need to register a table of function pointers with some API. Let's say the API looks like this:
void (*FuncPtr)(void*);
void RegisterCallbacks(FuncPtr const (&callbacks)[10], void* context);
Since I'm writing OOP code, I create static "thunk" methods in my class that in turn call methods on an object of my class. I use the context
pointer to pass the pointer to my object. I want to statically allocate the entire table of callback pointers at static initialization time. So I end up with this:
class MyClass
{
public:
static void Thunk (void* context)
{
reinterpret_cast<MyClass*>(context)->Method();
}
void Method ();
//
// More callback thunks and methods...
//
MyClass ()
{
RegisterCallbacks(s_callbackTable, this);
}
private:
static const FuncPtr s_callbackTable[];
}
//
// In the .cpp file:
//
const FuncPtr MyClass::s_callbackTable[] =
{
&MyClass::Thunk,
...
};
The problem with this approach is that I need to make MyClass::Thunk
publically visible so that the static initialization can access it.
Edit: I was mistaken. Apparently you don't need to make it public to do that.
I'd rather not though. I can think of two ways to solve this.
Which approach is better?
Create a private static method, and make the callback table a static variable inside the method. This way I can initialize it using pointers to private methods as well. Call the private static method from inside the constructor to get the table.
Create a separate class that contains public static thunk methods and make that class a friend of MyClass
. Forward declare that class in the header for MyClass
, just to allow the friend declaration. I like this because it also hides the thunk methods completely from any external consumers. Not really sure if there is a tangible benefit to that though.
Like so:
// .h file:
class MyClassInternals
class MyClass
{
public:
void Method ();
private:
friend class MyClassInternals;
static const FuncPtr s_callbackTable[];
}
// .cpp file:
class MyClassInternals
{
public:
static void Thunk(void* context)
{
reinterpret_cast<MyClass*>(context)->Method();
}
}
const FuncPtr MyClass::s_callbackTable[] =
{
&MyClassInternals::Thunk,
...
};
You can simply keep all of the trampoline machinery inside the constructor implementation (.cpp), no need to expose anything about the trampolines at the interface level (.h):
MyClass::MyClass() {
struct trampolines {
static void method1(void *ctx) { ((MyClass *)ctx)->method1(); }
static void method2(void *ctx) { ((MyClass *)ctx)->method2(); }
};
static void (*callbacks[])(void*) = { trampolines::method1,
trampolines::method2 };
register_callbacks(callbacks, this);
}
You don't even need to include in the (.h) the header of the C library that uses the callback/context interface. This is cleaner as the clients of your module won't depend on those C details.