include/ylt/standalone/iguana/field_reflection.hpp (161 lines of code) (raw):

#pragma once #include <array> #include <string_view> #include "frozen/string.h" #include "frozen/unordered_map.h" namespace iguana { #if defined(__clang__) || defined(_MSC_VER) || \ (defined(__GNUC__) && __GNUC__ > 8) template <typename T> constexpr std::string_view get_raw_name() { #ifdef _MSC_VER return __FUNCSIG__; #else return __PRETTY_FUNCTION__; #endif } template <auto T> constexpr std::string_view get_raw_name() { #ifdef _MSC_VER return __FUNCSIG__; #else return __PRETTY_FUNCTION__; #endif } template <typename T> inline constexpr std::string_view type_string() { constexpr std::string_view sample = get_raw_name<int>(); constexpr size_t pos = sample.find("int"); constexpr std::string_view str = get_raw_name<T>(); constexpr auto next1 = str.rfind(sample[pos + 3]); #if defined(_MSC_VER) constexpr std::size_t npos = str.find_first_of(" ", pos); if (npos != std::string_view::npos) return str.substr(npos + 1, next1 - npos - 1); else return str.substr(pos, next1 - pos); #else return str.substr(pos, next1 - pos); #endif } template <auto T> inline constexpr std::string_view enum_string() { constexpr std::string_view sample = get_raw_name<int>(); constexpr size_t pos = sample.find("int"); constexpr std::string_view str = get_raw_name<T>(); constexpr auto next1 = str.rfind(sample[pos + 3]); #if defined(__clang__) || defined(_MSC_VER) constexpr auto s1 = str.substr(pos, next1 - pos); #else constexpr auto s1 = str.substr(pos + 5, next1 - pos - 5); #endif return s1; } template <auto field> inline constexpr std::string_view field_string() { constexpr std::string_view raw_name = enum_string<field>(); return raw_name.substr(raw_name.rfind(":") + 1); } #if defined(__clang__) && (__clang_major__ >= 17) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wenum-constexpr-conversion" #endif template <typename E, E V> constexpr std::string_view get_raw_name_with_v() { #ifdef _MSC_VER return __FUNCSIG__; #else return __PRETTY_FUNCTION__; #endif } // True means the V is a valid enum value, and the second of the result is the // name template <typename E, E V> constexpr std::pair<bool, std::string_view> try_get_enum_name() { constexpr std::string_view sample_raw_name = get_raw_name_with_v<int, 5>(); constexpr size_t pos = sample_raw_name.find("5"); constexpr std::string_view enum_raw_name = get_raw_name_with_v<E, V>(); constexpr auto enum_end = enum_raw_name.rfind(&sample_raw_name[pos + 1]); #ifdef _MSC_VER constexpr auto enum_begin = enum_raw_name.rfind(',', enum_end) + 1; #else constexpr auto enum_begin = enum_raw_name.rfind(' ', enum_end) + 1; #endif constexpr std::string_view res = enum_raw_name.substr(enum_begin, enum_end - enum_begin); constexpr size_t pos_brackets = res.find(')'); size_t pos_colon = res.find("::"); return {pos_brackets == std::string_view::npos, res.substr(pos_colon == std::string_view::npos ? 0 : pos_colon + 2)}; } // Enumerate the numbers in a integer sequence to see if they are legal enum // value template <typename E, std::int64_t... Is> constexpr inline auto get_enum_arr( const std::integer_sequence<std::int64_t, Is...> &) { constexpr std::size_t N = sizeof...(Is); std::array<std::string_view, N> enum_names = {}; std::array<E, N> enum_values = {}; std::size_t num = 0; (([&]() { constexpr auto res = try_get_enum_name<E, static_cast<E>(Is)>(); if constexpr (res.first) { // the Is is a valid enum value enum_names[num] = res.second; enum_values[num] = static_cast<E>(Is); ++num; } })(), ...); return std::make_tuple(num, enum_values, enum_names); } template <std::size_t N, const std::array<int, N> &arr, size_t... Is> constexpr auto array_to_seq(const std::index_sequence<Is...> &) { return std::integer_sequence<std::int64_t, arr[Is]...>(); } // convert array to map template <typename E, size_t N, size_t... Is> constexpr inline auto get_enum_to_str_map( const std::array<std::string_view, N> &enum_names, const std::array<E, N> &enum_values, const std::index_sequence<Is...> &) { return frozen::unordered_map<E, frozen::string, sizeof...(Is)>{ {enum_values[Is], enum_names[Is]}...}; } template <typename E, size_t N, size_t... Is> constexpr inline auto get_str_to_enum_map( const std::array<std::string_view, N> &enum_names, const std::array<E, N> &enum_values, const std::index_sequence<Is...> &) { return frozen::unordered_map<frozen::string, E, sizeof...(Is)>{ {enum_names[Is], enum_values[Is]}...}; } #endif // the default generic enum_value // if the user has not defined a specialization template, this will be called template <typename T> struct enum_value { constexpr static std::array<int, 0> value = {}; }; template <bool str_to_enum, typename E> constexpr inline auto get_enum_map() { #if defined(__clang__) || defined(_MSC_VER) || \ (defined(__GNUC__) && __GNUC__ > 8) constexpr auto &arr = enum_value<E>::value; constexpr auto arr_size = arr.size(); if constexpr (arr_size > 0) { // the user has defined a specialization template constexpr auto arr_seq = array_to_seq<arr_size, arr>(std::make_index_sequence<arr_size>()); constexpr auto t = get_enum_arr<E>(arr_seq); if constexpr (str_to_enum) { return get_str_to_enum_map<E, arr_size>( std::get<2>(t), std::get<1>(t), std::make_index_sequence<std::get<0>(t)>{}); } else { return get_enum_to_str_map<E, arr_size>( std::get<2>(t), std::get<1>(t), std::make_index_sequence<std::get<0>(t)>{}); } } else { return false; } #else return false; #endif } #if defined(__clang__) && (__clang_major__ >= 17) #pragma clang diagnostic pop #endif } // namespace iguana