user2770808
user2770808

Reputation: 347

Build a tuple recursively

Given a parameter pack and a vector of strings, I want to build a tuple recursively that will take the first of each at a time, and append that to the tuple. So If I had a vector with "string1", "string2", "string3" and a parameter pack of 5, 2.5, true... the resulting tuple would be "string1", 5, "string2", 2.5, "string3", true.

Heres what I've tried so far

in my main I do something like this

std::vector<std::string> string_vec; 
std::tuple<> t;

//initalize string_vec to something 
set_up_strings(string_vec);

//pass an empty tuple to start building with the strings and args
tuple_maker(t, string_vec, args...);

where my tuple_maker recursively adds one of each.

template<typename T, typename... Args, typename... Ts>
void tuple_maker(std::tuple<Ts...> t, std::vector<std::string> &vec, T value,  Args... args)
{
  auto newTup1 = tuple_append(t, vec.begin());
  auto newtup2 = tuple_append(newTup1, value);
  vec.erase(vec.begin());

//now pass in the vector and args after removing front of each
  tuple_maker(newtup2, vec,args...);
}

eventually when there are no more args, this function will be called(ending the recursion)

template<typename... Ts>
std::tuple<Ts...> tuple_maker(std::tuple<Ts...> t, std::vector<std::string> &vec)
{

int tup_size = std::tuple_size<decltype(t)>::value;
std::cout<< "final tuple has size of " << tup_size << std::endl;

//return t;

}

If I pass in something like string1-3, and 3 args like I mentioned before, It prints the tuple having a size of 6 so I believe it is creating it correctly. However, I am having trouble returning it back to the main function. I dont know how to set up the return types so that it will correctly bring back the final tuple back to the previous function, and then back to the main.

for reference, the helper 'tuple_maker' function I use is here

template <typename NewType, typename... TupleElem>
std::tuple<TupleElem..., NewType> tuple_append(const std::tuple<TupleElem...> &tup, const NewType &n)
{
 return std::tuple_cat(tup, std::make_tuple(n));
}

I tried something like this...

template<typename T, typename... Args, typename... Ts, typename... ret>
std::tuple<ret...> tuple_maker(std::tuple<Ts...> t, std::vector<std::string> &vec, T value,  Args... args)

Upvotes: 2

Views: 2495

Answers (2)

md5i
md5i

Reputation: 3083

I can see a couple of possible problems in your tuple_maker definition (why are you passing an iterator to tuple_append?), but it looks like you have a handle on what you are trying to do, so I'll let you sort that out yourself.

Your question appears to be asking how to determine what the return type should be for your function. There are a couple of ways to do this.

One, which is simple but requires a lot of duplication of code, is to use a trailing return type using decltype. However, since your tuple_maker function has more than one line of code, it is probably better to do this via another method.

Absent C++ 14 (in which you can just use auto as the return value), you can create a type generator for your function like this:

#include <tuple>
#include <type_traits>
#include <utility>

template <typename>
struct RetMaker {
    using type = std::tuple<>;
};

template <typename T, typename... Rest>
struct RetMaker<std::tuple<T, Rest...>> {
    using rest = typename RetMaker<std::tuple<Rest...>>::type;
    using type = decltype(std::tuple_cat(
                              std::declval<std::tuple<std::string, T>>(),
                              std::declval<rest>()));
};

Then the return type of your function is typename RetMaker<std::tuple<Ts...>>::type.

I should note that there are many other varied ways to achieve this, including doing do without using std::tuple_cat, but this was the first one that popped into my head that didn't require a large amount of additional typing. The essence of the decltype/std::declval bit is: Give me the type that results from calling std::tuple_cat on a std::string, T tuple and the recursive result of this applied to the rest of the tuple.

Upvotes: 1

bipll
bipll

Reputation: 11940

You should return a value from the non-final version of tuple_maker:

return tuple_maker(newtup2, vec, args...);

And from the final one:

return t;

This way the chain of calls to various instantiations of tuple_maker turns into a chain of tail calls, the last one returning accumulated value.

Upvotes: 0

Related Questions