Search code examples
c++templatesmember-pointers

Member function wrapper with a single argument template?


I made a template function which takes a member function as a parameter.

However, since the class has to be declared before it can be used as part of the member function parameter, I have to make it a separate parameter:

template<typename C, void (C::*Method)(void)>
function<void(C*)> methodWrap()
{
}

Which means when explicitly instantiate the template (I want these wrappers to be generated at compile time, not passing in a member pointer as an argument) I have to type it twice when I use it:

function<void(C*)> someFunc = wrapMethod<SomeClass, &SomeClass::someMethod>();

Why can't I just write something like tis:

template<void (C::*Method)(void)>
function<void(C*)> methodWrap()
{
}

and let it capture the type of C and its member function pointer without having to type SomeClass twice?

Or why can't I wrapper it in an outer template that declares C as a "free variable" and then has an inner template argument that performs the deduction

template<typename C>
template<void (C::*Method)(void)>
function<void(C*)> methodWrap()
{
}

Solution

  • If you can live with having a normal function parameter for the member function pointer instead of making it a template parameter, you can do

    #include <functional>
    
    template<typename R, typename C, typename... Args>
    struct MemberFunctionPointer
    {
        typedef R Return;
        typedef C Class;
    };
    
    template<typename R, typename C>
    constexpr auto inferMemberFunctionPointer(R (C::*method)())
    {
        return MemberFunctionPointer<R,C>{};
    }
    
    template<typename T> 
    constexpr auto methodWrap(T m)
    {
        typedef typename decltype(inferMemberFunctionPointer(m))::Class Class;
        typedef typename decltype(inferMemberFunctionPointer(m))::Return Return;
    
            return std::function<Return (Class*)>();
    }
    
    struct B {};
    
    struct A
    {
        B f();   
    };
    
    void foo()
    {
        auto const m = methodWrap( &A::f );
    }
    

    std::function is not a constexpr type, so we cannot use methodWrap to initialize a constexpr variable. You can bypass this by creating your own simple constexpr member function wrapper. I've also added a static_assert to get better error messages.

    #include <functional>
    
    template<typename R, typename C, typename... Args>
    struct MemberFunctionPointer
    {
        typedef R Return;
        typedef C Class;
    };
    
    template<typename R, typename C>
    constexpr auto inferMemberFunctionPointer(R (C::*method)() const)
    {
        return MemberFunctionPointer<R,C>{};
    }
    
    template<typename R, typename C>
    struct MemberFunction
    {
        constexpr explicit MemberFunction(R (C::*g)() const): f(g) {}
    
        constexpr R operator()(C const* obj) const
        {
            return (obj->*f)();
        }
    
        R (C::*f)() const;
    };
    
    template<typename T> 
    constexpr auto methodWrap(T m)
    {
        static_assert( std::is_member_function_pointer<T>::value, 
                       "Member function pointer expected!");
    
        typedef typename decltype(inferMemberFunctionPointer(m))::Class Class;
        typedef typename decltype(inferMemberFunctionPointer(m))::Return Return;
    
            return MemberFunction<Return, Class>{ MemberFunctionPointer<Return,Class>{} };
    }
    
    struct B {};
    
    struct A
    {
        constexpr B f() const;   
    };
    
    void foo()
    {
        auto constexpr m = methodWrap( &A::f );
        auto constexpr a = A{};
        m( &a );
    
        auto b = A{};
        m( &b );
    }