C++11 variadic std::function parameter

23,900

Solution 1

It looks like you want to use overloading

template<typename R, typename ...A>
void test(R f(A...))
{
    test(std::function<R(A...)>(f));
}

This simple implementation will accept most if not all the functions you will try to pass. Exotic functions will be rejected (like void(int...)). More work will give you more genericity.

Solution 2

std::function implements the Callable interface, i.e. it looks like a function, but that doesn't mean you should require callable objects to be std::functions.

template< typename F > // accept any type
void test(F const &f) {
    typedef std::result_of< F( args ) >::type R; // inspect with traits queries
}

Duck typing is the best policy in template metaprogramming. When accepting a template argument, be unspecific and just let the client implement the interface.

If you really need a std::function for example to re-target the variable or something crazy like that, and you know the input is a raw function pointer, you can decompose a raw function pointer type and reconsitute it into a std::function.

template< typename R, typename ... A >
void test( R (*f)( A ... ) ) {
    std::function< R( A ... ) > internal( f );
}

Now the user can't pass a std::function because that has been encapsulated within the function. You could keep your existing code as another overload and just delegate to that, but be careful to keep interfaces simple.

As for stateful lambdas, I don't know how to handle that case. They don't decompose to function pointers and as far as I know the argument types cannot be queried or deduced. This information is necessary to instantiate std::function, for better or worse.

Solution 3

This is an old one, and I can't seem to find much on the same topic, so I thought I would go ahead and put in a note.

Compiled on GCC 4.8.2, the following works:

template<typename R, typename... A>
R test(const std::function<R(A...)>& func)
{
    // ...
}

However, you can't just call it by passing in your pointers, lambdas, etc. However, the following 2 examples both work with it:

test(std::function<void(int, float, std::string)>(
        [](int i, float f, std::string s)
        {
            std::cout << i << " " << f << " " << s << std::endl;
        }));

Also:

void test2(int i, float f, std::string s)
{
    std::cout << i << " " << f << " " << s << std::endl;
}

// In a function somewhere:
test(std::function<void(int, float, std::string)>(&test2));

The downside of these should stand out pretty obviously: you have to explicitly declare the std::function for them, which might look a little bit ugly.

That said, though, I threw that together with a tuple that gets expanded to call the incoming function, and it works, just requiring a little bit more of an explicitly saying what you're doing calling the test function.

Example code including the tuple thing, if you want to play with it: http://ideone.com/33mqZA

Solution 4

It's usually ill-advised to accept std::function by value unless you are at 'binary delimitation' (e.g. dynamic library, 'opaque' API) since as you've just witnessed they play havoc with overloading. When a function does in fact take an std::function by value then it's often the burden of the caller to construct the object to avoid the overloading problems (if the function is overloaded at all).

Since however you've written a template, it's likely the case that you're not using std::function (as a parameter type) for the benefits of type-erasure. If what you want to do is inspecting arbitrary functors then you need some traits for that. E.g. Boost.FunctionTypes has traits such as result_type and parameter_types. A minimal, functional example:

#include <functional>

#include <boost/function_types/result_type.hpp>
#include <boost/function_types/parameter_types.hpp>
#include <boost/function_types/function_type.hpp>

template<typename Functor>
void test(Functor functor) // accept arbitrary functor!
{
    namespace ft = boost::function_types;

    typedef typename ft::result_type<Functor>::type result_type;
    typedef ft::parameter_types<Functor> parameter_types;
    typedef typename boost::mpl::push_front<
        parameter_types
        , result_type
    >::type sequence_type;
    // sequence_type is now a Boost.MPL sequence in the style of
    // mpl::vector<int, double, long> if the signature of the
    // analyzed functor were int(double, long)

    // We now build a function type out of the MPL sequence
    typedef typename ft::function_type<sequence_type>::type function_type;

    std::function<function_type> function = std::move(functor);
}

As a final note, I do not recommend introspecting functors (i.e. prodding for their result type and argument types) in the general case as that simply don't work for polymorphic functors. Consider several overloaded operator(): then there is no 'canonical' result type or argument types. With C++11 it's better to 'eagerly' accept any kind of functor, or constrain them using techniques like SFINAE or static_assert depending on the needs, and later on (when parameters are available) to use std::result_of to inspect the result type for a given set of arguments. A case where constraining up front is desirable is when the aim is to store functors into e.g. a container of std::function<Sig>.

To get a taste of what I mean by the previous paragraph it's enough to test the above snippet with polymorphic functors.

Share:
23,900

Related videos on Youtube

Daniel K.
Author by

Daniel K.

Programmer :)

Updated on August 29, 2020

Comments

  • Daniel K.
    Daniel K. over 3 years

    A function named test takes std::function<> as its parameter.

    template<typename R, typename ...A>
    void test(std::function<R(A...)> f)
    {
        // ...
    }
    

    But, if I do the following:

    void foo(int n) { /* ... */ }
    
    // ...
    
    test(foo);
    

    Compiler(gcc 4.6.1) says no matching function for call to test(void (&)(int)).

    To make the last line test(foo) compiles and works properly, how can I modify the test() function? In test() function, I need f with type of std::function<>.

    I mean, is there any template tricks to let compiler determine the signature of function(foo in example), and convert it to std::function<void(int)> automatically?

    EDIT

    I want to make this work for lambdas(both stated and stateless) as well.

  • Daniel K.
    Daniel K. about 12 years
    What about lambdas (both stated and stateless)?
  • Johannes Schaub - litb
    Johannes Schaub - litb about 12 years
    @Daniel you are out of luck. Or make test a template that accepts anything (T). std::function won't reject incompatible function objects outright anyway, so the objective to limit the type of the function template parameter seems not to be too useful to me here.
  • Daniel K.
    Daniel K. about 12 years
    I tried with (T), but, how can it be made to std::function<R(A...)>? It seems that I can get R using std::result_of<>, but A...?
  • user3694249
    user3694249 almost 10 years
    Just for kicks, I came up with a method using index_sequence (being added in C++14, but easy to implement yourself in C++11) and a function_traits style structure to come up with something that will take any lambda, functor, or function, and use it. http://ideone.com/LNpj74 shows a working example of that. Note, however, for functors with 2+ operator() overloaded, it requires an additional interface specifying the types to use still. Did not try it with polymorphic functors, but I would expect that'll cause issues as well...
  • underscore_d
    underscore_d over 7 years
    As Potatoswatter alluded above, when templates are available, using an std::function is a pile of dynamic allocation overhead and boilerplate for no benefit. Better to just make a generic function template that can accept a lambda, than pulling in std::function all over the place.
  • serup
    serup about 7 years
    I believe the correct term would be dynamic binding - use of the term duck typing has been deprecated
  • Potatoswatter
    Potatoswatter about 7 years
    @serup Google => "Late binding, or dynamic binding, is a computer programming mechanism in which the method being called upon an object or the function being called with arguments is looked up by name at runtime." Not applicable to templates.
  • serup
    serup about 7 years
    Then why use the term duck typing ?
  • Potatoswatter
    Potatoswatter about 7 years
    @serup By convention. I'm open to suggestions…