// // Enum.h // Clock Signal // // Created by Thomas Harte on 17/02/2020. // Copyright © 2020 Thomas Harte. All rights reserved. // #ifndef Enum_h #define Enum_h #include #include #include #include #include #include #include namespace Reflection { #define ReflectableEnum(Name, ...) \ enum class Name { __VA_ARGS__ }; \ constexpr static const char *__declaration##Name = #__VA_ARGS__; #define EnumDeclaration(Name) #Name, __declaration##Name #define AnnounceEnum(Name) ::Reflection::Enum::declare(EnumDeclaration(Name)) #define AnnounceEnumNS(Namespace, Name) ::Reflection::Enum::declare(#Name, Namespace::__declaration##Name) /*! This provides a very slight version of enum reflection; you can introspect only: * enums have been registered, along with the text of their declarations; * provided that those enums do not declare specific values for their members. The macros above help avoid duplication of the declaration, making this just mildly less terrible than it might have been. No guarantees of speed or any other kind of efficiency are offered. */ class Enum { public: /*! Registers @c name and the entries within @c declaration for the enum type @c Type. Assuming the caller used the macros above, a standard pattern where both things can be placed in the same namespace might look like: ReflectableEnum(MyEnum, int, A, B, C); ... AnnounceEnum(MyEnum) If AnnounceEnum cannot be placed into the same namespace as ReflectableEnum, see the EnumDeclaration macro. */ template static void declare(const char *name, const char *declaration) { const char *d_ptr = declaration; std::vector result; while(true) { // Skip non-alphas, and exit if the terminator is found. while(*d_ptr && !isalpha(*d_ptr)) ++d_ptr; if(!*d_ptr) break; // Note the current location and proceed for all alphas and digits. const auto start = d_ptr; while(isalpha(*d_ptr) || isdigit(*d_ptr)) ++d_ptr; // Add a string view. result.emplace_back(std::string(start, size_t(d_ptr - start))); } members_by_type_.emplace(std::make_pair(std::type_index(typeid(Type)), result)); names_by_type_.emplace(std::make_pair(std::type_index(typeid(Type)), std::string(name))); } /*! @returns the declared name of the enum @c Type if it has been registered; the empty string otherwise. */ template static const std::string &name() { return name(typeid(Type)); } /*! @returns the declared name of the enum with type_info @c type if it has been registered; the empty string otherwise. */ static const std::string &name(std::type_index type) { const auto entry = names_by_type_.find(type); if(entry == names_by_type_.end()) return empty_string_; return entry->second; } /*! @returns the number of members of the enum @c Type if it has been registered; 0 otherwise. */ template static size_t size() { return size(typeid(Type)); } /*! @returns the number of members of the enum with type_info @c type if it has been registered; @c std::string::npos otherwise. */ static size_t size(std::type_index type) { const auto entry = members_by_type_.find(type); if(entry == members_by_type_.end()) return std::string::npos; return entry->second.size(); } /*! @returns A @c std::string name for the enum value @c e. */ template static const std::string &to_string(Type e) { return to_string(typeid(Type), size_t(e)); } /*! @returns A @c std::string name for the enum value @c e from the enum with type_info @c type. */ static const std::string &to_string(std::type_index type, size_t e) { const auto entry = members_by_type_.find(type); if(entry == members_by_type_.end()) return empty_string_; return entry->second[e]; } /*! @returns a vector naming the members of the enum with type_info @c type if it has been registered; an empty vector otherwise. */ static const std::vector &all_values(std::type_index type) { const auto entry = members_by_type_.find(type); if(entry == members_by_type_.end()) return empty_vector_; return entry->second; } /*! @returns a vector naming the members of the enum @c Type type if it has been registered; an empty vector otherwise. */ template static const std::vector &all_values() { return all_values(typeid(Type)); } /*! @returns A value of @c Type for the name @c str, or @c EnumType(std::string::npos) if the name is not found. */ template static Type from_string(const std::string &str) { return Type(from_string(typeid(Type), str)); } /*! @returns A value for the name @c str in the enum with type_info @c type , or @c std::string::npos if the name is not found. */ static size_t from_string(std::type_index type, const std::string &str) { const auto entry = members_by_type_.find(type); if(entry == members_by_type_.end()) return std::string::npos; const auto iterator = std::find(entry->second.begin(), entry->second.end(), str); if(iterator == entry->second.end()) return std::string::npos; return size_t(iterator - entry->second.begin()); } private: static inline std::unordered_map> members_by_type_; static inline std::unordered_map names_by_type_; static inline const std::string empty_string_; static inline const std::vector empty_vector_; }; } #endif /* Enum_h */