While Boost.Fusion is a good solution, I thought I'd add that in C++11 you could use std::tuple
like this:
template <unsigned ... indices>
struct sequence
{
typedef sequence type;
};
template <unsigned size, unsigned ... indices>
struct static_range : static_range<size-1,size-1,indices...> {};
template <unsigned ... indices>
struct static_range<0, indices...> : sequence<indices...> {};
template <class Function, class Tuple, unsigned ... indices>
auto transform_impl(const Tuple & t, Function f, sequence<indices...>) ->
std::tuple<decltype(f(std::get<indices>(t)))...>
{
return std::make_tuple(f(std::get<indices>(t))...);
}
template <class Function, class Tuple>
auto transform_tuple(const Tuple & t, Function f) ->
decltype(transform_impl(t, f, static_range<std::tuple_size<Tuple>::value>()))
{
return transform_impl(t, f, static_range<std::tuple_size<Tuple>::value>());
}
The sequence
/static_range
classes have been invaluable in my code in expanding classes (not just std::tuple
s) by indices so that I can std::get
them.
Other than that, I think the code is fairly straightforward, it should be noted however that with this method the order in which f
is invoked on each tuple element is undefined.
Usage would look like:
std::tuple<char, short, int, long long> t;
struct addone
{ template <class T> auto operator()(T t) -> decltype(t+1) {return t + 1;}};
auto t2 = transform_tuple(t, addone());
The resulting tuple will not have the same types as the input tuple due to integral promotion, each will have type typename std::common_type<T,int>::type
.