generic member function pointer as a template parameter
Consider this code:
#include <iostream>
using namespace std;
class hello{
public:
void f(){
cout<<"f"<<endl;
}
virtual void ff(){
cout<<"ff"<<endl;
}
};
#define call_mem_fn(object, ptr) ((object).*(ptr))
template<R (C::*ptr_to_mem)(Args...)> void proxycall(C& obj){
cout<<"hello"<<endl;
call_mem_fn(obj, ptr_to_mem)();
}
int main(){
hello obj;
proxycall<&hello::f>(obj);
}
Of course this won't compile at line 16, because the compiler doesn't know what R
, C
and Args
, are. But there's another problem: if one tries to define those template parameters right before ptr_to_mem
, he runs into this bad situation:
template<typename R, typename C, typename... Args, R (C::*ptr_to_mem)(Args...)>
// ^variadic template, but not as last parameter!
void proxycall(C& obj){
cout<<"hello"<<endl;
call_mem_fn(obj, ptr_to_mem)();
}
int main(){
hello obj;
proxycall<void, hello, &hello::f>(obj);
}
Surprisingly, g++ does not complain about Args
not being the last parameter in the template list, but anyway it cannot bind proxycall
to the right template function, and just notes that it's a possible candidate.
Any solution? My last resort is to pass the member function pointer as an argument, but if I could pass it as a template parameter it would fit better with the rest of my code.
EDIT: as some have pointed out, the example seems pointless because proxycall isn't going to pass any argument. This is not true in the actual code I'm working on: the arguments are fetched with some template tricks from a Lua stack. But that part of the code is irrelevant to the question, and rather lengthy, so I won't paste it here.
You could try something like this:
template <typename T, typename R, typename ...Args>
R proxycall(T & obj, R (T::*mf)(Args...), Args &&... args)
{
return (obj.*mf)(std::forward<Args>(args)...);
}
Usage: proxycall(obj, &hello::f);
Alternatively, to make the PTMF into a template argument, try specialization:
template <typename T, T> struct proxy;
template <typename T, typename R, typename ...Args, R (T::*mf)(Args...)>
struct proxy<R (T::*)(Args...), mf>
{
static R call(T & obj, Args &&... args)
{
return (obj.*mf)(std::forward<Args>(args)...);
}
};
Usage:
hello obj;
proxy<void(hello::*)(), &hello::f>::call(obj);
// or
typedef proxy<void(hello::*)(), &hello::f> hello_proxy;
hello_proxy::call(obj);
In modern C++ one can use template<auto>
and generic lambda-wrapper:
#include <utility>
#include <functional>
template<auto mf, typename T>
auto make_proxy(T && obj)
{
return [&obj] (auto &&... args) { return (std::forward<T>(obj).*mf)(std::forward<decltype(args)>(args)...); };
}
struct R {};
struct A {};
struct B {};
struct Foo
{
R f(A &&, const B &) { return {}; }
//R f(A &&, const B &) const { return {}; }
};
int main()
{
Foo foo;
make_proxy<&Foo::f>(foo)(A{}, B{});
//make_proxy<static_cast<R (Foo::*)(A &&, const B &) const>(&Foo::f)>(std::as_const(foo))(A{}, B{});
//make_proxy<static_cast<R (Foo::*)(A &&, const B &)>(&Foo::f)>(foo)(A{}, B{});
}
If there are overloadings one should to specify member function type explicitly as in commented code.