Search code examples
c++templatesc++14variadic-templatestemplate-argument-deduction

How can I wrap a std::function and have easy access to its return and argument types?


I'm trying to do something that simplifies to the following:

#include <functional>
#include <initializer_list>
#include <vector>

template <typename Ret, typename... Args>
struct func_wrapper
{
public:
    using Func = std::function<Ret(Args...)>;

    Ret operator()(Args && ...args)
    {
        return _impl(std::forward<Args>(args)...);
    }

    void another_function(int another_arg, Args && ...args) { }
    /// and so on

private:
   Func _impl;
};

func_wrapper<void(int, float)> f;


Basically, I want to make a type that wraps a std::function and adds some other functionality for my application. Inside my type, I would like to be able to use the return type Ret and argument parameter pack Args freely in the class interface. However, when I try to compile the above with gcc 8.3, I get an error:

<source>: In instantiation of 'struct func_wrapper<void(int, float)>':
<source>:20:32:   required from here
<source>:9:45: error: function returning a function
     using Func = std::function<Ret(Args...)>;
                                             ^
<source>:11:9: error: function returning a function
     Ret operator()(Args && ...args)

I'm not sure what to make of that error. Is there a straightforward way to do what I want?


Solution

  • The problem is that you have to use partial specialization

    template <typename>
    struct func_wrapper;
    
    template <typename Ret, typename... Args>
    struct func_wrapper<Ret(Args...)>
     {
       // ...
     };
    

    If you write

    template <typename Ret, typename... Args>
    struct func_wrapper
     {
       // ...
     };
    

    declaring f as

    func_wrapper<void(int, float)> f;
    

    you have that Ret is deduced as void(int, float) and the Args... variadic list is deduced as an empty list.

    So, when you define

    using Func = std::function<Ret(Args...)>;
    

    the Func declaration become

    // ............................V..........VVV  function-function type ?
    using Func = std::function<void(int, float)()>;
    

    that gives the "error: function returning a function" error.

    You have to declare func_wrapper as receiving a single typename

    template <typename>
    struct func_wrapper;
    

    so you can pass the type of the function (void(int, float) in your example) as template parameter, and then a template specialization

    template <typename Ret, typename... Args>
    struct func_wrapper<Ret(Args...)>
     {
       // ...
     };
    

    can resolve return type and arguments types from the function type.