Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ How to store a parameter pack as a variable

Tags:

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.

like image 680
Luka Avatar asked Mar 21 '13 01:03

Luka


People also ask

What is a parameter pack?

Parameter packs (C++11) A parameter pack can be a type of parameter for templates. Unlike previous parameters, which can only bind to a single argument, a parameter pack can pack multiple parameters into a single parameter by placing an ellipsis to the left of the parameter name.

What are Variadic functions in C?

Variadic functions are functions that can take a variable number of arguments. In C programming, a variadic function adds flexibility to the program. It takes one fixed argument and then any number of arguments can be passed.

What is Variadic template in C++?

A variadic template is a class or function template that supports an arbitrary number of arguments. This mechanism is especially useful to C++ library developers: You can apply it to both class templates and function templates, and thereby provide a wide range of type-safe and non-trivial functionality and flexibility.

What is ellipsis in C++?

Ellipsis in C++ allows the function to accept an indeterminate number of arguments. It is also known as the variable argument list. Ellipsis tells the compiler to not check the type and number of parameters the function should accept which allows the user to pass the variable argument list.


2 Answers

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.

like image 51
Omnifarious Avatar answered Oct 01 '22 14:10

Omnifarious


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...); } 
like image 27
Maro Avatar answered Oct 01 '22 12:10

Maro