Search code examples
c++callbackprivatefriend

Use of a friend class to hide private static callback methods


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?

  1. 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.

  2. 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,
        ...
    };
    

Solution

  • 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.