#pragma once #include #include #include #include #include "Support/TypeTraits.h" namespace clice::refl { namespace impl { struct Any { consteval Any(std::size_t); template consteval operator T() const; }; template consteval auto test() { return [](std::index_sequence) { return requires { T{Any(I)...}; }; }(std::make_index_sequence{}); } template consteval auto member_count() { if constexpr(test() && !test()) { return N; } else { return member_count(); } } template struct wrapper { T value; constexpr wrapper(T value) : value(value) {} }; template union storage_t { char dummy; T value; storage_t() {} ~storage_t() {} }; template inline storage_t storage; template consteval auto member_name() { std::string_view name = std::source_location::current().function_name(); #if __GNUC__ && (!__clang__) && (!_MSC_VER) std::size_t start = name.rfind("::") + 2; std::size_t end = name.rfind(')'); name = name.substr(start, end - start); #elif __clang__ std::size_t start = name.rfind(".") + 1; std::size_t end = name.rfind('}'); name = name.substr(start, end - start); #elif _MSC_VER std::size_t start = name.rfind("->") + 2; std::size_t end = name.rfind('}'); name = name.substr(start, end - start); #else static_assert(false, "Not supported compiler"); #endif if(name.rfind("::") != std::string_view::npos) { name = name.substr(name.rfind("::") + 2); } return name; } template constexpr inline auto to_string_literal_impl = [] { if constexpr(N == 0) { return std::array{'0', '\0'}; } else { constexpr auto length = [] { std::size_t result = 0; for(std::size_t n = N; n; n /= 10) { ++result; } return result; }(); std::array result = {}; std::size_t n = N; for(std::size_t i = length; i; n /= 10, i--) { result[i - 1] = '0' + n % 10; } result[length] = '\0'; return result; } }(); } // namespace impl template constexpr std::string_view to_string_literal() { return {impl::to_string_literal_impl.data()}; } template struct Struct; /// To check if the type is reflectable_struct. template concept reflectable_struct = Struct>::reflectable_struct; /// Get the member count of the type. template constexpr static std::size_t member_count() { return Struct>::member_count; } /// Get the all member names of the type. template constexpr static auto& member_names() { return Struct>::member_names; } /// Get the member name of the type at index N. template constexpr static std::string_view member_name() { return member_names()[N]; } /// Get the member types of the type. template constexpr decltype(auto) member_value(T&& object) { return *std::get(Struct>::collect_members(object)); } template using member_types = tuple_to_list_t::collect_members(std::declval())), std::remove_pointer_t>; template using member_type = std::tuple_element_t::to_tuple>; template concept TupleLike = requires { std::tuple_size::value; }; /// Specialize for aggregate class. template requires std::is_aggregate_v && (!TupleLike) struct Struct { constexpr inline static bool reflectable_struct = true; constexpr inline static auto member_count = impl::member_count(); template constexpr static auto collect_members(Object&& object) { // clang-format off if constexpr (member_count == 0) { return std::tuple{}; } else if constexpr (member_count == 1) { auto&& [e1] = object; return std::tuple{ &e1 }; } else if constexpr (member_count == 2) { auto&& [e1, e2] = object; return std::tuple{ &e1, &e2 }; } else if constexpr (member_count == 3) { auto&& [e1, e2, e3] = object; return std::tuple{ &e1, &e2, &e3 }; } else if constexpr (member_count == 4) { auto&& [e1, e2, e3, e4] = object; return std::tuple{ &e1, &e2, &e3, &e4 }; } else if constexpr (member_count == 5) { auto&& [e1, e2, e3, e4, e5] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5 }; } else if constexpr (member_count == 6) { auto&& [e1, e2, e3, e4, e5, e6] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6 }; } else if constexpr (member_count == 7) { auto&& [e1, e2, e3, e4, e5, e6, e7] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7 }; } else if constexpr (member_count == 8) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8 }; } else if constexpr (member_count == 9) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9 }; } else if constexpr (member_count == 10) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10 }; } else if constexpr (member_count == 11) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11 }; } else if constexpr (member_count == 12) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12 }; } else if constexpr (member_count == 13) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13 }; } else if constexpr (member_count == 14) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14 }; } else if constexpr (member_count == 15) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15 }; } else if constexpr (member_count == 16) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16 }; } else if constexpr (member_count == 17) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17 }; } else if constexpr (member_count == 18) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18 }; } else if constexpr (member_count == 19) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19 }; } else if constexpr (member_count == 20) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20 }; } else if constexpr (member_count == 21) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21 }; } else if constexpr (member_count == 22) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22 }; } else if constexpr (member_count == 23) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23 }; } else if constexpr (member_count == 24) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24 }; } else if constexpr (member_count == 25) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24, &e25 }; } else if constexpr (member_count == 26) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24, &e25, &e26 }; } else if constexpr (member_count == 27) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24, &e25, &e26, &e27 }; } else if constexpr (member_count == 28) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24, &e25, &e26, &e27, &e28 }; } else if constexpr (member_count == 29) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24, &e25, &e26, &e27, &e28, &e29 }; } else if constexpr (member_count == 30) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24, &e25, &e26, &e27, &e28, &e29, &e30 }; } else if constexpr (member_count == 31) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24, &e25, &e26, &e27, &e28, &e29, &e30, &e31 }; } else if constexpr (member_count == 32) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24, &e25, &e26, &e27, &e28, &e29, &e30, &e31, &e32 }; } else if constexpr (member_count == 33) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32, e33] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24, &e25, &e26, &e27, &e28, &e29, &e30, &e31, &e32, &e33 }; } else if constexpr (member_count == 34) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32, e33, e34] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24, &e25, &e26, &e27, &e28, &e29, &e30, &e31, &e32, &e33, &e34 }; } else if constexpr (member_count == 35) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32, e33, e34, e35] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24, &e25, &e26, &e27, &e28, &e29, &e30, &e31, &e32, &e33, &e34, &e35 }; } else if constexpr (member_count == 36) { auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32, e33, e34, e35, e36] = object; return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24, &e25, &e26, &e27, &e28, &e29, &e30, &e31, &e32, &e33, &e34, &e35, &e36 }; } else { // For counts greater than 36, trigger a compile-time error static_assert(member_count <= 36, "Not supported member count"); } // clang-format on } constexpr inline static auto member_names = [](std::index_sequence) { if constexpr(member_count == 0) { return std::array{}; } else { constexpr auto members = collect_members(impl::storage.value); return std::array{impl::member_name(members)>()...}; } }(std::make_index_sequence{}); }; template struct Inheritance : Ts... {}; /// Use to define a reflectable_struct struct with inheritance. #define inherited_struct(name, ...) \ struct name##Body; \ using name = clice::refl::Inheritance<__VA_ARGS__, name##Body>; \ struct name##Body template struct Struct> { constexpr inline static bool reflectable_struct = (refl::reflectable_struct && ...); constexpr static std::size_t member_count = (Struct::member_count + ...); template constexpr static auto collect_members(Object&& object) { if constexpr(std::is_const_v>) { return std::tuple_cat(Struct::collect_members(static_cast(object))...); } else { return std::tuple_cat(Struct::collect_members(static_cast(object))...); } } constexpr inline static auto member_names = [](std::index_sequence) { if constexpr(member_count == 0) { return std::array{}; } else { constexpr auto members = collect_members(impl::storage>.value); return std::array{impl::member_name(members)>()...}; } }(std::make_index_sequence{}); }; template struct Struct { constexpr inline static bool reflectable_struct = true; constexpr inline static std::size_t member_count = std::tuple_size_v; template constexpr static auto collect_members(Object&& object) { return std::apply([](auto&&... args) { return std::tuple{&args...}; }, object); } constexpr inline static auto member_names = [](std::index_sequence) { if constexpr(member_count == 0) { return std::array{}; } else { return std::array{to_string_literal()...}; } }(std::make_index_sequence{}); }; /// Turn the return value of the callable to bool. template constexpr auto foldable(const Callable& callable) { return [&](auto&&... args) { using Ret = std::invoke_result_t; if constexpr(std::is_void_v) { callable(args...); return true; } else { return bool(callable(args...)); } }; } template constexpr bool foreach(Object&& object, const Callback& callback) { auto foldable = refl::foldable(callback); return [&](std::index_sequence) { return (foldable(refl::member_name(), refl::member_value(object)) && ...); }(std::make_index_sequence()>{}); } /// Invoke callback for each member of lhs and rhs, return false /// in callback to abort the iteration. Return true if all members are visited. template constexpr bool foreach(LHS&& lhs, RHS&& rhs, const Callback& callback) { static_assert(member_count() == member_count(), "Member count mismatch"); auto foldable = refl::foldable(callback); return [&](std::index_sequence) { return (foldable(refl::member_value(lhs), refl::member_value(rhs)) && ...); }(std::make_index_sequence()>{}); } } // namespace clice::refl