#pragma once // support basic reflection through template meta programming #include #include #include #include #include namespace clice::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 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(')'); return name.substr(start, end - start); #elif __clang__ std::size_t start = name.rfind(".") + 1; std::size_t end = name.rfind('}'); return name.substr(start, end - start); #elif _MSC_VER std::size_t start = name.rfind("->") + 2; std::size_t end = name.rfind('}'); return name.substr(start, end - start); #else static_assert(false, "Not supported compiler"); #endif } template constexpr auto collcet_members(Object&& object) { // clang-format off if constexpr(count == 0) { return std::tuple{}; } else if constexpr(count == 1) { auto&& [a] = object; return std::tuple{&a}; } else if constexpr(count == 2) { auto&& [a, b] = object; return std::tuple{&a, &b}; } else if constexpr(count == 3) { auto&& [a, b, c] = object; return std::tuple{&a, &b, &c}; } else if constexpr(count == 4) { auto&& [a, b, c, d] = object; return std::tuple{&a, &b, &c, &d}; } else if constexpr(count == 5) { auto&& [a, b, c, d, e] = object; return std::tuple{&a, &b, &c, &d, &e}; } else if constexpr(count == 6) { auto&& [a, b, c, d, e, f] = object; return std::tuple{&a, &b, &c, &d, &e, &f}; } else if constexpr(count == 7) { auto&& [a, b, c, d, e, f, g] = object; return std::tuple{&a, &b, &c, &d, &e, &f, &g}; } else if constexpr(count == 8) { auto&& [a, b, c, d, e, f, g, h] = object; return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h}; } else if constexpr(count == 9) { auto&& [a, b, c, d, e, f, g, h, i] = object; return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i}; } else if constexpr(count == 10) { auto&& [a, b, c, d, e, f, g, h, i, j] = object; return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j}; } else if constexpr(count == 11) { auto&& [a, b, c, d, e, f, g, h, i, j, k] = object; return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k}; } else if constexpr(count == 12) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l] = object; return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l}; } else if constexpr(count == 13) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m] = object; return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m}; } else if constexpr(count == 14) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n] = object; return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n}; } else if constexpr(count == 15) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o] = object; return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o}; } else if constexpr(count == 16) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p] = object; return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p}; } else if constexpr(count == 17) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q] = object; return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q}; } else if constexpr(count == 18) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r] = object; return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r}; } else if constexpr(count == 19) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s] = object; return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s}; } else if constexpr(count == 20) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t] = object; return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t}; } else if constexpr(count == 21) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u] = object; return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u}; } else if constexpr(count == 22) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v] = object; return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v}; } else if constexpr(count == 23) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w] = object; return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w}; } else if constexpr(count == 24) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x] = object; return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x}; } else if constexpr(count == 25) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y] = object; return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y}; } else if constexpr(count == 26) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z] = object; return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z}; } else if constexpr(count == 27) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, _0] = object; return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z, &_0}; } else if constexpr(count == 28) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, _0, _1] = object; return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z, &_0, &_1}; } else if constexpr(count == 29) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, _0, _1, _2] = object; return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z, &_0, &_1, &_2}; } else if constexpr(count == 30) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, _0, _1, _2, _3] = object; return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z, &_0, &_1, &_2, &_3}; } else { static_assert(count <= 30, "Not supported member count"); } // clang-format on } template struct Storage { inline static T value; }; template consteval auto enum_name() { std::string_view name = std::source_location::current().function_name(); #if __GNUC__ || __clang__ std::size_t start = name.find('=') + 2; std::size_t end = name.size() - 1; #elif _MSC_VER std::size_t start = name.find('<') + 1; std::size_t end = name.rfind(">("); #else static_assert(false, "Not supported compiler"); #endif name = name.substr(start, end - start); start = name.rfind("::"); return start == std::string_view::npos ? name : name.substr(start + 2); } template consteval auto enum_max() { constexpr auto value = std::bit_cast(static_cast>(N)); if constexpr(enum_name().find(")") == std::string_view::npos) return enum_max(); else return N; } } // namespace clice::impl namespace clice::refl { template struct Record; template constexpr inline bool is_record_v = false; template constexpr inline bool is_record_v> = true; #define CLICE_RECORD(name, ...) \ struct name##Body; \ using name = clice::refl::Record<__VA_ARGS__, name##Body>; \ struct name##Body template concept Reflectable = std::is_aggregate_v> && std::is_default_constructible_v>; template constexpr void foreach(Object&& object, const Callback& callback) { using T = std::decay_t; if constexpr(is_record_v) { T::foreach(std::forward(object), callback); } else { constexpr auto count = impl::member_count(); auto members = impl::collcet_members(object); constexpr auto static_members = impl::collcet_members(impl::Storage::value); [&](std::index_sequence) { (callback(impl::member_name(static_members)>(), *std::get(members)), ...); }(std::make_index_sequence{}); } } // reflectable struct definition template struct Record : Ts... { template static void foreach(Object&& object, const Callback& callback) { (clice::refl::foreach(static_cast>(object), callback), ...); } }; template constexpr void walk(Object&& object, const Callback& callback) { clice::refl::foreach(object, [&](std::string_view name, Field& field) { if constexpr(Reflectable) { walk(field, callback); } else { callback(name, field); } }); } template requires std::is_enum_v constexpr auto enum_name(T value) { constexpr auto count = impl::enum_max(); constexpr auto names = [](std::index_sequence) { return std::array{impl::enum_name(Is)>()...}; }(std::make_index_sequence{}); return names[static_cast(value)]; } /// 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) { constexpr auto lcount = impl::member_count>(); constexpr auto rcount = impl::member_count>(); static_assert(lcount == rcount, "Member count mismatch"); auto lmembers = impl::collcet_members(lhs); auto rmembers = impl::collcet_members(rhs); auto wrapper = [&](auto& lhs, auto& rhs) { if constexpr(std::is_same_v) { callback(lhs, rhs); return true; } else { return callback(lhs, rhs); } }; return [&](std::index_sequence) { /// use && for short-circuit evaluation. return (wrapper(*std::get(lmembers), *std::get(rmembers)) && ...); }(std::make_index_sequence{}); } constexpr auto equal(const auto& lhs, const auto& rhs) { if constexpr(requires { lhs == rhs; }) { return (lhs == rhs); } else { return refl::foreach(lhs, rhs, [](const auto& lhs, const auto& rhs) { return refl::equal(lhs, rhs); }); } } /// Compare lhs and rhs according to the dictionary order of their members. constexpr auto less(const auto& lhs, const auto& rhs) { if constexpr(requires { lhs < rhs; }) { return (lhs < rhs); } else { bool result = false; refl::foreach(lhs, rhs, [&result](const auto& lhs, const auto& rhs) { /// if lhs less than rhs, abort the iteration and return true. if(refl::less(lhs, rhs)) { result = true; return false; } /// if lhs equal to rhs, continue to next member. if(refl::equal(lhs, rhs)) { return true; } /// if lhs greater than rhs, abort the iteration and return false. return false; }); return result; } } }; // namespace clice::refl