forked from Alepha/Alepha
UnifiedEnum
-- union construction of enumerates
This commit is contained in:
@ -33,6 +33,7 @@ add_subdirectory( Utility )
|
|||||||
# The local subdir tests to build
|
# The local subdir tests to build
|
||||||
add_subdirectory( AutoRAII.test )
|
add_subdirectory( AutoRAII.test )
|
||||||
add_subdirectory( Enum.test )
|
add_subdirectory( Enum.test )
|
||||||
|
add_subdirectory( UnifiedEnum.test )
|
||||||
add_subdirectory( make_template.test )
|
add_subdirectory( make_template.test )
|
||||||
add_subdirectory( comparisons.test )
|
add_subdirectory( comparisons.test )
|
||||||
add_subdirectory( Exception.test )
|
add_subdirectory( Exception.test )
|
||||||
|
129
UnifiedEnum.h
Normal file
129
UnifiedEnum.h
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
static_assert( __cplusplus > 2023'00 );
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Alepha/Alepha.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
92
UnifiedEnum.test/0.cc
Normal file
92
UnifiedEnum.test/0.cc
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
static_assert( __cplusplus > 2023'00 );
|
||||||
|
|
||||||
|
#include "../UnifiedEnum.h"
|
||||||
|
|
||||||
|
#include <Alepha/Testing/test.h>
|
||||||
|
#include <Alepha/Testing/TableTest.h>
|
||||||
|
|
||||||
|
#include <Alepha/Utility/evaluation_helpers.h>
|
||||||
|
|
||||||
|
#include <Alepha/IOStreams/String.h>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
template< typename T >
|
||||||
|
concept CanNegate=
|
||||||
|
requires( const T &t )
|
||||||
|
{
|
||||||
|
{ not t };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto init= Alepha::Utility::enroll <=[]
|
||||||
|
{
|
||||||
|
using namespace Alepha::literals::enum_literals;
|
||||||
|
using namespace Alepha::Testing::literals;
|
||||||
|
|
||||||
|
using namespace Alepha::Testing::exports;
|
||||||
|
|
||||||
|
using BirdMeat= Alepha::Enum< "Chicken"_value, "Duck"_value, "Quail"_value >;
|
||||||
|
using LandMeat= Alepha::Enum< "Beef"_value, "Chicken"_value, "Pork"_value >;
|
||||||
|
|
||||||
|
using MyEnum= Alepha::UnifiedEnum< BirdMeat, LandMeat >;
|
||||||
|
|
||||||
|
MyEnum e;
|
||||||
|
|
||||||
|
"Can Unified Enum be constructed from a base enum (left base)?"_test <=TableTest
|
||||||
|
<
|
||||||
|
[]( const BirdMeat &bm ) -> MyEnum
|
||||||
|
{
|
||||||
|
return MyEnum( bm );
|
||||||
|
}
|
||||||
|
>
|
||||||
|
::Cases
|
||||||
|
{
|
||||||
|
{ "Chicken", { "Chicken"_value }, "Chicken"_value },
|
||||||
|
{ "Duck", { "Duck"_value }, "Duck"_value },
|
||||||
|
{ "Quail", { "Quail"_value }, "Quail"_value },
|
||||||
|
};
|
||||||
|
|
||||||
|
"Unified Enum round trip printing test"_test <= TableTest
|
||||||
|
<
|
||||||
|
[]( const std::string s ) -> std::string
|
||||||
|
{
|
||||||
|
MyEnum e;
|
||||||
|
std::istringstream iss{ s };
|
||||||
|
|
||||||
|
( iss >> e );
|
||||||
|
return Alepha::IOStreams::String{} << e;
|
||||||
|
}
|
||||||
|
>
|
||||||
|
::Cases
|
||||||
|
{
|
||||||
|
{ "Beef", { "Beef" }, "Beef" },
|
||||||
|
{ "Chicken", { "Chicken" }, "Chicken" },
|
||||||
|
{ "Duck", { "Duck" }, "Duck" },
|
||||||
|
{ "Unicorn", { "Unicorn" }, std::type_identity< Alepha::EnumTextMismatchError >{} },
|
||||||
|
};
|
||||||
|
|
||||||
|
"Unified Enum failure parse"_test <= TableTest
|
||||||
|
<
|
||||||
|
[]( const std::string s )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
MyEnum e;
|
||||||
|
std::istringstream iss{ s };
|
||||||
|
|
||||||
|
( iss >> e );
|
||||||
|
throw "Fail";
|
||||||
|
}
|
||||||
|
catch( const Alepha::EnumTextMismatchError &ex )
|
||||||
|
{
|
||||||
|
return ex.expectedValues();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
::Cases
|
||||||
|
{
|
||||||
|
{ "Unicorn", { "Unicorn" }, { "Chicken", "Duck", "Quail", "Beef", "Chicken", "Pork" } },
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
1
UnifiedEnum.test/CMakeLists.txt
Normal file
1
UnifiedEnum.test/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
unit_test( 0 )
|
Reference in New Issue
Block a user