Search code examples
c++c++14metaprogrammingfunction-pointerspointer-to-member

How to write a generic non-member wrapper around arbitrary member functions?


Problem

Have a template that generates a wrapper for an arbitrary member function that allows to invoke that function on an object, e.g.:

Given bool std::string::empty(), the wrapper to be generated would be bool wrapper(const std::string& str) { return str.empty(); }.

The wrapper function shall not be a lambda (so that it can be passed as function pointer).

My approach (for non-const member functions)

/* Wrapper (for non-const member functions) */

template<typename T, typename R, typename... ARGS>
struct staticWrapperHelper
{
    using fType = R (T::*)(ARGS...);
    template<fType FUNCTION>
    static R wrapper(T& object, ARGS... args) { return object.*FUNCTION(args...); }
};

template<typename T, typename R, typename... ARGS>
auto staticWrapper(R (T::*memberFunction)(ARGS...))
{
    //auto function = [memberFunction] (T& object, ARGS... args) -> R { return (object.*memberFunction)(args...); };
    using Helper = staticWrapperHelper<T, R, ARGS...>;

    R (*wrapper)(T&, ARGS...) = &Helper::template wrapper<R (T::*)(ARGS...)>;
    return wrapper;
}

/* Example */

class A
{
public:
    void member() {}
};

int main()
{
    A a;
    auto f = staticWrapper<A>(&A::member);
    f(a);

    return 0;
}

Issue with my approach

It does not compile. It seems like the returned wrapper is still a template: http://coliru.stacked-crooked.com/a/d4c0dd9ab631aa1f

g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
main.cpp: In instantiation of 'auto staticWrapper(R (T::*)(ARGS ...)) [with T = A; R = void; ARGS = {}]':
main.cpp:32:41:   required from here
main.cpp:17:9: error: no matches converting function 'wrapper' to type 'void (*)(class A&)'
   17 |     R (*wrapper)(T&, ARGS...) = &Helper::template wrapper;
      |         ^~~~~~~
main.cpp:8:14: note: candidate is: 'template static R staticWrapperHelper::wrapper(T&, ARGS ...) [with R (T::* FUNCTION)(ARGS ...) = FUNCTION; T = A; R = void; ARGS = {}]'
    8 |     static R wrapper(T& object, ARGS... args) { return object.*FUNCTION(args...); }
      |              ^~~~~~~

Any ideas?


Solution

  • I found a solution I can accept by passing the member function pointer as template argument, which -- unfortunately -- requires the type to be explicitly mentioned in C++14 (a helper macro can help here). The signature of the function pointer is then broken down by a helper function in order to create the wrapper.

    #include <iostream>
    
    /* Wrapper (for non-const member functions) */
    
    template<typename PTM, PTM ptm>
    struct staticWrapper
    {
        constexpr static auto make()
        {
            return breakDownAndWrap(ptm);
        }
    
    private:
        template<typename C, typename R, typename... ARGS>
        constexpr static auto breakDownAndWrap(R (C::*)(ARGS...))
        {
            R (*wrapper)(C&, ARGS...) = &Wrapper<C, R, ARGS...>::get;
            return wrapper;
        }
    
        template<typename C, typename R, typename... ARGS>
        struct Wrapper
        {
            constexpr static R get(C& obj, ARGS... args)
            {
                return (obj.*ptm)(args...);
            }
        };
    };
    
    /* Optional helper macro */
    
    #define AUTO_T(val) decltype(val), val
    
    /* Example */
    
    class A
    {
    public:
        int number;
    
        void member() { std::cout << number << std::endl; }
    };
    
    int main()
    {
        A a {2};
        auto f = staticWrapper<decltype(&A::member), &A::member>::make();
        auto g = staticWrapper<AUTO_T(&A::member)>::make();
        f(a);
        g(a);
    
        return 0;
    }
    

    Working example: http://coliru.stacked-crooked.com/a/4c5cb1d98437c789