Skip to content

Instantly share code, notes, and snippets.

@orlp
Created October 31, 2014 04:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save orlp/d8650ed50a766ebe65e5 to your computer and use it in GitHub Desktop.
Save orlp/d8650ed50a766ebe65e5 to your computer and use it in GitHub Desktop.
#include <cstddef>
#include <iostream>
#include <stdexcept>
#include <tuple>
#include <type_traits>
#include <utility>
namespace op {
template<class T, T... I>
struct integer_sequence {
using value_type = T;
static constexpr std::size_t size() noexcept { return sizeof...(I); }
};
namespace detail {
template<class, class> struct Combine;
template<class T, T ...I, T ...J>
struct Combine<integer_sequence<T, I...>, integer_sequence<T, J...>> {
using type = integer_sequence<T, I..., (sizeof...(I) + J)...>;
};
template<class T, T N, int = (N == 0 || N == 1) ? N : 2>
struct Seq {
static_assert(N >= 0, "N must be nonnegative in make_integer_sequence<T, N>");
using type = typename Combine<typename Seq<T, N/2>::type,
typename Seq<T, N - N/2>::type>::type;
};
template<class T, T N> struct Seq<T, N, 0> { using type = integer_sequence<T>; };
template<class T, T N> struct Seq<T, N, 1> { using type = integer_sequence<T, 0>; };
}
template<class T, T N> using make_integer_sequence = typename detail::Seq<T, N>::type;
template<std::size_t... I> using index_sequence = integer_sequence<std::size_t, I...>;
template<std::size_t N> using make_index_sequence = make_integer_sequence<std::size_t, N>;
template<class... T> using index_sequence_for = make_index_sequence<sizeof...(T)>;
}
namespace detail {
template<class Func, class Tuple> struct TupleVisitRet;
template<class Func, template<class...> class Tuple, class ...Args>
struct TupleVisitRet<Func, Tuple<Args...>> {
using type = typename std::common_type<
decltype(std::declval<Func>()(std::declval<Args>()))...
>::type;
};
template<class Ret, class Tuple, class Func>
inline Ret tuple_visit_helper(const Tuple&, std::size_t, const Func&, op::index_sequence<>) {
throw std::out_of_range("tuple index is out of bounds");
}
template<class Ret, class Tuple, class Func, std::size_t N, std::size_t... Tail>
inline Ret tuple_visit_helper(const Tuple& tuple, std::size_t index, const Func& func,
op::index_sequence<N, Tail...>) {
if (index == N) return func(std::get<N>(tuple));
return tuple_visit_helper<Ret>(tuple, index, func, op::index_sequence<Tail...>());
}
}
// tuple_visit(tuple, index, func) returns func(std::get<index>(tuple)). std::out_of_range is
// thrown when the index is out of bounds of the tuple.
template<class Func, class Tuple, class Ret = typename detail::TupleVisitRet<Func, Tuple>::type>
Ret tuple_visit(const Tuple& tuple, std::size_t index, const Func& func) {
return detail::tuple_visit_helper<Ret>(tuple, index, func,
op::make_index_sequence<std::tuple_size<Tuple>::value>());
}
// visit_copy<T> returns a copy of its argument if implicitly convertible to T, throws a
// std::invalid_argument otherwise.
template<class T>
struct visit_copy {
template<class U>
typename std::enable_if<std::is_convertible<const U&, T>::value, T>::type
operator()(const U& u) const { return u; }
T operator()(...) const {
throw std::invalid_argument("visit_copy called with unconvertible type");
}
};
// visit_print()(arg) calls std::cout << arg << "\n" or throws an std::invalid_argument if doing
// so is an syntax error.
struct visit_print {
template<class T>
decltype(std::cout << std::declval<T>() << "\n", void()) // disable if not printable
operator()(const T& arg) const { std::cout << arg << "\n"; }
void operator()(...) const {
throw std::invalid_argument("visit_print called with unconvertible type");
}
};
int main(int argc, char** argv) {
struct NonPrintable { };
auto x = std::make_tuple(2, "test", 3, 8.4, NonPrintable());
auto d = tuple_visit(x, 3, visit_copy<double>());
std::cout << d << "\n";
tuple_visit(x, 3, visit_print());
std::cout << tuple_visit(x, 2, visit_copy<int>()) << "\n"; // 3
std::cout << tuple_visit(x, 1, visit_copy<std::string>()) << "\n"; // test
// can't implicitly convert from int to string
try { std::cout << tuple_visit(x, 0, visit_copy<std::string>()) << "\n"; }
catch (const std::exception& e) { std::cout << e.what() << "\n"; }
// can't print NotPrintable()
try { tuple_visit(x, 4, visit_print()); }
catch (const std::exception& e) { std::cout << e.what() << "\n"; }
// out of bounds
try { tuple_visit(x, 5, visit_print()); }
catch (const std::exception& e) { std::cout << e.what() << "\n"; }
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment