1
typedef std::tuple< int, double > Tuple;
Tuple t;
int a = std::get<0>(t);
double b = std::get<1>(t);

for( size_t i = 0; i < std::tuple_size<Tuple>::value; i++ ) {
   std::tuple_element<i,Tuple>::type v = std::get<i>(t);// will not compile because i must be known at compile time
}

I know it is possible to write code for get std::get working (see for example iterate over tuple ), is it possible to get std::tuple_element working too?

Some constraints (they can be relaxed):

no variadic templates, no Boost

Community
  • 1
  • 1
Alessandro Jacopson
  • 18,047
  • 15
  • 98
  • 153
  • 6
    I don't understand: the point in having compile-time indices for `std::tuple` is type safety. Querying an element whose index is known at run time would imply that all the members of the tuple have the same type. In this case you would use an array (preferably `std::array`). – Alexandre C. Nov 07 '11 at 09:09
  • 1
    Why are you saying *no variadic templates*? `std::tuple` *is* a variadic template. – Björn Pollex Nov 07 '11 at 09:17
  • 1
    @BjörnPollex Maybe I have an half-baked tuple ;-) As of november 2011 the compiler in Microsoft Visual Studio 2010 does not implement the variadic templates. http://connect.microsoft.com/VisualStudio/feedback/details/463677/support-variadic-templates – Alessandro Jacopson Nov 07 '11 at 09:26
  • Question is pointless since you can't do much of anything with `v` since you don't know it's type. – Mooing Duck Nov 14 '12 at 23:18

4 Answers4

7

C++ is a compile-time typed language. You cannot have a type that the C++ compiler cannot determine at compile-time.

You can use polymorphism of various forms to work around that. But at the end of the day, every variable must have a well-defined type. So while you can use Boost.Fusion algorithms to iterate over variables in a tuple, you cannot have a loop where each execution of the loop may use a different type than the last.

The only reason Boost.Fusion can get away with it is because it doesn't use a loop. It uses template recursion to "iterate" over each element and call your user-provided function.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
5

If you want to do without boost, the answers to iterate over tuple already tell you everything you need to know. You have to write a compile-time for_each loop (untested).

template<class Tuple, class Func, size_t i>
void foreach(Tuple& t, Func fn) {
    // i is defined at compile-time, so you can write:
    std::tuple_element<i, Tuple> te = std::get<i>(t);
    fn(te);
    foreach<i-1>(t, fn);
}

template<class Tuple, class Func>
void foreach<0>(Tuple& t, Func fn) { // template specialization
    fn(std::get<0>(t)); // no further recursion
}

and use it like that:

struct SomeFunctionObject {
    void operator()( int i ) const {}
    void operator()( double f ) const {}
};

foreach<std::tuple_size<Tuple>::value>(t, SomeFunctionObject());

However, if you want to iterate over members of a tuple, Boost.Fusion really is the way to go.

#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <boost/fusion/adapted/boost_tuple.hpp>

and in your code write:

boost::for_each(t, SomeFunctionObject());

This an example for boost::tuple. There is an adapter for boost::fusion to work with the std::tuple here: http://groups.google.com/group/boost-list/browse_thread/thread/77622e41af1366af/

Community
  • 1
  • 1
Sebastian
  • 4,802
  • 23
  • 48
1

Here is my tuple foreach/transformation function:

#include <cstddef>
#include <tuple>
#include <type_traits>

template<size_t N>
struct tuple_foreach_impl {
    template<typename T, typename C>
    static inline auto call(T&& t, C&& c)
        -> decltype(::std::tuple_cat(
            tuple_foreach_impl<N-1>::call(
                ::std::forward<T>(t), ::std::forward<C>(c)
            ),
            ::std::make_tuple(c(::std::get<N-1>(::std::forward<T>(t))))
        ))
    {
        return ::std::tuple_cat(
            tuple_foreach_impl<N-1>::call(
                ::std::forward<T>(t), ::std::forward<C>(c)
            ),
            ::std::make_tuple(c(::std::get<N-1>(::std::forward<T>(t))))
        );
    }
};

template<>
struct tuple_foreach_impl<0> {
    template<typename T, typename C>
    static inline ::std::tuple<> call(T&&, C&&) { return ::std::tuple<>(); }
};

template<typename T, typename C>
auto tuple_foreach(T&& t, C&& c)
    -> decltype(tuple_foreach_impl<
        ::std::tuple_size<typename ::std::decay<T>::type
    >::value>::call(std::forward<T>(t), ::std::forward<C>(c)))
{
    return tuple_foreach_impl<
        ::std::tuple_size<typename ::std::decay<T>::type>::value
    >::call(::std::forward<T>(t), ::std::forward<C>(c));
}

The example usage uses the following utility to allow printing tuples to ostreams:

#include <cstddef>
#include <ostream>
#include <tuple>
#include <type_traits>

template<size_t N>
struct tuple_print_impl {
    template<typename S, typename T>
    static inline void print(S& s, T&& t) {
        tuple_print_impl<N-1>::print(s, ::std::forward<T>(t));
        if (N > 1) { s << ',' << ' '; }
        s << ::std::get<N-1>(::std::forward<T>(t));
    }
};

template<>
struct tuple_print_impl<0> {
    template<typename S, typename T>
    static inline void print(S&, T&&) {}
};

template<typename S, typename T>
void tuple_print(S& s, T&& t) {
    s << '(';
    tuple_print_impl<
        ::std::tuple_size<typename ::std::decay<T>::type>::value
    >::print(s, ::std::forward<T>(t));
    s << ')';
}

template<typename C, typename... T>
::std::basic_ostream<C>& operator<<(
    ::std::basic_ostream<C>& s, ::std::tuple<T...> const& t
) {
    tuple_print(s, t);
    return s;
}

And finally, here is the example usage:

#include <iostream>

using namespace std;

struct inc {
    template<typename T>
    T operator()(T const& val) { return val+1; }
};

int main() {
    // will print out "(7, 4.2, z)"
    cout << tuple_foreach(make_tuple(6, 3.2, 'y'), inc()) << endl;
    return 0;
}

Note that the callable object is constructed so that it can hold state if needed. For example, you could use the following to find the last object in the tuple that can be dynamic casted to T:

template<typename T>
struct find_by_type {
    find() : result(nullptr) {}
    T* result;
    template<typename U>
    bool operator()(U& val) {
        auto tmp = dynamic_cast<T*>(&val);
        auto ret = tmp != nullptr;
        if (ret) { result = tmp; }
        return ret;
    }
};

Note that one shortcoming of this is that it requires that the callable returns a value. However, it wouldn't be that hard to rewrite it to detect whether the return type is void for a give input type, and then skip that element of the resulting tuple. Even easier, you could just remove the return value aggregation stuff altogether and simply use the foreach call as a tuple modifier.

Edit: I just realized that the tuple writter could trivially be written using the foreach function (I have had the tuple printing code for much longer than the foreach code).

template<typename T>
struct tuple_print {
    print(T& s) : _first(true), _s(&s) {}
    template<typename U>
    bool operator()(U const& val) {
        if (_first) { _first = false; } else { (*_s) << ',' << ' '; }
        (*_s) << val;
        return false;
    }
private:
    bool _first;
    T* _s;
};

template<typename C, typename... T>
::std::basic_ostream<C> & operator<<(
    ::std::basic_ostream<C>& s, ::std::tuple<T...> const& t
) {
    s << '(';
    tuple_foreach(t, tuple_print< ::std::basic_ostream<C>>(s));
    s << ')';
    return s;
}
DRayX
  • 1,043
  • 2
  • 12
  • 19
1

No, this is not possible the way you describe it. Basically, you'd have to write your code for every possible runtime-value of i and then use some dispatching-logic (e.g. switch(i)) to run the correct code based on the actual runtime-value of i.

In practice, it might be possible to generate the code for the different values of i with templates, but I am not really sure how to do this, and whether it would be practical. What you are describing sounds like a flawed design.

Björn Pollex
  • 75,346
  • 28
  • 201
  • 283