2

I have a overloaded function which looks like:

template<typename T>
T getColumn(size_t i);

template<>
std::string getColumn<std::string>(size_t i) {
    if(i == 0)
        return "first";
    else
        return "other";
}

template<>
int getColumn<int>(size_t i) {
    return i*10;
}

// ...

Now I want to implement the function

template<typename... Values>
std::tuple<Values...> getColumns();

Which creates a tuple (for the return value) and calls getColumn for every element of the tuple (saving the return value in that element), where i is the position of the element. The code which generates the return value of getColumn is simplified (in reality it gets the value from a database).

But I have no idea how to do that.

My best try was with boost::fusion::for_each but I wasn't able to hand i down to getColumn.

Another try was with the iterators from boost::fusion, but that also didn't work:

namespace fusion = boost::fusion;
tuple<Values...> t;
auto it = fusion::begin(t);
while(it != fusion::end(t)) {
    getColumn(distance(fusion::begin(t), it), fusion::deref(it));
    it = fusion::next(it); // error: assignment not allowed
}

How can I call getColumn for every Type from Values... with the correct value for i and save the results in a std::tuple?

zigarrre
  • 70
  • 6

2 Answers2

3

You need to map each element of a parameter pack to its index within the pack - this is the typical use case for the "index sequence trick":

template <int... I> struct index_sequence {};
template <int N, int... I>
struct make_index_sequence : make_index_sequence<N-1,N-1,I...> {};
template <int... I>
struct make_index_sequence<0, I...> : index_sequence<I...> {};

template<typename... Values, int... I>
auto getColumns(index_sequence<I...>) ->
  decltype(std::make_tuple(getColumn<Values>(I)...)) {
    return std::make_tuple(getColumn<Values>(I)...);
}

template<typename... Values>
auto getColumns() ->
  decltype(getColumns<Values...>(make_index_sequence<sizeof...(Values)>())) {
    return getColumns<Values...>(make_index_sequence<sizeof...(Values)>());
}

Live demo at Coliru.

Casey
  • 41,449
  • 7
  • 95
  • 125
  • A nice side effect is that the helper function is also useful. But can it be eliminated via default args, (or in other words) making the helper function the only one? – Yakk - Adam Nevraumont Dec 28 '13 at 17:17
  • @Yakk I think not: the intermediate type is necessary from which to deduce the indices. There is no syntax specified for providing a default to a parameter pack (See [temp.param]/1). – Casey Dec 28 '13 at 17:39
  • Can `Values...` be deduced, used to produce the default argument for the second argument, from which `Is...` is then deduced? Probably not: even if it compiled, I woukd want to standard delve to make sure it was not an accidental extension. – Yakk - Adam Nevraumont Dec 29 '13 at 02:17
  • Exactly what I need, thanks! The helper function isn't a problem for me. I've integrated the functions in a class and just made the helper private. Just be careful with decltype on member functions as GCC has a bug so you'll need to explicitly use `this->` (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57543). Clang doesn't have that problem. – zigarrre Dec 29 '13 at 12:45
1

Maybe with auto:

template<typename... Values>
auto getColumns() -> decltype(std::make_tuple(getColumns<Values>()...))
{
    return std::make_tuple(getColumns<Values>()...);
}

In C++14, you'll be able to omit the -> decltype... part, since it will be deduced from the function body.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084