Search code examples
c++c++11variadic-templates

How to store variadic template arguments?


Is it possible to store a parameter pack somehow for a later use?

template <typename... T>
class Action {
private:        
    std::function<void(T...)> f;
    T... args;  // <--- something like this
public:
    Action(std::function<void(T...)> f, T... args) : f(f), args(args) {}
    void act(){
        f(args);  // <--- such that this will be possible
    }
}

Then later on:

void main(){
    Action<int,int> add([](int x, int y){std::cout << (x+y);}, 3, 4);

    //...

    add.act();
}

Solution

  • To accomplish what you want done here, you'll have to store your template arguments in a tuple:

    std::tuple<Ts...> args;
    

    Furthermore, you'll have to change up your constructor a bit. In particular, initializing args with an std::make_tuple and also allowing universal references in your parameter list:

    template <typename F, typename... Args>
    Action(F&& func, Args&&... args)
        : f(std::forward<F>(func)),
          args(std::forward<Args>(args)...)
    {}
    

    Moreover, you would have to set up a sequence generator much like this:

    namespace helper
    {
        template <int... Is>
        struct index {};
    
        template <int N, int... Is>
        struct gen_seq : gen_seq<N - 1, N - 1, Is...> {};
    
        template <int... Is>
        struct gen_seq<0, Is...> : index<Is...> {};
    }
    

    And you can implement your method in terms of one taking such a generator:

    template <typename... Args, int... Is>
    void func(std::tuple<Args...>& tup, helper::index<Is...>)
    {
        f(std::get<Is>(tup)...);
    }
    
    template <typename... Args>
    void func(std::tuple<Args...>& tup)
    {
        func(tup, helper::gen_seq<sizeof...(Args)>{});
    }
    
    void act()
    {
       func(args);
    }
    

    And that it! So now your class should look like this:

    template <typename... Ts>
    class Action
    {
    private:
        std::function<void (Ts...)> f;
        std::tuple<Ts...> args;
    public:
        template <typename F, typename... Args>
        Action(F&& func, Args&&... args)
            : f(std::forward<F>(func)),
              args(std::forward<Args>(args)...)
        {}
    
        template <typename... Args, int... Is>
        void func(std::tuple<Args...>& tup, helper::index<Is...>)
        {
            f(std::get<Is>(tup)...);
        }
    
        template <typename... Args>
        void func(std::tuple<Args...>& tup)
        {
            func(tup, helper::gen_seq<sizeof...(Args)>{});
        }
    
        void act()
        {
            func(args);
        }
    };
    

    Here is your full program on Coliru.


    Update: Here is a helper method by which specification of the template arguments aren't necessary:

    template <typename F, typename... Args>
    Action<Args...> make_action(F&& f, Args&&... args)
    {
        return Action<Args...>(std::forward<F>(f), std::forward<Args>(args)...);
    }
    
    int main()
    {
        auto add = make_action([] (int a, int b) { std::cout << a + b; }, 2, 3);
    
        add.act();
    }
    

    And again, here is another demo.