Reputation: 1386
I would like to have a function, that calls a given member function with the provides variadic input argument. I wrote something like this:
#include <type_traits>
#include <utility>
struct A {
constexpr int show(int a, int b) const noexcept {return a + b;}
};
template <typename T, typename MemFn, typename ... Args>
int show(T && obj, MemFn Fn, Args&&... args)
{
return (obj.*Fn)(std::forward<Args>(args)...);
}
int main()
{
constexpr A a;
return show(a, &A::show, 1, 2);
}
and it works just fine, as long as I only have one definition of show
method in my struct. As soon as I add something like
struct A {
constexpr int show(int a, int b) const noexcept {return a + b;}
constexpr int show(int a) const noexcept {return a * 3;}
};
The compiler can not deduce the type of the member function and it really makes all the sense, but I was wondering if there is a workaround for this problem, like embedding the input arguments types in member function template or something?
Sample code can be found here.
Upvotes: 3
Views: 122
Reputation: 136208
I was wondering if there is a workaround for this problem, like embedding the input arguments types in member function template or something?
Use lambdas instead of an object and a member function pointer. E.g.:
struct A {
constexpr int show(int a, int b) const noexcept {return a + b;}
constexpr int show(int a) const noexcept {return a * 3;}
};
template <typename F, typename ... Args>
int show(F&& f, Args&&... args) {
return std::forward<F>(f)(std::forward<Args>(args)...);
}
int main() {
constexpr A a;
auto f = [&a](auto... args) { return a.show(std::forward<decltype(args)>(args)...); };
show(f, 1);
show(f, 1, 2);
}
Upvotes: 1
Reputation: 302748
This is an annoyingly difficult problem, which continuously leads to language proposals in an attempt to address it (P0119, P0834, P1170).
Until then, the question of how to wrap invoking a particular member function on a type, where that member function is either overloaded or a template or takes default arguments, is pretty difficult.
The easiest way to do this is just to write a lambda:
[](A& a, auto&&... args) -> decltype(a.show(FWD(args)...)) { return a.show(FWD(args)...); }
But this is actually not that easy, nor is it particularly convenient - and it really only handles the case where show
is invokable on a non-const
A
. What if we had const
and non-const
overloads? Or &
and &&
?
The most complete way to implement this, in my opinion, is to use Boost.HOF with this macro:
#define CLASS_MEMBER(T, mem) boost::hof::fix(boost::hof::first_of(\
boost::hof::match( \
[](auto, T& s, auto&&... args) \
BOOST_HOF_RETURNS(s.mem(FWD(args)...)), \
[](auto, T&& s, auto&&... args) \
BOOST_HOF_RETURNS(std::move(s).mem(FWD(args)...)), \
[](auto, T const&& s, auto&&... args) \
BOOST_HOF_RETURNS(std::move(s).mem(FWD(args)...)), \
[](auto, T const& s, auto&&... args) \
BOOST_HOF_RETURNS(s.mem(FWD(args)...))), \
[](auto self, auto&& this_, auto&&... args) \
BOOST_HOF_RETURNS(self(*FWD(this_), FWD(args)...)) \
))
which in your case, you want: CLASS_MEMBER(A, show)
. That will give you a function object that you can properly invoke:
auto show_fn = CLASS_MEMBER(A, show);
show_fn(a, 1); // ok, calls a.show(1)
show_fn(a, 1, 2); // ok, calls a.show(1, 2)
show_fn(a, 1, 2, 3); // error, no matching call - but sfinae friendly
Upvotes: 4
Reputation: 6144
You can constrain your function using a more specific type for a member function:
template <typename T, typename... Args>
int show(T && obj, int(std::remove_reference_t<T>::*Fn)(int, int) const, Args&&... args)
{
return (obj.*Fn)(std::forward<Args>(args)...);
}
This definition, however, might be too constrained depending on your use-cases, since now Fn
parameter has to exactly match the int(int, int) const
signature including possible cv and ref-qualifiers.
Upvotes: 0