3

If I have this tuple type:

std::tuple<int, string, std::tuple<...>, int>

How can I traverse it? I've been able to write functions that traverse a flat tuple, but not with nested tuples.

The problem seems to be for any implementation of a templated function or type that handles all the conceivable types in a nested tuple, there must be this case:

template<typename T>
void somefunc(T t)
{
    // Do something to t
}

Which ends up being the best choice for overload resolution for every type, and you can't recursively traverse the tuple because you lose the information that it's a tuple. If you try to write a class or function that tries to determine whether it's a tuple or not, you still run in to the "false" case which has the same problem as above in that it ends up matching for every type and the tuple specialized version gets overlooked.

Is there a way I'm not aware of to check whether something is a tuple?

Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
  • Can you explain in (pseudo-)code what you are trying to do? The question is not really clear to me. – Daniel Frey Oct 26 '13 at 11:09
  • 2
    Normal overloading should work just fine. What's the problem? – Kerrek SB Oct 26 '13 at 11:15
  • You want to run `some_func` once for each element in the tuple? But if a sub-element *is* a tuple, you want to run `some_func` over each element of that instead? Recursively all the way down? – Aaron McDaid Oct 26 '13 at 12:06

3 Answers3

2

Simply overload the someFunc() function for tuples and non-tuple types:

template<typename... Ts>
void someFunc( const std::tuple<Ts...>& tuple )
{
    /* traverse the elements of the tuple */
    traverse_tuple( tuple );
}

template<typename T>
void someFunc( const T& value )
{
    /* do something with the value */
}

Where traverse_tuple is the same function you have implemented to traverse non-nested (flat) tuples. It calls someFunc() for each member of the tuple.
For an implementation of a tuple traversing function, you could check this answer.

Community
  • 1
  • 1
Manu343726
  • 13,969
  • 4
  • 40
  • 75
0

If you want to check whether or not a type T is a tuple, this should work:

#include <type_traits>
#include <tuple>

template<typename> struct is_tuple : std::false_type {};
template<typename... Ts> struct is_tuple<std::tuple<Ts...>> : std::true_type {};

static_assert( is_tuple<std::tuple<int>>::value, "Oops" );
static_assert( !is_tuple<int>::value, "Oops" );

int main() {}
Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
0

You may specialize struct/class instead of specializing function: adapt the following to your case (http://ideone.com/VgIJfj) :

namespace details
{

template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
  for_each(const std::tuple<Tp...> &, FuncT)
  { }

template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
  for_each(const std::tuple<Tp...>& t, FuncT f)
  {
    f(std::get<I>(t));
    for_each<I + 1, FuncT, Tp...>(t, f);
  }

template <typename T>
struct traverseType
{
    void operator () (const T& t) const
    {
        std::cout << "it is a generic T:" << t << std::endl;
    }
};

template <>
struct traverseType<int>
{
    void operator () (int i) const
    {
        std::cout << "it is a int:" << i << std::endl;
    }
};

// needed by the for_each.
struct traverseTypeCaller
{
    template <typename T>
    void operator () (const T& t) const
    {
         details::traverseType<T>()(t);
    }
};

template <typename ...T>
struct traverseType<std::tuple<T...>>
{
    void operator () (const std::tuple<T...>& t) const
    {
        std::cout << "it is a tuple:" << std::endl;
        for_each(t, traverseTypeCaller());
    }
};

}

template <typename T>
void traverseType(const T& t)
{
    details::traverseTypeCaller()(t);
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302