1
0
forked from Alepha/Alepha

Flesh out more automatic exception class generation.

Also improved the tests.
This commit is contained in:
2022-10-17 22:49:49 -04:00
parent d5684b8d51
commit 72b316d0e2
3 changed files with 201 additions and 109 deletions

View File

@ -18,7 +18,7 @@ namespace Alepha::Hydrogen
/*! /*!
* @file * @file
* *
* Exception grades. * Exception grades -- Exceptions have different grades of severity.
* *
* In Alepha, there are several "grades" of exception types which can be thrown. The grade of * In Alepha, there are several "grades" of exception types which can be thrown. The grade of
* an exception is "branded" into its name as the last word in camel case. * an exception is "branded" into its name as the last word in camel case.
@ -52,9 +52,54 @@ namespace Alepha::Hydrogen
* bug. `Violation::~Violation` is setup to call `abort()` when called. * bug. `Violation::~Violation` is setup to call `abort()` when called.
*/ */
template< typename ... Bases >
struct bases : virtual public Bases... {};
template< typename unique, typename GradeType, typename Bases >
class synthetic_any_tagged_type
{
};
template< typename unique_handle, typename GradeType, typename ... Bases >
class synthetic_exception
: virtual public bases< GradeType, Bases... >
{
public:
using grade_type= GradeType;
class any_tagged_type
: virtual public bases< synthetic_exception, typename GradeType::any_tagged_type, typename Bases::any_tagged_type... >
{
public:
using grade_type= GradeType;
};
template< typename tag >
class tagged_type
: virtual public bases< synthetic_exception, any_tagged_type, typename GradeType::template tagged_type< tag >, typename Bases::template tagged_type< tag >... >
{
public:
using grade_type= GradeType;
};
};
template< typename unique_handle, typename GradeType, typename ... Bases >
using create_exception= synthetic_exception< unique_handle, GradeType, Bases ... >;
template< typename Exc >
using AnyTagged= typename Exc::any_tagged_type;
template< typename Exc, typename tag >
using Tagged= typename Exc::template tagged_type< tag >;
class Throwable class Throwable
{ {
public: public:
using grade_type= Throwable;
class any_tagged_type;
template< typename Tag > class tagged_type;
virtual ~Throwable()= default; virtual ~Throwable()= default;
virtual const char *message() const noexcept= 0; virtual const char *message() const noexcept= 0;
@ -77,17 +122,15 @@ namespace Alepha::Hydrogen
return dynamic_cast< const Target * >( this ); return dynamic_cast< const Target * >( this );
} }
}; };
class Throwable::any_tagged_type
class AnyTaggedThrowable : virtual public grade_type
: virtual public Throwable
{ {
public: public:
virtual std::type_index tag() const noexcept= 0; virtual std::type_index tag() const noexcept= 0;
}; };
template< typename Tag > template< typename Tag >
class TaggedThrowable class Throwable::tagged_type
: virtual public AnyTaggedThrowable : virtual public grade_type, virtual public grade_type::any_tagged_type
{ {
public: public:
std::type_index std::type_index
@ -96,19 +139,32 @@ namespace Alepha::Hydrogen
return typeid( std::type_identity< Tag > ); return typeid( std::type_identity< Tag > );
} }
}; };
using AnyTaggedThrowable= AnyTagged< Throwable >;
template< typename tag >
using TaggedThrowable= Tagged< Throwable, tag >;
template< typename ... Bases >
struct bases : virtual public Bases... {};
// `Notification`s are "events" or "interrupts" that // `Notification`s are "events" or "interrupts" that
// if ignored will gracefully terminate the current // if ignored will gracefully terminate the current
// thread, but not halt the program. // thread, but not halt the program.
class Notification : public virtual Throwable {}; class Notification
class AnyTaggedNotification : public virtual Throwable
: public virtual bases< Notification, AnyTaggedThrowable > {}; {
public:
using grade_type= Notification;
class any_tagged_type;
template< typename tag > template< typename tag >
class TaggedNotification class tagged_type;
: public virtual bases< TaggedThrowable< tag >, AnyTaggedNotification > {}; };
class Notification::any_tagged_type
: public virtual bases< grade_type, Throwable::any_tagged_type > {};
template< typename tag >
class Notification::tagged_type
: public virtual bases< grade_type::any_tagged_type, Throwable::tagged_type< tag > > {};
using AnyTaggedNotification= Notification::any_tagged_type;
template< typename tag >
using TaggedNotification= Notification::tagged_type< tag >;
using Interrupt= Notification; using Interrupt= Notification;
using AnyTaggedInterrupt= AnyTaggedNotification; using AnyTaggedInterrupt= AnyTaggedNotification;
@ -119,31 +175,58 @@ namespace Alepha::Hydrogen
class ExceptionBridgeInterface class ExceptionBridgeInterface
{ {
public: public:
virtual ~ExceptionBridgeInterface()= default;
virtual const char *what() const noexcept= 0; virtual const char *what() const noexcept= 0;
}; };
class Exception : virtual public bases< Throwable >, virtual private ExceptionBridgeInterface { public: using ExceptionBridgeInterface::what; }; class Exception
class AnyTaggedException : virtual public bases< Exception, AnyTaggedThrowable > {}; : virtual public bases< Throwable >, virtual private ExceptionBridgeInterface
{
public:
using grade_type= Exception;
using ExceptionBridgeInterface::what;
class any_tagged_type;
template< typename tag > template< typename tag >
class TaggedException : virtual public bases< TaggedThrowable< tag >, AnyTaggedException > {}; class tagged_type;
};
class Exception::any_tagged_type : virtual public bases< grade_type, Throwable::any_tagged_type > {};
template< typename tag >
class Exception::tagged_type
: virtual public bases< grade_type::any_tagged_type, Throwable::tagged_type< tag > > {};
using AnyTaggedException= Exception::any_tagged_type;
template< typename tag >
using TaggedException= Exception::tagged_type< tag >;
// `Error`s are only really recoverable by terminating // `Error`s are only really recoverable by terminating
// the major procedure underway. Like terminating the // the major procedure underway. Like terminating the
// entire thread or similar. Essentially, arbitrarily // entire thread or similar. Essentially, arbitrarily
// localized recovery is impossible. // localized recovery is impossible.
class Error: virtual public bases< Throwable > {}; class Error
class AnyTaggedError : virtual public bases< Error, AnyTaggedThrowable > {}; : virtual public bases< Throwable >
{
public:
using grade_type= Error;
class any_tagged_type;
template< typename tag > template< typename tag >
class TaggedError : virtual bases< AnyTaggedError, TaggedThrowable< tag > > {}; class tagged_type;
};
class Error::any_tagged_type
: virtual public bases< grade_type, Throwable::any_tagged_type > {};
template< typename tag >
class Error::tagged_type
: virtual bases< grade_type::any_tagged_type, Throwable::tagged_type< tag > > {};
using AnyTaggedError= Error::any_tagged_type;
template< typename tag >
using TaggedError= Error::tagged_type< tag >;
// `Violation`s are unrecoverable events which happen to // `Violation`s are unrecoverable events which happen to
// the process. They are impossible to recover from. // the process. They are impossible to recover from.
// Handlers for this should be treated almost like // Handlers for this should be treated almost like
// `[[noreturn]]` functions. Mostly one would catch // `[[noreturn]]` functions, such as `std::terminate_handler`.
// this class if they intended to perform a bit of local // Mostly one would catch this class if they intended to
// persistent state sanitization and then continue the // perform a bit of local persistent state sanitization
// unwind process // which is somewhat different to the normal dtors in scope
// and then continue the unwind process
class Violation class Violation
: virtual public bases< Throwable > : virtual public bases< Throwable >
{ {
@ -151,14 +234,22 @@ namespace Alepha::Hydrogen
bool active= true; bool active= true;
public: public:
using grade_type= Violation;
class any_tagged_type;
template< typename tag >
class tagged_type;
~Violation() override { if( not active ) abort(); } ~Violation() override { if( not active ) abort(); }
Violation( const Violation &copy )= delete; Violation( const Violation &copy )= delete;
Violation( Violation &copy ) : active( copy.active ) { copy.active= false; } Violation( Violation &copy ) : active( copy.active ) { copy.active= false; }
}; };
class AnyTaggedViolation : virtual public bases< Violation, AnyTaggedThrowable > {}; class Violation::any_tagged_type : virtual public bases< grade_type, Throwable::any_tagged_type > {};
template< typename tag > template< typename tag >
class TaggedViolation : virtual public bases< AnyTaggedViolation, TaggedThrowable< tag > > {}; class Violation::tagged_type : virtual public bases< grade_type::any_tagged_type, Throwable::tagged_type< tag > > {};
using AnyTaggedViolation= Violation::any_tagged_type;
template< typename tag >
using TaggedViolation= Violation::tagged_type< tag >;
template< typename T > template< typename T >
concept DerivedFromException= std::is_base_of_v< Exception, T >; concept DerivedFromException= std::is_base_of_v< Exception, T >;
@ -181,44 +272,30 @@ namespace Alepha::Hydrogen
public: public:
std::string_view resourceName() const noexcept final { return storage; } std::string_view resourceName() const noexcept final { return storage; }
}; };
class NamedResourceThrowable : virtual public bases< Throwable, NamedResourceInterface > {}; class NamedResourceThrowable : public virtual synthetic_exception< struct named_resource_throwable, Throwable >, virtual public NamedResourceInterface {};
class AnyTaggedNamedResourceThrowable using AnyTaggedNamedResourceThrowable= NamedResourceThrowable::any_tagged_type;
: virtual public bases< NamedResourceThrowable, AnyTaggedThrowable > {};
template< typename tag > template< typename tag >
class TaggedNamedResourceThrowable using TaggedNamedResourceThrowable= NamedResourceThrowable::tagged_type< tag >;
: virtual public bases< AnyTaggedNamedResourceThrowable, TaggedThrowable< tag > > {};
class NamedResourceNotification using NamedResourceNotification= synthetic_exception< struct named_resource_notification, Notification, NamedResourceThrowable >;
: public virtual bases< Notification, NamedResourceThrowable > {}; using AnyTaggedNamedResourceNotification= NamedResourceNotification::any_tagged_type;
class AnyTaggedNamedResourceNotification
: public virtual bases< NamedResourceNotification, AnyTaggedNotification, AnyTaggedNamedResourceThrowable > {};
template< typename tag > template< typename tag >
class TaggedNamedResourceNotification using TaggedNamedResourceNotification= NamedResourceNotification::tagged_type< tag >;
: public virtual bases< AnyTaggedNamedResourceNotification, TaggedNotification< tag >, TaggedNamedResourceThrowable< tag > > {};
class NamedResourceException using NamedResourceException= synthetic_exception< struct named_resource_exception, Exception, NamedResourceThrowable >;
: public virtual bases< Exception, NamedResourceThrowable > {}; using AnyTaggedNamedResourceException= NamedResourceException::any_tagged_type;
class AnyTaggedNamedResourceException
: public virtual bases< NamedResourceException, AnyTaggedException, AnyTaggedNamedResourceThrowable > {};
template< typename tag > template< typename tag >
class TaggedNamedResourceException using TaggedNamedResourceException= NamedResourceException::tagged_type< tag >;
: public virtual bases< AnyTaggedNamedResourceException, TaggedException< tag >, TaggedNamedResourceThrowable< tag > > {};
class NamedResourceError using NamedResourceError= synthetic_exception< struct named_resource_error, Error, NamedResourceThrowable >;
: public virtual bases< Error, NamedResourceThrowable > {}; using AnyTaggedNamedResourceError= NamedResourceError::any_tagged_type;
class AnyTaggedNamedResourceError
: public virtual bases< NamedResourceError, AnyTaggedError, AnyTaggedNamedResourceThrowable > {};
template< typename tag > template< typename tag >
class TaggedNamedResourceError using TaggedNamedResourceError= NamedResourceError::tagged_type< tag >;
: public virtual bases< AnyTaggedNamedResourceError, TaggedError< tag >, TaggedNamedResourceThrowable< tag > > {};
class NamedResourceViolation using NamedResourceViolation= synthetic_exception< struct named_resource_violation, Violation, NamedResourceThrowable >;
: public virtual bases< Violation, NamedResourceThrowable > {}; using AnyTaggedNamedResourceViolation= NamedResourceViolation::any_tagged_type;
class AnyTaggedNamedResourceViolation
: public virtual bases< NamedResourceViolation, AnyTaggedViolation, AnyTaggedNamedResourceThrowable > {};
template< typename tag > template< typename tag >
class TaggedNamedResourceViolation using TaggedNamedResourceViolation= NamedResourceViolation::tagged_type< tag >;
: public virtual bases< AnyTaggedNamedResourceViolation, TaggedViolation< tag >, TaggedNamedResourceThrowable< tag > > {};
class AllocationAmountStorage; class AllocationAmountStorage;
@ -239,58 +316,42 @@ namespace Alepha::Hydrogen
std::size_t allocationAmount() const noexcept final { return amount; } std::size_t allocationAmount() const noexcept final { return amount; }
}; };
class AllocationThrowable class AllocationThrowable
: public virtual bases< Throwable, AllocationAmountInterface > {}; : virtual public synthetic_exception< struct allocation_throwable, Throwable >, virtual public AllocationAmountInterface {};
class AnyTaggedAllocationThrowable using AnyTaggedAllocationThrowable= AllocationThrowable::any_tagged_type;
: public virtual bases< AllocationThrowable, AnyTaggedThrowable > {};
template< typename tag > template< typename tag >
class TaggedAllocationThrowable using TaggedAllocationThrowable= AllocationThrowable::tagged_type< tag >;
: public virtual bases< AnyTaggedAllocationThrowable, TaggedThrowable< tag > > {};
class AllocationException using AllocationException= synthetic_exception< struct allocation_exception, Exception, AllocationThrowable >;
: virtual public bases< Exception, AllocationThrowable > {}; using AnyTaggedAllocationException= AllocationException::any_tagged_type;
class AnyTaggedAllocationException
: virtual public bases< AllocationException, AnyTaggedAllocationThrowable, AnyTaggedException > {};
template< typename tag > template< typename tag >
class TaggedAllocationException using TaggedAllocationException= AllocationException::tagged_type< tag >;
: virtual public bases< AnyTaggedAllocationException, TaggedAllocationThrowable< tag >, TaggedException< tag > > {};
class AllocationError using AllocationError= synthetic_exception< struct allocation_error, Error, AllocationThrowable >;
: virtual public bases< Error, AllocationThrowable > {}; using AnyTaggedAllocationError= AllocationError::any_tagged_type;
class AnyTaggedAllocationError
: virtual public bases< AllocationError, AnyTaggedAllocationThrowable, AnyTaggedError > {};
template< typename tag > template< typename tag >
class TaggedAllocationError using TaggedAllocationError= AllocationError::tagged_type< tag >;
: virtual public bases< AnyTaggedAllocationError, TaggedAllocationThrowable< tag >, TaggedError< tag > > {};
class AllocationViolation using AllocationViolation= synthetic_exception< struct allocation_violation, Violation, AllocationThrowable >;
: virtual public bases< Violation, AllocationThrowable > {}; using AnyTaggedAllocationViolation= AllocationViolation::any_tagged_type;
class AnyTaggedAllocationViolation
: virtual public bases< AllocationViolation, AnyTaggedAllocationThrowable, AnyTaggedViolation > {};
template< typename tag > template< typename tag >
class TaggedAllocationViolation using TaggedAllocationViolation= AllocationViolation::tagged_type< tag >;
: virtual public bases< AnyTaggedAllocationViolation, TaggedAllocationThrowable< tag >, TaggedViolation< tag > > {};
class MessageStorage class MessageStorage
: virtual public Throwable : virtual public Throwable
{ {
private: protected:
std::string storage; std::string storage;
public: MessageStorage()= default;
explicit MessageStorage( std::string storage ) : storage( std::move( storage ) ) {} explicit MessageStorage( std::string storage ) : storage( std::move( storage ) ) {}
public:
const char *message() const noexcept { return storage.c_str(); } const char *message() const noexcept { return storage.c_str(); }
}; };
class AllocationExceptionBridge template< typename std_exception >
: virtual public std::bad_alloc, public virtual ExceptionBridgeInterface, virtual public Exception class GenericExceptionBridge
{ : virtual public std_exception, public virtual ExceptionBridgeInterface, virtual public Exception
public:
const char *what() const noexcept override { return message(); }
};
class RegularExceptionBridge
: virtual public std::exception, public virtual ExceptionBridgeInterface, virtual public Exception
{ {
public: public:
const char *what() const noexcept override { return message(); } const char *what() const noexcept override { return message(); }
@ -303,8 +364,14 @@ namespace Alepha::Hydrogen
if constexpr( false ) {} if constexpr( false ) {}
else if constexpr( std::is_base_of_v< AllocationException, Kind > ) else if constexpr( std::is_base_of_v< AllocationException, Kind > )
{ {
class Undergird
: virtual public Kind, virtual protected GenericExceptionBridge< std::bad_alloc >,
virtual protected MessageStorage, virtual protected AllocationAmountStorage,
virtual public std::bad_alloc
{};
class Exception class Exception
: virtual public Kind, virtual private AllocationExceptionBridge, virtual private MessageStorage, virtual private AllocationAmountStorage : virtual private Undergird, virtual public Kind, public virtual std::bad_alloc
{ {
public: public:
explicit Exception( std::string message ) : MessageStorage( std::move( message ) ) {} explicit Exception( std::string message ) : MessageStorage( std::move( message ) ) {}
@ -314,8 +381,16 @@ namespace Alepha::Hydrogen
} }
else if constexpr( std::is_base_of_v< Exception, Kind > ) else if constexpr( std::is_base_of_v< Exception, Kind > )
{ {
class Undergird
: virtual public Kind, virtual protected GenericExceptionBridge< std::exception >,
virtual protected MessageStorage, virtual protected AllocationAmountStorage,
virtual public std::exception
{};
class Exception class Exception
: virtual public Kind, virtual private RegularExceptionBridge, virtual private MessageStorage : virtual private Undergird,
virtual public Kind,
virtual public std::exception
{ {
public: public:
explicit Exception( std::string message ) : MessageStorage( std::move( message ) ) {} explicit Exception( std::string message ) : MessageStorage( std::move( message ) ) {}
@ -328,12 +403,17 @@ namespace Alepha::Hydrogen
class Thrown class Thrown
: virtual public Kind, virtual private MessageStorage : virtual public Kind, virtual private MessageStorage
{ {
public:
explicit Thrown( std::string message ) : MessageStorage( std::move( message ) ) {} explicit Thrown( std::string message ) : MessageStorage( std::move( message ) ) {}
}; };
return Thrown{ std::move( message ) }; return Thrown{ std::move( message ) };
} }
} }
using FinishedException= synthetic_exception< struct finished_exception, Exception >;
using AnyTaggedFinishedException= AnyTagged< FinishedException >;
template< typename tag > using TaggedFinishedException= Tagged< FinishedException, tag >;
} }
inline namespace exports {} inline namespace exports {}

View File

@ -4,5 +4,6 @@ CXX=clang++-12
CXXFLAGS+= -Wno-inline-namespace-reopened-noninline CXXFLAGS+= -Wno-inline-namespace-reopened-noninline
CXXFLAGS+= -Wno-unused-comparison CXXFLAGS+= -Wno-unused-comparison
CXXFLAGS+= -DAlepha=MyProject_Alepha
all: exception all: exception

View File

@ -33,7 +33,13 @@ namespace
{ {
try try
{ {
throw Alepha::build_exception< Alepha::TaggedAllocationException< struct tag > >( "This is my allocation exception" ); auto exc= Alepha::build_exception< Alepha::TaggedAllocationException< struct tag > >( "This is my allocation exception" );
static_assert( std::is_same_v< decltype( exc )::grade_type, Alepha::Exception > );
static_assert( std::is_same_v< Alepha::AnyTaggedException::grade_type, Alepha::Exception > );
static_assert( std::is_same_v< typename Alepha::TaggedException< struct bob >::grade_type, Alepha::Exception > );
static_assert( std::is_same_v< typename Alepha::TaggedThrowable< struct bob >::grade_type, Alepha::Throwable > );
static_assert( std::is_same_v< typename Alepha::TaggedViolation< struct bob >::grade_type, Alepha::Violation > );
throw exc;
} }
catch( const CatchException &ex ) catch( const CatchException &ex )
{ {
@ -76,19 +82,24 @@ namespace
return true; return true;
}; };
"catch"_test <=catchable< Alepha::TaggedAllocationException< tag >, std::bad_alloc >; "catch.std::bad_alloc"_test <=catchable< Alepha::TaggedAllocationException< tag >, std::bad_alloc >;
"catch"_test <=catchable< Alepha::TaggedAllocationException< tag >, std::exception >; "catch.std::exception"_test <=catchable< Alepha::TaggedAllocationException< tag >, std::exception >;
"catch"_test <=catchable< Alepha::TaggedAllocationException< tag >, Alepha::Throwable >; "catch.Alepha::Throwable"_test <=catchable< Alepha::TaggedAllocationException< tag >, Alepha::Throwable >;
"catch"_test <=catchable< Alepha::TaggedAllocationException< tag >, Alepha::AnyTaggedThrowable >; "catch.Alepha::AnyTaggedThrowable"_test <=catchable< Alepha::TaggedAllocationException< tag >, Alepha::AnyTaggedThrowable >;
"catch"_test <=catchable< Alepha::TaggedAllocationException< tag >, Alepha::TaggedThrowable< tag > >; "catch.Alepha::TaggedThrowable"_test <=catchable< Alepha::TaggedAllocationException< tag >, Alepha::TaggedThrowable< tag > >;
"catch"_test <=catchable< Alepha::TaggedAllocationException< tag >, Alepha::Exception >; "catch.Alepha::Exception"_test <=catchable< Alepha::TaggedAllocationException< tag >, Alepha::Exception >;
"catch"_test <=catchable< Alepha::TaggedAllocationException< tag >, Alepha::AnyTaggedException >; "catch.Alepha::AnyTaggedException"_test <=catchable< Alepha::TaggedAllocationException< tag >, Alepha::AnyTaggedException >;
"catch"_test <=catchable< Alepha::TaggedAllocationException< tag >, Alepha::TaggedException< tag > >; "catch.Alepha::TaggedException"_test <=catchable< Alepha::TaggedAllocationException< tag >, Alepha::TaggedException< tag > >;
"catch"_test <=catchable< Alepha::TaggedAllocationException< tag >, Alepha::AllocationThrowable >; "catch.Alepha::AllocationThrowable"_test <=catchable< Alepha::TaggedAllocationException< tag >, Alepha::AllocationThrowable >;
"catch"_test <=catchable< Alepha::TaggedAllocationException< tag >, Alepha::AnyTaggedAllocationThrowable >; "catch.Alepha::AnyTaggedAllocationThrowable"_test <=catchable< Alepha::TaggedAllocationException< tag >, Alepha::AnyTaggedAllocationThrowable >;
"catch"_test <=catchable< Alepha::TaggedAllocationException< tag >, Alepha::TaggedAllocationThrowable< tag > >; "catch.Alepha::TaggedAllocationThrowable"_test <=catchable< Alepha::TaggedAllocationException< tag >, Alepha::TaggedAllocationThrowable< tag > >;
"catch"_test <=catchable< Alepha::TaggedAllocationException< tag >, Alepha::AllocationException >; "catch.Alepha::AllocationException"_test <=catchable< Alepha::TaggedAllocationException< tag >, Alepha::AllocationException >;
"catch"_test <=catchable< Alepha::TaggedAllocationException< tag >, Alepha::AnyTaggedAllocationException >; "catch.Alepha::AnyTaggedAllocationException"_test <=catchable< Alepha::TaggedAllocationException< tag >, Alepha::AnyTaggedAllocationException >;
"catch"_test <=catchable< Alepha::TaggedAllocationException< tag >, Alepha::TaggedAllocationException< tag > >; "catch.Alepha::TaggedAllocationException"_test <=catchable< Alepha::TaggedAllocationException< tag >, Alepha::TaggedAllocationException< tag > >;
"size_probe"_test <=[]
{
std::cout << "Size: " << sizeof( Alepha::build_exception< Alepha::TaggedAllocationException< tag > >( "Message" ) ) << std::endl;
};
}; };
} }