Skip to content

Instantly share code, notes, and snippets.

@ecatmur
Last active August 24, 2016 10:50
Show Gist options
  • Save ecatmur/7944e0915391a3885b102fa8284ba144 to your computer and use it in GitHub Desktop.
Save ecatmur/7944e0915391a3885b102fa8284ba144 to your computer and use it in GitHub Desktop.
// 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