Reputation: 131523
I have a bunch of functions which I want to use as functors (that is, use types instead of pass pointers to the functions, or any other kind of data).
Is there an elegant/idiomatic/standard way of doing this with the standard library, or the standard library + Boost? Perhaps using bind()
somehow?
Or should I go with something simplistic (well, kind of simplistic) such as:
template<typename Function, Function& F, typename... Parameters>
struct functor {
using function_type = Function;
using parameters_as_tuple_type = std::tuple<Parameters...>;
auto operator() (Parameters&&... params) ->
decltype(F(std::forward<Parameters>(params)...))
{
return F(std::forward<Parameters>(params)...);
}
};
Notes:
Upvotes: 2
Views: 305
Reputation: 275385
First, a concrete example for a fixed type.
int foo( int );
void foo( double );
struct foo_t {
template<class...Args>
auto operator()(Args&&...args)const
->decltype( foo( std::declval<Args>()... ) )
{ return ( foo( std::forward<Args>(args)... ) ); }
};
now foo_t
is an object that invokes the overloads of foo
via perfect forwarding.
To make it generic:
#define RETURNS(...) noexcept(noexcept(__VA_ARGS__)) -> decltype(__VA_ARGS__) { return __VA_ARGS__; }
#define OVERLOAD_SET_TYPE(...) \
struct { \
template<class...Args> \
auto operator()(Args&&...args)const \
RETURNS( __VA_ARGS__( std::forward<Args>(args)... ) ) \
}
so we can do
using foo_t = OVERLOAD_SET_TYPE(foo);
You cannot manipulate overload sets of a function as an object; the only way to do it is textually. Hence the macro.
This has all of the usual imperfections of perfect forwarding. There is no way to generically avoid those imperfections.
Upvotes: 2
Reputation: 93274
If the function is not overloaded, you can do this in C++17:
template <auto F>
auto to_function_object()
{
return [](auto&&... xs) -> decltype(auto)
{
return F(std::forward<decltype(xs)>(xs)...);
};
}
void a(int) { }
int main()
{
auto af = to_function_object<a>();
af(1);
}
If the function is overloaded, there's no way to pass its overload set as an argument to another function or a template. You're forced to manually write a wrapper lambda on the spot. Example:
void foo(int) { }
void foo(char) { }
// ...
bar([](auto x){ return foo(x); });
N3617 aimed to solve this issue by introducing a "lift" operator.
P0119 by A. Sutton solves the problem in a different way by allowing overload sets to basically generate the "wrapper lambda" for you when passed as arguments.
Until any of those proposal is accepted you can use a beautiful C++14 macro instead:
#define LIFT(f) \
[](auto&&... xs) noexcept(noexcept(f(std::forward<decltype(xs)>(xs)...))) \
-> decltype(f(std::forward<decltype(xs)>(xs)...)) \
{ \
return f(std::forward<decltype(xs)>(xs)...); \
}
Upvotes: 4