4

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()
{
}
stands2reason
  • 672
  • 2
  • 7
  • 18
  • 1
    Templatize the most basic building blocks that you need and express the rest via them - e.g., see @Zoltan suggestion below. You can also use the oldschool macro on top of your template to hide the extra verbosity, altough it's less desirable solution – SomeWittyUsername Dec 17 '15 at 06:50

2 Answers2

2

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 );
}
stands2reason
  • 672
  • 2
  • 7
  • 18
Jens
  • 9,058
  • 2
  • 26
  • 43
  • I am confused by "inferMemberFunctionPointer". What is its explicit return type? Also, I have set my compiler to use C++14 but says the deduced return type is a "C++14 extension". It sounds like it means C++1z but I thought it was a C++14 feature. – stands2reason Dec 17 '15 at 18:47
  • @stands2reason The return type should be `MemberFunctionPointer`. I compiled the example with gcc 4.9.2 in C++1y mode. Which compiler are you using? – Jens Dec 17 '15 at 20:19
  • Sure enough, it was a build config error. I got it to work, except for this line `return MemberFunction{ memberFunctionPointer };`. – stands2reason Dec 18 '15 at 16:48
0

Why can't I just write something like this:

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

Because there's no way to determine C's type here. Hence the need for typename C.

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

I'm not sure how this would help your predicament. You still need a way to specify the object's type in your template argument list.

You could though do something like this:

 template<typename C>
 std::function<void(C&)> wrap(void(C::*mf)())
 {
   return std::function<void(C&)>(mf);
 }

Usage: wrap(&Obj::memfun);

And of course to actually invoke a non-static member function through a pointer, you will need an instance object.

Zoli
  • 1,137
  • 7
  • 12
  • Yes, this would save typing. Unfortunately, as I said in the question, I do not want to pass in a pointer as a run-time argument. I want a template instantiation that wraps a specific method. – stands2reason Dec 17 '15 at 19:00