1
0
forked from Alepha/Alepha

UnifiedEnum -- union construction of enumerates

This commit is contained in:
2024-07-13 16:38:06 -04:00
parent 3c814a53c3
commit 308b64fc64
4 changed files with 223 additions and 0 deletions

View File

@ -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
View 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
View 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" } },
};
};

View File

@ -0,0 +1 @@
unit_test( 0 )