static_assert( __cplusplus > 2023'00 ); #pragma once #include #include "Enum.h" namespace Alepha::Hydrogen ::detail:: UnifiedEnum_m { inline namespace exports {} template< typename T > constexpr bool is_alepha_enum_v= false; using detail::Enum_m::EnumValueString; template< detail::Enum_m::EnumValueString ... values > constexpr bool is_alepha_enum_v< Enum< values... > >{ true }; template< typename T > concept EnhancedEnumerate= is_alepha_enum_v< T >; namespace exports { template< EnhancedEnumerate ... > class UnifiedEnum; } template< EnumValueString cs_strs > struct StringWrapper; template< EnhancedEnumerate ... enums > struct unify_values; template< detail::Enum_m::EnumValueString ... cs_str > struct unify_values< Enum< cs_str... > > { using type= TypeList< StringWrapper< cs_str >... >; }; template< detail::Enum_m::EnumValueString ... cs_str, EnhancedEnumerate ... next > struct unify_values< Enum< cs_str... >, next... > { using type= list_cat_t< TypeList< StringWrapper< cs_str >... >, typename unify_values< next... >::type >; }; // Because of the way that this works, I do wind up with a possibility of duplicates // in the enumerate list. This is (hopefully) benign. The underlying `Enum` // type doesn't check for duplicates for the same reason we'll not check here. // It's hopefully benign as the duplicated enumerate values simply become an // unused index by the `get_index` and `set_index` functions. For the most // part users of the class shouldn't be calling those functions. If they are, // it should merely be for serialization. As such, skipping a value in the // sequential indexing shouldn't be observable. // // Without good enough support for a compiletime search and dedupe on types, // we have to resort to O( N ** 2 ) or worse algorithms to remove duplicates. // With so much compiletime algorithm pressure already, this would become a // significant pain point for TUs which have a lot of `Enum`s. // // An unfortunate side-effect of this design choice is that when/if this design // is changed to support deduplication of enumerate values (strings), that // will create a massive but subtle ABI break: // `UnifiedEnum< Enum< "a"_value >, Enum< "a"_value > >` would now use a // different index scheme, despite having the same typename. template< TypeListType > struct build_enum; template< detail::Enum_m::EnumValueString ... cs_str > struct build_enum< TypeList< StringWrapper< cs_str >... > > { using type= Enum< cs_str... >; }; template< EnhancedEnumerate ... EnumType > class exports::UnifiedEnum : protected build_enum< typename unify_values< EnumType... >::type >::type { private: using Base= typename build_enum< typename unify_values< EnumType... >::type >::type; public: using Base::Base; using Base::name; using Base::accepts; using Base::set_index; using Base::get_index; template< typename Enumerate > requires( list_contains_v< TypeList< EnumType... >, Enumerate > ) UnifiedEnum( const Enumerate e ) { unsigned value= 0; for( const auto &next: this->keys() ) { if( next == e.key() ) { set_index( value ); return; } ++value; } // It should not be possible to reach here. abort(); } friend std::ostream & operator << ( std::ostream &os, const UnifiedEnum &rhs ) { return os << static_cast< const Base & >( rhs ); } friend std::istream & operator >> ( std::istream &is, UnifiedEnum &rhs ) { return is >> static_cast< Base & >( rhs ); } friend constexpr bool operator == ( const UnifiedEnum &, const UnifiedEnum & )= default; }; } namespace Alepha::Hydrogen::inline exports::inline UnifiedEnum_m { using namespace detail::UnifiedEnum_m::exports; }