C++ How to store a parameter pack as a variable
Asked Answered
c++
B

2

30

At the moment I am having trouble trying to store a parameter pack, this is example code of the design:

template<typename Func, typename... Args>
void handleFunc(Func func, Args&&... args) {
    struct nest {
        Func nestFunc;
        Args... nestArgs; // I DONT KNOW WHAT TO DO HERE
        void setup(Func func, Args... args) {
            nestFunc = func;
            nestArgs = (args)...; // SO I CAN SET IT HERE
        }
        // Later I will forward this and run the function with its arguments
        unsigned process() {
            nestFunc(std::forward<Args>(nestArgs)...); // USE IT HERE
            return 0;
        }
    };
    nest* myNest;
    myNest->setup(func, (args)...);
}

This is an example of everything involved for the problem, I need to store the arguments for later invoke in my nest struct. Also, if you have a solution to store it but to set it is different to mine, please also let me know about that too. Thanks.

Ballistics answered 21/3, 2013 at 1:19 Comment(2)
std::tuple<Args...> - also, std::bind and std::function and all that fun stuff.Carltoncarly
Does this answer your question? Is it possible to "store" a template parameter pack without expanding it?Havenot
Y
26

Edit from 2018: In C++17 the answer to this question is different. You still have to store your arguments in a ::std::tuple, but when the time comes to call the function ::std::apply handles unpacking this tuple and calling the function for you. And if you need to use the indices trick for something other than what ::std::apply does, there's ::std::integer_sequence and the associated helper function, ::std::make_index_sequence that you should investigate.

Now back to your C++11/14 answer from way back in 2013.

You have to use ::std::tuple<Args...> to store it. But then the question is how to unpack it when you need it. For that you need to use a technique called 'indices'.

So, here is a link to a place where I've done approximately what you're looking to do. The most relevant class here that's sort of the centerpiece is suspended_call.

https://bitbucket.org/omnifarious/sparkles/src/tip/sparkles/deferred.hpp?at=default

In just a bit, I'll extract the most relevant bits and put them in terms of your code.

This line:

auto saved_args = ::std::make_tuple(::std::move(args)...);

saves the arguments into a tuple. I used ::std::move there, and I think that's the right thing to do. But it's possible I'm wrong and I should use ::std::forward. I've never been clear on the exact difference aside from signaling intent.

The code that actually does the call with the saved arguments can be found here. Now that code is fairly specific to exactly what I'm doing. The bit that implements the indices trick involves creating a pack of integers that maps to the indices to use as arguments the ::std::get<I> template. Once you have this pack of integers, you can then use it to expand the call to ::std::get to get all the tuple elements as individual arguments.

I'll try to come up with code that does that in a relatively straightforward way:

#include <tuple>
#include <cstddef>
#include <string>
#include <utility>

template < ::std::size_t... Indices>
struct indices {};

template < ::std::size_t N, ::std::size_t... Is>
struct build_indices : build_indices<N-1, N-1, Is...>
{};

template < ::std::size_t... Is>
struct build_indices<0, Is...> : indices<Is...>
{};

template <typename FuncT, typename ArgTuple, ::std::size_t... Indices>
auto call(const FuncT &f, ArgTuple &&args, const indices<Indices...> &)
   -> decltype(f(::std::get<Indices>(::std::forward<ArgTuple>(args))...))
{
   return ::std::move(f(::std::get<Indices>(::std::forward<ArgTuple>(args))...));
}

template <typename FuncT, typename ArgTuple>
auto call(const FuncT &f, ArgTuple &&args)
     -> decltype(call(f, args,
                      build_indices< ::std::tuple_size<ArgTuple>::value>{}))
{
    const build_indices< ::std::tuple_size<ArgTuple>::value> indices;

    return ::std::move(call(f, ::std::move(args), indices));
}

int myfunc(::std::string name, const unsigned int foo)
{
   return 0;
}

int foo(::std::tuple< ::std::string, const unsigned int> saved_args)
{
   return call(myfunc, ::std::move(saved_args));
}

A lot of this code was borrowed from this page on the indices trick.

Also, that's sort of a sample that you will have to adapt slightly to your specific situation. Basically, just call call(nestFunc, saved_args) somewhere.

Yorick answered 21/3, 2013 at 1:26 Comment(9)
How would set this tuple with (args)...;Ballistics
Can you elaborate more on these "indicies"?Kugler
@David: Yes, more examples. You shall have them. I actually had to write code that did this recently.Yorick
@Omnifarious, Interesting, I was just thinking if it would be possible to store a parameter pack for later expansion. Would your code work to, say, store a callable object along with a list of arguments for later calling?Bottleneck
@Omnifarious, Sweet, that actually fills in for some uses I've been wanting. Honestly, the best I had, from quite a while ago, was a std::function<void()> (It wouldn't need a return value) with bound arguments.Bottleneck
Ok, so far using a tuple to store args, I've added: std::tuple<Args...> nestArgs; setting the tuple: nestArgs = std::tuple<Args...>(args...); OR nestArgs = std::make_tuple(args...); Now it's a case of forwarding it to the function...Ballistics
@Luka: I added an example of how to do the forwarding. Again, I used ::std::move. Not positive that's the right thing to do, but I think it is.Yorick
Thanks for your help. I must say though, I came across this not long ago, just figured it out and it applies the same thing your solution gives: linkBallistics
@Luka: Yep. :-) Perhaps I should've pointed you there. I was enjoying the mental exercise of figuring out how it worked again. The original (I think) example: #7859317Yorick
D
18

I know it's been a while but I had similar needs and came up with this solution, hope it helps someone:

#include <functional>

template<typename Func, typename... Args>
struct nest {
    std::function<void()> callBack;

    void setup(Func func1, Args... args) {
        callBack = [func1, args...]()
        {
            (func1)(args...);
        };
    }

    unsigned process() {
        callBack();
        return 0;
    }
};

template<typename Func, typename... Args>
void handleFunc(Func func, Args&&... args) {
    nest<Func, Args...> myNest;
    myNest.setup(func, args...);
}
Denman answered 17/12, 2016 at 17:20 Comment(5)
clever workaround for the insanely crippled C++ pack syntax )Establishment
Storing them all inside a lambda instead of in a tuple is very clever.Yorick
Though using generalized lambda capture expressions to forward all of the arguments (and then also forward them in the call inside the lambda) might be a better idea.Yorick
Does this implement perfect forwarding?Amphitrite
This really solved my 2 days of finding ... ThanksKrol

© 2022 - 2024 — McMap. All rights reserved.