From e35ba7770e36e2798d875946f90f32c5425ddb4d Mon Sep 17 00:00:00 2001 From: rich_sposato Date: Tue, 31 Mar 2009 21:56:08 +0000 Subject: [PATCH] Added structs to declare typedefs for various exception policies. git-svn-id: svn://svn.code.sf.net/p/loki-lib/code/trunk@1012 7ec92016-0320-0410-acc4-a06ded1c099a --- include/loki/Checker.h | 139 ++++++++++++++++++++++++++++++++--------- test/Checker/main.cpp | 49 ++++++--------- 2 files changed, 130 insertions(+), 58 deletions(-) diff --git a/include/loki/Checker.h b/include/loki/Checker.h index 1935067..2954020 100644 --- a/include/loki/Checker.h +++ b/include/loki/Checker.h @@ -39,7 +39,7 @@ namespace Loki @par Class & Data Invariants The ContractChecker and StaticChecker define invariants as "expressions that - are true for particular data". They uses a function which returns true if all + are true for particular data". They call a function which returns true if all data are valid, and returns false if any datum is invalid. This is called the validator function, and the host class or function provides a pointer to it. The validator could also assert for any invariant which fails rather than @@ -67,6 +67,40 @@ namespace Loki - Basic guarantee: A function will not leak resources and data will remain in a valid state if an exception occurs. (Which I call either the no-leak or no-break guarantee depending on context.) + + @par Writing Your Own Policies + Loki provides several exception policies for ContractChecker. These policies + assert if an object changed or a function threw an exception. If you prefer + policies that log failures to a file, pop-up a message box, notify your unit- + test framework, or whatever else, you can easily write your own policies. + Your policy class should have two functions, a constructor and a Check + function, both of which accept a pointer to const instance of the host class. + Your policy class will become a base class of ContractChecker. Check should + return true if all is okay, and false if any failure was detected. Check + should never throw any exceptions since it is called from ContractChecker's + destructor. You may add other functions to the policy class. This code + snippet shows the signatures for the two required functions. + @code + class YourPolicy + { public: + explicit YourPolicy( const Host * ); + bool Check( const Host * ) const; + } + @endcode + Loki provides two exception policies for StaticChecker - one that asserts if + an exception occurred, and one that does not care about exceptions. You can + make your own policy to log failures, send an email, file a bug report, or do + whatever. Just write a policy with a default constructor and a function call + Check which look like those shown below. Make sure your Check function never + throws any exceptions. Any additional functions or features of the policy + are up to you. + @code + class YourPolicy + { public: + YourPolicy(); + bool Check() const; + } + @endcode */ // ---------------------------------------------------------------------------- @@ -81,6 +115,7 @@ namespace Loki @par Requirements For Host Class: This policy imposes no requirements on a host class. */ + template < class Host > class CheckForNoThrow { @@ -173,7 +208,8 @@ private: /** @class CheckForEquality @par Exception Safety Level: - This exception-checking policy class for ContractChecker asserts if a copy of the host differs from the host object regardless of whether an exception occurs. + This exception-checking policy class for ContractChecker asserts if a copy of + the host differs from the host object regardless of whether an exception occurs. Host classes can use this policy to show which member functions never change data members, and thereby provide the strong exception safety level by default. @@ -241,23 +277,23 @@ public: something is wrong. - Or it could assert if anything is wrong. - Ideally, it should be private. - -# Implement similar functions to check for pre-conditions and post-conditions. - Functions which verify pre-conditions and post-conditions do not need to - check all class invariants, just conditions specific to certain public - functions in the host class. - -# Declare some typedef's inside the class declaration like these. Make one - typedef for each exception policy you use. I typedef'ed the CheckForNothing - policy as CheckInvariants because even if a function can't provide either the - no-throw nor the no-change policies, it should still make sure the object - remains in a valid state. - - typedef ::Loki::ContractChecker< Host, ::Loki::CheckForNoThrow > CheckForNoThrow; - - typedef ::Loki::ContractChecker< Host, ::Loki::CheckForNoChange > CheckForNoChange; - - typedef ::Loki::ContractChecker< Host, ::Loki::CheckForEquality > CheckForEquality; - - typedef ::Loki::ContractChecker< Host, ::Loki::CheckForNothing > CheckInvariants; - -# Construct a checker near the top of each member function - except in the - validator member function. Pass the this pointer and the address of your - validator function into the checker's constructor. You may also pass in pointers - to function which check pre- and post-conditions. + - It should never throw an exception. + -# Optionally implement similar functions to check for pre-conditions and post- + conditions. Functions which verify pre-conditions and post-conditions do + not need to check all class invariants, just conditions specific to certain + public functions in the host class. The post-condition function should never + throw exceptions. + -# Add this line in the class declaration: + - typedef ::Loki::CheckFor< Host > CheckFor; + -# Add one of these lines at the top of various class member functions to + construct a checker near the top of each public function. You may also pass + in pointers to functions which check pre- and post-conditions. + - CheckFor::NoChangeOrThrow checker( this, &Host::IsValid ); + - CheckFor::NoThrow checker( this, &Host::IsValid ); + - CheckFor::NoChange checker( this, &Host::IsValid ); + - CheckFor::Equality checker( this, &Host::IsValid ); + - CheckFor::Invariants checker( this, &Host::IsValid ); + -# Use these guidelines to decide which policy to use inside which function: - If the function never throws, then use the CheckForNoThrow policy. - If the function never changes any data members, then use CheckForEquality policy. @@ -362,6 +398,26 @@ private: // ---------------------------------------------------------------------------- +/** @struct CheckFor + This struct declares types of checkers used to validate a host object. All of + Loki's exception-checking policies are named here as typedef's so host classes + have a one-stop convenience place for declaring them. If you write your own + exception policies for ContractChecker, you might want to also write a struct + similiar to CheckFor to conveniently declare all your policies. + */ +template < class Host > +struct CheckFor +{ + // These lines declare checkers for non-static functions in a host class. + typedef ContractChecker< Host, CheckForNoChangeOrThrow > NoChangeOrThrow; + typedef ContractChecker< Host, CheckForNoThrow > NoThrow; + typedef ContractChecker< Host, CheckForNoChange > NoChange; + typedef ContractChecker< Host, CheckForEquality > Equality; + typedef ContractChecker< Host, CheckForNothing > Invariants; +}; + +// ---------------------------------------------------------------------------- + /** @class CheckStaticForNoThrow @par Exception Safety Level: @@ -410,18 +466,25 @@ public: - The function should return true if everything is okay, but false if something is wrong. - Or it could assert if anything is wrong. - -# If the checker is for static functions within a class, declare typedef's - inside the class declaration like these. Make one typedef for each policy - you use. I typedef'ed the CheckForNothing policy as CheckInvariants because - even if a function can't provide the no-throw guarantee, it should still - make sure that static data remains in a valid state. - - typedef ::Loki::StaticChecker< ::Loki::CheckForNoThrow > CheckStaticForNoThrow; - - typedef ::Loki::StaticChecker< ::Loki::CheckForNothing > CheckStaticInvariants; - -# Construct a checker near the top of each member function - except in the - validator member function. Pass the address of your validator function into - the checker's constructor. + - But it should never throw an exception. + -# If the checker validates static functions within a class, add this line to + the class declaration. + - typedef ::Loki::CheckStaticFor CheckStaticFor; + -# Construct a checker near the top of each member function using one of + these lines: + - CheckStaticFor::NoThrow checker( &Host::StaticIsValid ); + - CheckStaticFor::Invariants checker( &Host::StaticIsValid ); + -# These guidelines can help you decide which exception policy to use within + each function. - If the function never throws, then use the CheckForNoThrow policy. - Otherwise use the CheckInvariants policy. + -# If the checker validates standalone functions, then just add one of these + lines at the top of the function. You may also want to write validation + functions which check pre-conditions and post-conditions of standalone + functions which you can pass into the checker as optional 2nd and 3rd + parameters. + - ::Loki::CheckStaticFor::NoThrow checker( &AllIsValid ); + - ::Loki::CheckStaticFor::Invariants checker( &AllIsValid ); -# Recompile a debug version of your program, run it, and see if an assertion fails. */ @@ -511,6 +574,24 @@ private: // ---------------------------------------------------------------------------- +/** @struct CheckStaticFor + This struct declares types of checkers used to validate standalone functions + and class static functions. All of Loki's exception-checking policies for + StaticChecker are named here as typedef's. If you write your own + exception policies for StaticChecker, you might want to also write a struct + similiar to CheckStaticFor to conveniently declare all your policies. + */ +struct CheckStaticFor +{ + // These lines declare checkers for static functions of a host class + // or for standalone functions outside any class or struct. + typedef StaticChecker< CheckStaticForNoThrow > NoThrow; + typedef StaticChecker< CheckStaticForNothing > Invariants; + +}; + +// ---------------------------------------------------------------------------- + }; // end namespace Loki #endif diff --git a/test/Checker/main.cpp b/test/Checker/main.cpp index f096246..0b864a0 100644 --- a/test/Checker/main.cpp +++ b/test/Checker/main.cpp @@ -92,16 +92,11 @@ private: /// This can be used to validate pre-conditions and post-conditions. bool IsValidFull( void ) const; - // These lines show how to declare checkers for non-static functions in a host class. - typedef ::Loki::ContractChecker< Thingy, ::Loki::CheckForNoThrow > CheckForNoThrow; - typedef ::Loki::ContractChecker< Thingy, ::Loki::CheckForNoChangeOrThrow > CheckForNoChangeOrThrow; - typedef ::Loki::ContractChecker< Thingy, ::Loki::CheckForNoChange > CheckForNoChange; - typedef ::Loki::ContractChecker< Thingy, ::Loki::CheckForEquality > CheckForEquality; - typedef ::Loki::ContractChecker< Thingy, ::Loki::CheckForNothing > CheckInvariants; + // This shows how to declare checkers for non-static functions in a host class. + typedef ::Loki::CheckFor< Thingy > CheckFor; - // These lines show how to declare checkers for static functions of a host class. - typedef ::Loki::StaticChecker< ::Loki::CheckStaticForNoThrow > CheckStaticForNoThrow; - typedef ::Loki::StaticChecker< ::Loki::CheckStaticForNothing > CheckStaticInvariants; + // This shows how to declare checkers for static functions of a host class. + typedef ::Loki::CheckStaticFor CheckStaticFor; typedef ::std::vector< unsigned int > IntBlock; @@ -120,7 +115,7 @@ unsigned int Thingy::s_value = 10; // This example shows how static functions can use a no-throw checkers. void Thingy::ChangeThat( void ) { - CheckStaticForNoThrow checker( &Thingy::StaticIsValid ); + CheckStaticFor::NoThrow checker( &Thingy::StaticIsValid ); (void)checker; s_value--; } @@ -130,7 +125,7 @@ void Thingy::ChangeThat( void ) // This example shows how static functions can use an invariant checker. unsigned int Thingy::GetThat( void ) { - CheckStaticInvariants checker( &Thingy::StaticIsValid ); + CheckStaticFor::Invariants checker( &Thingy::StaticIsValid ); (void)checker; return s_value; } @@ -142,7 +137,7 @@ Thingy::Thingy( unsigned int value ) : m_value( value ), m_counts() { - CheckInvariants checker( this, &Thingy::IsValid ); + CheckFor::Invariants checker( this, &Thingy::IsValid ); (void)checker; } @@ -152,7 +147,7 @@ Thingy::Thingy( const Thingy & that ) : m_value( that.m_value ), m_counts( that.m_counts ) { - CheckInvariants checker( this, &Thingy::IsValid ); + CheckFor::Invariants checker( this, &Thingy::IsValid ); (void)checker; } @@ -160,7 +155,7 @@ Thingy::Thingy( const Thingy & that ) : Thingy & Thingy::operator = ( const Thingy & that ) { - CheckInvariants checker( this, &Thingy::IsValid ); + CheckFor::Invariants checker( this, &Thingy::IsValid ); (void)checker; if ( &that != this ) { @@ -184,9 +179,9 @@ Thingy::~Thingy( void ) // A swap function gets 2 checkers - one for this, and another for that. void Thingy::Swap( Thingy & that ) { - CheckInvariants checker1( this, &Thingy::IsValid ); + CheckFor::Invariants checker1( this, &Thingy::IsValid ); (void)checker1; - CheckInvariants checker2( &that, &Thingy::IsValid ); + CheckFor::Invariants checker2( &that, &Thingy::IsValid ); (void)checker2; const IntBlock counts( m_counts ); @@ -216,7 +211,7 @@ void Thingy::DoSomethingEvil( void ) // This example shows how to use the no-throw checker. unsigned int Thingy::GetValue( void ) const { - CheckForNoThrow checker( this, &Thingy::IsValid ); + CheckFor::NoThrow checker( this, &Thingy::IsValid ); (void)checker; return m_value; } @@ -226,7 +221,7 @@ unsigned int Thingy::GetValue( void ) const // This example shows how to use the equality checker. unsigned int Thingy::DoSomething( bool doThrow ) const { - CheckForEquality checker( this, &Thingy::IsValid ); + CheckFor::Equality checker( this, &Thingy::IsValid ); (void)checker; if ( doThrow ) throw ::std::logic_error( "Test Exception." ); @@ -238,7 +233,7 @@ unsigned int Thingy::DoSomething( bool doThrow ) const // This example shows how to use the no-change checker. void Thingy::DoSomethingElse( void ) const { - CheckForNoChange checker( this, &Thingy::IsValid ); + CheckFor::NoChange checker( this, &Thingy::IsValid ); (void)checker; } @@ -250,7 +245,7 @@ void Thingy::AddCount( unsigned int count ) // but does not need to check pre-conditions, so it passes in a nullptr for // the pre-condition validator. Ths post-condition validator just makes sure // the container has at least 1 element. - CheckInvariants checker( this, &Thingy::IsValid, nullptr, &Thingy::IsValidFull ); + CheckFor::Invariants checker( this, &Thingy::IsValid, nullptr, &Thingy::IsValidFull ); m_counts.push_back( count ); } @@ -261,7 +256,7 @@ unsigned int Thingy::GetCount( unsigned int index ) const // This function's checker cares about class invariants and both the pre- and // post-conditions, so it passes in pointers for all 3 validators. The pre- // and post-conditions are both about making sure the container is not empty. - CheckForNoChangeOrThrow checker( this, &Thingy::IsValid, &Thingy::IsValidFull, &Thingy::IsValidFull ); + CheckFor::NoChangeOrThrow checker( this, &Thingy::IsValid, &Thingy::IsValidFull, &Thingy::IsValidFull ); if ( m_counts.size() <= index ) return 0; const unsigned int count = m_counts[ index ]; @@ -276,7 +271,7 @@ void Thingy::ClearCounts( void ) // but does not need to check pre-conditions, so it passes in a nullptr for // the pre-condition validator. Ths post-condition validator just makes sure // the container has no elements. - CheckForNoThrow checker( this, &Thingy::IsValid, nullptr, &Thingy::IsValidEmpty ); + CheckFor::NoThrow checker( this, &Thingy::IsValid, nullptr, &Thingy::IsValidEmpty ); m_counts.clear(); } @@ -328,16 +323,10 @@ bool AllIsValid( void ) // ---------------------------------------------------------------------------- -// These lines show how to declare checkers for standalone functions. -typedef ::Loki::StaticChecker< ::Loki::CheckStaticForNoThrow > CheckStaticForNoThrow; -typedef ::Loki::StaticChecker< ::Loki::CheckStaticForNothing > CheckStaticInvariants; - -// ---------------------------------------------------------------------------- - void DoSomething( void ) { // This example shows how to use a checker in a stand-alone function. - CheckStaticForNoThrow checker( &AllIsValid ); + ::Loki::CheckStaticFor::NoThrow checker( &AllIsValid ); (void)checker; Thingy::ChangeThat(); } @@ -355,6 +344,8 @@ void ThrowTest( void ) int main( unsigned int argc, const char * const argv[] ) { + (void)argc; + (void)argv; try { cout << "Just before call to ThrowTest." << endl;