0
votes

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?

2

2 Answers

2
votes

Regarding using fType = R (T::*)(ARGS...);, type fType is a pointer type, so it can't be used as a template parameter in order to memorize/save the instance method address at runtime to be used later. So, you should define a member in staticWrapperHelper to save the method pointer to be used later:

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

Also I changed wrapper static method to an instance method: operator() to be a functor object.

Now, in the staticWrapper function that works as a factory function for staticWrapperHelper, we return an instance of staticWrapperHelper that stores the method pointer. This instace object can be used later to invoke the instance method on the supplied object of type T.

template<typename T, typename R, typename... ARGS>
auto staticWrapper(R (T::*memberFunction)(ARGS...))
{
    using Helper = staticWrapperHelper<T, R, ARGS...>;
    return Helper{.method = memberFunction};
}

Live demo: http://coliru.stacked-crooked.com/a/9a7b7c04b36d9dc0

0
votes

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