Skip to content

Instantly share code, notes, and snippets.

@AltimorTASDK
Last active August 5, 2022 10:26
Show Gist options
  • Save AltimorTASDK/63208b3f7d1c7a7da7941b3bb8205ae3 to your computer and use it in GitHub Desktop.
Save AltimorTASDK/63208b3f7d1c7a7da7941b3bb8205ae3 to your computer and use it in GitHub Desktop.
#include <algorithm>
#include <type_traits>
#include <utility>
using std::size_t;
template<typename Target>
class field_list {
template<size_t N>
struct reader {
friend constexpr auto query(reader<N>);
};
template<size_t N, typename T>
struct writer {
friend constexpr auto query(reader<N>) { return std::type_identity<T>{}; }
};
public:
struct nop {
using target = Target;
template<size_t N, typename T>
struct writer {};
template<size_t N, typename T>
static constexpr auto set() { return writer<N, T>{}; }
};
using target = Target;
template<size_t N = 0, auto = []{}>
static constexpr auto size()
{
if constexpr (requires { query(reader<N>{}); })
return size<N + 1>();
else
return N;
}
template<size_t N>
using get = typename decltype(query(reader<N>{}))::type;
template<size_t N, typename T>
static constexpr auto set() { return writer<N, T>{}; }
};
template<size_t N>
struct dummy_probe {
template<typename T> constexpr operator T&() const&& noexcept;
template<typename T> constexpr operator T&&() const&& noexcept;
};
template<typename List, size_t N>
struct prvalue_probe {
template<typename T>
constexpr operator T() const&& noexcept((List::template set<N, T>(), true));
};
template<typename List, size_t N>
struct lvalue_probe {
template<typename T>
constexpr operator T&() const&& noexcept((List::template set<N, T&>(), true));
};
template<typename List, size_t N>
struct xvalue_probe {
template<typename T>
constexpr operator T&&() const&& noexcept((List::template set<N, T&&>(), true));
};
template<typename Target, size_t N>
constexpr auto struct_size_check = []<size_t ...I>(std::index_sequence<I...>) {
return requires { Target { std::declval<dummy_probe<I>>()... }; };
}(std::make_index_sequence<N>());
template<typename Target, size_t N = 0> requires requires {
requires struct_size_check<Target, N>;
requires !struct_size_check<Target, N+1>;
} constexpr auto struct_size() { return N; }
template<typename Target, size_t N = 0>
constexpr auto struct_size() { return struct_size<Target, N+1>(); }
template<typename List, size_t N, template<typename, size_t> typename Probe>
constexpr auto probe_field_check()
{
using target = typename List::target;
constexpr auto remaining_fields = std::max(struct_size<target>(), N + 1) - N - 1;
return []<size_t ...I, size_t ...J>(std::index_sequence<I...>, std::index_sequence<J...>) {
return requires {
target { dummy_probe<I>{}..., Probe<List, N>{}, dummy_probe<J>{}... };
};
}(std::make_index_sequence<N>(), std::make_index_sequence<remaining_fields>());
}
template<typename List, size_t N> requires requires {
requires probe_field_check<typename List::nop, N, lvalue_probe>();
requires !probe_field_check<typename List::nop, N, prvalue_probe>();
} constexpr void probe_field_impl() noexcept(probe_field_check<List, N, lvalue_probe>()) {}
template<typename List, size_t N> requires requires {
requires probe_field_check<typename List::nop, N, prvalue_probe>();
requires probe_field_check<typename List::nop, N, lvalue_probe>();
} constexpr void probe_field_impl() noexcept(probe_field_check<List, N, prvalue_probe>()) {}
template<typename List, size_t N> requires requires {
requires probe_field_check<typename List::nop, N, xvalue_probe>();
requires !probe_field_check<typename List::nop, N, lvalue_probe>();
} constexpr void probe_field_impl() noexcept(probe_field_check<List, N, xvalue_probe>()) {}
template<typename Target>
constexpr void probe_fields()
{
[]<size_t ...I>(std::index_sequence<I...>) {
(probe_field_impl<field_list<Target>, I>(), ...);
}(std::make_index_sequence<struct_size<Target>()>());
}
int main()
{
struct S {
int my;
int &super;
int &&laser;
float piss;
};
static_assert(struct_size<S>() == 4);
probe_fields<S>();
static_assert(std::is_same_v<field_list<S>::get<0>, int>);
static_assert(std::is_same_v<field_list<S>::get<1>, int&>);
static_assert(std::is_same_v<field_list<S>::get<2>, int&&>);
static_assert(std::is_same_v<field_list<S>::get<3>, float>);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment