Created
October 31, 2014 04:34
-
-
Save orlp/d8650ed50a766ebe65e5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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