Skip to content

Instantly share code, notes, and snippets.

@sjswitzer
Last active December 30, 2017 23:19
Show Gist options
  • Save sjswitzer/987bfdc3cf33776eb66a55c2eed882a1 to your computer and use it in GitHub Desktop.
Save sjswitzer/987bfdc3cf33776eb66a55c2eed882a1 to your computer and use it in GitHub Desktop.
C++ tuple utilities. Shows very simple way to recurse over tuple elements.
#include <iostream>
#include <tuple>
namespace detail {
template <size_t... N> struct Indices { };
template <typename T, size_t I>
inline void print_tuple(std::ostream &out, const T &tup, Indices<0, I>) { }
template <typename T, size_t N, size_t I>
inline void print_tuple(std::ostream &out, const T &tup, Indices<N, I>)
{
if (I > 0)
out << ", ";
out << std::get<I>(tup);
print_tuple(out, tup, Indices<N-1, I+1>());
}
}
template <typename... Args>
std::ostream &operator<<(std::ostream &out, const std::tuple<Args...> &tup)
{
out << "(";
detail::print_tuple(out, tup, detail::Indices<std::tuple_size<std::tuple<Args...>>::value, 0>());
return out << ")";
}
namespace detail {
// Copies elements N elements beginning at given indices
template <typename T1, typename T2, size_t I1, size_t I2>
inline void tuple_copy(T1 &to, const T2 &from, Indices<0, I1, I2>) { }
template <typename T1, typename T2, size_t N, size_t I1, size_t I2>
inline void tuple_copy(T1 &to, const T2 &from, Indices<N, I1, I2>)
{
std::get<I1>(to) = std::get<I2>(from);
tuple_copy(to, from, Indices<N-1, I1+1, I2+1>());
}
}
template <typename T1, typename... Rest> // more complex than necessary, but better to fail on match criteria
inline auto tuple_head(const std::tuple<T1, Rest...> &from) -> T1
{
return std::get<0>(from); // trivial, but symmetry demands it
}
template <typename T1, typename... Rest>
inline auto tuple_tail(const std::tuple<T1, Rest...> &from) -> std::tuple<Rest...>
{
std::tuple<Rest...> to;
detail::tuple_copy(to, from, detail::Indices<std::tuple_size<decltype(to)>::value, 0, 1>());
return to;
}
template <typename T1, typename... Rest> // more complex than necessary, but better to fail on match criteria
inline auto tuple_cons(const T1 &val, const std::tuple<Rest...> &from) -> std::tuple<T1, Rest...>
{
std::tuple<T1, Rest...> to;
std::get<0>(to) = val;
detail::tuple_copy(to, from, detail::Indices<std::tuple_size<decltype(to)>::value-1, 1, 0>());
return to;
}
int main(int argc, const char * argv[])
{
auto &cout = std::cout;
std::tuple<> t0;
cout << t0 << "\n";
cout << std::make_tuple(1, 'a', false, 1.2) << "\n";
std::tuple<int, bool, double, char> t1 = { 1, false, 1.2, 'a'};
cout << t1 << ' ' << tuple_head(t1) << ' ' << tuple_tail(t1) << "\n";
auto t2 = tuple_tail(t1);
cout << t2 << ' ' << tuple_head(t2) << ' ' << tuple_tail(t2) << "\n";
auto t3 = tuple_tail(t2);
cout << t3 << ' ' << tuple_head(t3) << ' ' << tuple_tail(t3) << "\n";
auto t4 = tuple_cons(99, t2);
cout << t4 << ' ' << tuple_head(t4) << ' '<< tuple_tail(t4) << "\n";
return 0;
}
@sjswitzer
Copy link
Author

sjswitzer commented Dec 30, 2017

The main thing to note here is the Indices template type that can be used to pass arbitrarily many index parameters easily.

It's much simpler than other approaches I've seen. Other implementations tend to use std::integral_type repeatedly, which is verbose and, well, repetitive.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment