Search code examples
c++c++11templatesc++17functor

How do I make a functor out of an arbitrary function?


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:

  • C++11 solutions are preferred, but if you have something requiring even C++17, that's also interesting.
  • My "solution" might not work, I think, for overloaded functions.

Solution

  • 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);
    

    live example.

    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.