//----------------------------------------------------------------------------- // ___ __ _ _ // / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __ // / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ / // / ___/ (_| | | \__ \ __/ /__| | | | | < // \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ . // //----------------------------------------------------------------------------- // Author: Kurt Sassenrath // Module: Utility // // Compile-time reflection for structures. // // Copyright (c) 2023 Kurt Sassenrath. // // License TBD. //----------------------------------------------------------------------------- #ifndef parselink_ct_reflect_080596cd36d52770 #define parselink_ct_reflect_080596cd36d52770 #include #include #include #include namespace parselink { namespace ct { namespace detail { // This allows us to reference a type instance without ever instantiating it. template extern T const phantom; // This type binds to anything, allowing us to sus out the number of data // members in an aggregate. struct any_type { template constexpr operator T(); }; // clang-format off // This template takes a wrapped pointer to a data member of a theoretical // instance of that type, in order to retrieve the name at compile time. // Examples: // gcc: // constexpr auto parselink::ct::detail::src_string() [with auto Member = const_ptr{(& phantom.structure::field)}] // // clang: // auto parselink::ct::detail::src_string() [Member = const_ptr{&phantom.field}] // clang-format on template constexpr auto src_string() { return std::source_location::current().function_name(); } template struct base_parse_traits { static constexpr char start_delim = StartDelim; static constexpr char end_delim = EndDelim; }; #ifdef __clang__ struct parse : base_parse_traits<'.', '}'> {}; #elif defined(__GNUC__) struct parse : base_parse_traits<':', ')'> {}; #endif static constexpr std::size_t max_members = 8; // TODO: Logarithmic approach // This needs to be limited to max_members, otherwise clang will go off into // the weeds. template requires(sizeof...(Members) <= max_members) consteval std::size_t count_members(Members&&... members) { if constexpr (requires { T{any_type{}, members...}; } == false) { return sizeof...(members); } else if constexpr (sizeof...(members) <= max_members) { return count_members(members..., any_type{}); } else { throw "Too many members / something went wrong!"; } }; // This function takes an aggregate and attempts to bind it into a tuple. // Unfortunately, without variadic packs for structured bindings, this will // rely on some heavy boilerplate. template ()> requires(MemberCount <= 8) consteval decltype(auto) as_tuple(T&& t) { if constexpr (MemberCount == 0) { return std::tuple{}; } else if constexpr (MemberCount == 1) { auto&& [a0] = t; return std::tie(a0); } else if constexpr (MemberCount == 2) { auto&& [a0, a1] = t; return std::tie(a0, a1); } else if constexpr (MemberCount == 3) { auto&& [a0, a1, a2] = t; return std::tie(a0, a1, a2); } else if constexpr (MemberCount == 4) { auto&& [a0, a1, a2, a3] = t; return std::tie(a0, a1, a2, a3); } else if constexpr (MemberCount == 5) { auto&& [a0, a1, a2, a3, a4] = t; return std::tie(a0, a1, a2, a3, a4); } else if constexpr (MemberCount == 6) { auto&& [a0, a1, a2, a3, a4, a5] = t; return std::tie(a0, a1, a2, a3, a4, a5); } else if constexpr (MemberCount == 7) { auto&& [a0, a1, a2, a3, a4, a5, a6] = t; return std::tie(a0, a1, a2, a3, a4, a5, a6); } else if constexpr (MemberCount == 8) { auto&& [a0, a1, a2, a3, a4, a5, a6, a7] = t; return std::tie(a0, a1, a2, a3, a4, a5, a6, a7); } else { throw "Too many members!"; } } template struct const_ptr { T const* ptr; }; template consteval decltype(auto) as_ptr(T&& t) noexcept { auto&& member = std::get(as_tuple(t)); using member_ptr = std::decay_t; return const_ptr(&member); } template consteval decltype(auto) raw_member_name() noexcept { return src_string(phantom)>(); } template consteval decltype(auto) get_member_name() noexcept { std::string_view raw = raw_member_name(); auto s = raw.rfind(parse::start_delim); auto e = raw.rfind(parse::end_delim); if (s == raw.npos || e == raw.npos) throw "name parsing is broken!"; return raw.substr(s + 1, e - (s + 1)); } template consteval decltype(auto) get_member_names(std::index_sequence) noexcept { return std::array{get_member_name()...}; } } // namespace detail template consteval decltype(auto) member_name() noexcept { std::string_view raw = detail::raw_member_name(); auto s = raw.rfind(detail::parse::start_delim); auto e = raw.rfind(detail::parse::end_delim); if (s == raw.npos || e == raw.npos) throw "name_of is broken!"; return raw.substr(s + 1, e - (s + 1)); } template consteval decltype(auto) member_names() noexcept { return detail::get_member_names( std::make_index_sequence()>{}); } } // namespace ct } // namespace parselink #endif // parselink_ct_reflect_080596cd36d52770