Last active
August 24, 2016 10:50
-
-
Save ecatmur/7944e0915391a3885b102fa8284ba144 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
// Based on https://www.reddit.com/r/cpp/comments/4yp7fv/c17_structured_bindings_convert_struct_to_a_tuple/ | |
#include <boost/preprocessor/repetition/repeat.hpp> | |
#include <tuple> | |
#include <type_traits> | |
#include <cassert> | |
struct any_type { template<class T> constexpr operator T(); }; | |
#pragma clang diagnostic push | |
#pragma clang diagnostic ignored "-Wmissing-field-initializers" | |
template<class T, std::size_t... I> decltype(void(T{(I, std::declval<any_type>())...}), std::true_type{}) | |
test_is_braces_constructible_n(std::index_sequence<I...>); | |
#pragma clang diagnostic pop | |
template <class, class...> std::false_type test_is_braces_constructible_n(...); | |
template <class T, std::size_t N> using is_braces_constructible_n = | |
decltype(test_is_braces_constructible_n<T>(std::make_index_sequence<N>{})); | |
template<class T, std::size_t L = 0u, std::size_t R = sizeof(T) + 1u> | |
constexpr std::size_t to_tuple_size_f(long = 0l) { | |
constexpr std::size_t M = (L + R) / 2u; | |
if constexpr (M == 0) | |
return std::is_empty<T>{} ? 0u : throw "Unable to determine number of elements"; | |
else if constexpr (L == M) | |
return M; | |
else if constexpr (is_braces_constructible_n<T, M>{}) | |
return to_tuple_size_f<T, M, R>(); | |
else | |
return to_tuple_size_f<T, L, M>(); | |
} | |
template<class T, std::size_t S = std::tuple_size<T>::value> | |
constexpr std::size_t to_tuple_size_f(int) { return S; } | |
template<class T> using to_tuple_size = std::integral_constant<std::size_t, to_tuple_size_f<T>(0)>; | |
#ifndef TO_TUPLE_MAX | |
#define TO_TUPLE_MAX 64 | |
#endif | |
template<class T> | |
auto to_tuple_impl(T&&, std::integral_constant<std::size_t, 0>) noexcept { | |
return std::make_tuple(); | |
} | |
template<class T> | |
auto to_tie_impl(T&, std::integral_constant<std::size_t, 0>) noexcept { | |
return std::make_tuple(); | |
} | |
#define TO_TUPLE_P(Z,N,_) , p ## N | |
#define TO_TUPLE_SPECIALIZATION(Z,N,_) \ | |
template<class T> \ | |
auto to_tuple_impl(T&& object, std::integral_constant<std::size_t, N + 1>) noexcept { \ | |
auto&& [p BOOST_PP_REPEAT_ ## Z(N, TO_TUPLE_P, nil)] = object; \ | |
return std::make_tuple(p BOOST_PP_REPEAT_ ## Z(N, TO_TUPLE_P, nil)); \ | |
} \ | |
template<class T> \ | |
auto to_tie_impl(T& object, std::integral_constant<std::size_t, N + 1>) noexcept { \ | |
auto& [p BOOST_PP_REPEAT_ ## Z(N, TO_TUPLE_P, nil)] = object; \ | |
return std::tie(p BOOST_PP_REPEAT_ ## Z(N, TO_TUPLE_P, nil)); \ | |
} | |
BOOST_PP_REPEAT(TO_TUPLE_MAX, TO_TUPLE_SPECIALIZATION, nil) | |
#undef TO_TUPLE_SPECIALIZATION | |
#undef TO_TUPLE_P | |
template<class T, class = struct current_value, std::size_t = TO_TUPLE_MAX, class = struct required_value, std::size_t N> | |
auto to_tuple_impl(T&&, std::integral_constant<std::size_t, N>) noexcept { | |
static_assert(N <= TO_TUPLE_MAX, "Please increase TO_TUPLE_MAX"); | |
} | |
template<class T, class = struct current_value, std::size_t = TO_TUPLE_MAX, class = struct required_value, std::size_t N> | |
auto to_tie_impl(T&&, std::integral_constant<std::size_t, N>) noexcept { | |
static_assert(N <= TO_TUPLE_MAX, "Please increase TO_TUPLE_MAX"); | |
} | |
template<class T, class = std::enable_if_t<std::is_class<T>::value>> | |
auto to_tuple(T&& object) noexcept { | |
return to_tuple_impl(std::forward<T>(object), to_tuple_size<std::decay_t<T>>{}); | |
} | |
template<class T, class = std::enable_if_t<std::is_class<T>::value>> | |
auto to_tie(T& object) noexcept { | |
return to_tie_impl(object, to_tuple_size<std::decay_t<T>>{}); | |
} | |
int main() { | |
{ | |
struct S { | |
int p1; | |
double p2; | |
}; | |
auto t = to_tuple(S{1, 2.0}); | |
static_assert(std::is_same<std::tuple<int, double>, decltype(t)>{}); | |
assert(1 == std::get<0>(t)); | |
assert(2.0 == std::get<1>(t)); | |
S s{1, 2.0}; | |
auto r = to_tie(s); | |
static_assert(std::is_same<std::tuple<int&, double&>, decltype(r)>{}); | |
assert(&s.p1 == &std::get<0>(r)); | |
assert(&s.p2 == &std::get<1>(r)); | |
} | |
{ | |
struct S { | |
struct nested { } p1; | |
int p2; | |
int p3; | |
S* p4; | |
}; | |
auto t = to_tuple(S{S::nested{}, 42, 87, nullptr}); | |
static_assert(std::is_same<std::tuple<S::nested, int, int, S*>, decltype(t)>{}); | |
assert(42 == std::get<1>(t)); | |
assert(87 == std::get<2>(t)); | |
assert(nullptr == std::get<3>(t)); | |
S s{S::nested{}, 42, 87, nullptr}; | |
auto r = to_tie(s); | |
static_assert(std::is_same<std::tuple<S::nested&, int&, int&, S*&>, decltype(r)>{}); | |
assert(&s.p1 == &std::get<0>(r)); | |
assert(&s.p2 == &std::get<1>(r)); | |
assert(&s.p3 == &std::get<2>(r)); | |
assert(&s.p4 == &std::get<3>(r)); | |
} | |
{ | |
using S = std::tuple<int, double>; | |
auto t = to_tuple(S{1, 2.0}); | |
static_assert(std::is_same<std::tuple<int, double>, decltype(t)>{}); | |
assert(1 == std::get<0>(t)); | |
assert(2.0 == std::get<1>(t)); | |
S s{1, 2.0}; | |
auto r = to_tie(s); | |
static_assert(std::is_same<std::tuple<int&, double&>, decltype(r)>{}); | |
assert(&std::get<0>(s) == &std::get<0>(r)); | |
assert(&std::get<1>(s) == &std::get<1>(r)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment