Search code examples
c++pointer-to-member

How to register a derived class member function pointer with a base class


As opposed to virtual member functions, I need a solution where a function implemented at each level class derivation can be registered for later call by the base class. ( Not just the most derived implementation)

To do this, I was thinking on providing a mechanism for derived classes to register their function with the base class such as during the derived class constructor.

I'm having trouble with the member function pointer argument though. I was thinking that Derived is derived from Base, the this pointer should be automatically casted.

Can this be done close to what I am trying or do I need to use static member functions, void *, and static_cast?

class Base
{
protected:
    typedef void (Base::*PrepFn)( int n );
    void registerPrepFn( PrepFn fn ) {};
}

class Derived : public Base
{
    Derived() {
        registerPrepFn( &Derived::derivedPrepFn );
    };

    void derivedPrepFn( int n ) {};

}

Compiler error:

error: no matching function for call to 'Derived::registerPrepFn(void (Derived::*)(int))'
note: candidates are:                 'void Base::registerPrepFn(void (Base::*)(int))'

Solution

  • This pattern can be handled with the Curiously Recurring Template Pattern, that is, compile-time polymorphism.

    The code below compiles without a cast.

    template <class D>
    class Base {
        protected:
            typedef void (D::*PrepFn) (int n);
            void registerPrepFn(PrepFn fn) {}
            Base() {};
            Base(PrepFn fn) {
                registerPrepFn(fn);
            }
    };
    
    class Derived : public Base<Derived>{
        Derived() {
            registerPrepFn( &Derived::derivedPrepFn);
        }
        void derivedPrepFn( int n) {};
    };
    
    class Derived2 : public Base<Derived2> {
        Derived2() : Base(&Derived2::derived2PrepFn) {}
        void derived2PrepFn(int n) {}
    };
    

    To my taste, the version with Derived2 is even better, because you push registration up into the base class. By deleting the default Base constructor (can't do that here, would break Derived), you can force derived classes to supply a pointer-to-member function with the correct signature.