Search code examples
c++lambdapolymorphismpointer-to-member

How to convert an object and its method into a function callable just with method’s arguments


There is a function

template<typename Class, typename MemberFuncPtr>
auto makeCallback( Class* a, MemberFuncPtr func )
{
    return [a, func]( auto&&... args )
    {
        return ( a->*func )( std::forward<decltype( args )>( args )... );
    };
}

that converts an object pointer and its member method into a lambda capturing them that can be called later just with method’s arguments. It works fine except for one situation, where the method is virtual and overridden in a derived class, e.g.

#include <iostream>

struct Base
{
    virtual void g( int, int ) const
    {
        std::cout << "Base" << std::endl;
    }
};

struct Derived : Base
{
    virtual void g( int, int ) const override
    {
        std::cout << "Derived" << std::endl;
    }
};

template<typename Class, typename MemberFuncPtr>
auto makeCallback( Class* a, MemberFuncPtr func )
{
    return [a, func]( auto&&... args )
    {
        return ( a->*func )( std::forward<decltype( args )>( args )... );
    };
}

int main()
{
    Derived d;
    auto cb = makeCallback( &d, &Base::g );
    cb( 1, 2 );

    return 0;
}

Here inspite of makeCallback( &d, &Base::g ) the program prints ‘Derived’.

Is there a way to modify only makeCallback (including any change of its signature) to receive ‘Base’ in program output?


Solution

  • You might do, with some change:

    template<typename Class, typename MemberFuncPtr>
    auto makeCallback( Class* a, MemberFuncPtr func )
    {
        return [a, func]( auto&&... args )
        {
            return std::invoke(func, a, std::forward<decltype( args )>( args )... );
        };
    }
    
    int main()
    {
        Derived d;
        auto cb = makeCallback(&d, [](auto obj, auto&&... args){ obj->Base::g(args...); });
        cb( 1, 2 );
    }
    

    Demo