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
This commit is contained in:
rich_sposato 2009-03-31 21:56:08 +00:00
parent 433534f136
commit e35ba7770e
2 changed files with 130 additions and 58 deletions

View file

@ -39,7 +39,7 @@ namespace Loki
@par Class & Data Invariants @par Class & Data Invariants
The ContractChecker and StaticChecker define invariants as "expressions that 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 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. 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 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 - 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 in a valid state if an exception occurs. (Which I call either the no-leak
or no-break guarantee depending on context.) 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: @par Requirements For Host Class:
This policy imposes no requirements on a host class. This policy imposes no requirements on a host class.
*/ */
template < class Host > template < class Host >
class CheckForNoThrow class CheckForNoThrow
{ {
@ -173,7 +208,8 @@ private:
/** @class CheckForEquality /** @class CheckForEquality
@par Exception Safety Level: @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 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. data members, and thereby provide the strong exception safety level by default.
@ -241,23 +277,23 @@ public:
something is wrong. something is wrong.
- Or it could assert if anything is wrong. - Or it could assert if anything is wrong.
- Ideally, it should be private. - Ideally, it should be private.
-# Implement similar functions to check for pre-conditions and post-conditions. - It should never throw an exception.
Functions which verify pre-conditions and post-conditions do not need to -# Optionally implement similar functions to check for pre-conditions and post-
check all class invariants, just conditions specific to certain public conditions. Functions which verify pre-conditions and post-conditions do
functions in the host class. not need to check all class invariants, just conditions specific to certain
-# Declare some typedef's inside the class declaration like these. Make one public functions in the host class. The post-condition function should never
typedef for each exception policy you use. I typedef'ed the CheckForNothing throw exceptions.
policy as CheckInvariants because even if a function can't provide either the -# Add this line in the class declaration:
no-throw nor the no-change policies, it should still make sure the object - typedef ::Loki::CheckFor< Host > CheckFor;
remains in a valid state. -# Add one of these lines at the top of various class member functions to
- typedef ::Loki::ContractChecker< Host, ::Loki::CheckForNoThrow > CheckForNoThrow; construct a checker near the top of each public function. You may also pass
- typedef ::Loki::ContractChecker< Host, ::Loki::CheckForNoChange > CheckForNoChange; in pointers to functions which check pre- and post-conditions.
- typedef ::Loki::ContractChecker< Host, ::Loki::CheckForEquality > CheckForEquality; - CheckFor::NoChangeOrThrow checker( this, &Host::IsValid );
- typedef ::Loki::ContractChecker< Host, ::Loki::CheckForNothing > CheckInvariants; - CheckFor::NoThrow checker( this, &Host::IsValid );
-# Construct a checker near the top of each member function - except in the - CheckFor::NoChange checker( this, &Host::IsValid );
validator member function. Pass the this pointer and the address of your - CheckFor::Equality checker( this, &Host::IsValid );
validator function into the checker's constructor. You may also pass in pointers - CheckFor::Invariants checker( this, &Host::IsValid );
to function which check pre- and post-conditions. -# 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 throws, then use the CheckForNoThrow policy.
- If the function never changes any data members, then use CheckForEquality - If the function never changes any data members, then use CheckForEquality
policy. 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 /** @class CheckStaticForNoThrow
@par Exception Safety Level: @par Exception Safety Level:
@ -410,18 +466,25 @@ public:
- The function should return true if everything is okay, but false if - The function should return true if everything is okay, but false if
something is wrong. something is wrong.
- Or it could assert if anything is wrong. - Or it could assert if anything is wrong.
-# If the checker is for static functions within a class, declare typedef's - But it should never throw an exception.
inside the class declaration like these. Make one typedef for each policy -# If the checker validates static functions within a class, add this line to
you use. I typedef'ed the CheckForNothing policy as CheckInvariants because the class declaration.
even if a function can't provide the no-throw guarantee, it should still - typedef ::Loki::CheckStaticFor CheckStaticFor;
make sure that static data remains in a valid state. -# Construct a checker near the top of each member function using one of
- typedef ::Loki::StaticChecker< ::Loki::CheckForNoThrow > CheckStaticForNoThrow; these lines:
- typedef ::Loki::StaticChecker< ::Loki::CheckForNothing > CheckStaticInvariants; - CheckStaticFor::NoThrow checker( &Host::StaticIsValid );
-# Construct a checker near the top of each member function - except in the - CheckStaticFor::Invariants checker( &Host::StaticIsValid );
validator member function. Pass the address of your validator function into -# These guidelines can help you decide which exception policy to use within
the checker's constructor. each function.
- If the function never throws, then use the CheckForNoThrow policy. - If the function never throws, then use the CheckForNoThrow policy.
- Otherwise use the CheckInvariants 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 -# Recompile a debug version of your program, run it, and see if an assertion
fails. 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 }; // end namespace Loki
#endif #endif

View file

@ -92,16 +92,11 @@ private:
/// This can be used to validate pre-conditions and post-conditions. /// This can be used to validate pre-conditions and post-conditions.
bool IsValidFull( void ) const; bool IsValidFull( void ) const;
// These lines show how to declare checkers for non-static functions in a host class. // This shows how to declare checkers for non-static functions in a host class.
typedef ::Loki::ContractChecker< Thingy, ::Loki::CheckForNoThrow > CheckForNoThrow; typedef ::Loki::CheckFor< Thingy > CheckFor;
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;
// These lines show how to declare checkers for static functions of a host class. // This shows how to declare checkers for static functions of a host class.
typedef ::Loki::StaticChecker< ::Loki::CheckStaticForNoThrow > CheckStaticForNoThrow; typedef ::Loki::CheckStaticFor CheckStaticFor;
typedef ::Loki::StaticChecker< ::Loki::CheckStaticForNothing > CheckStaticInvariants;
typedef ::std::vector< unsigned int > IntBlock; 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. // This example shows how static functions can use a no-throw checkers.
void Thingy::ChangeThat( void ) void Thingy::ChangeThat( void )
{ {
CheckStaticForNoThrow checker( &Thingy::StaticIsValid ); CheckStaticFor::NoThrow checker( &Thingy::StaticIsValid );
(void)checker; (void)checker;
s_value--; s_value--;
} }
@ -130,7 +125,7 @@ void Thingy::ChangeThat( void )
// This example shows how static functions can use an invariant checker. // This example shows how static functions can use an invariant checker.
unsigned int Thingy::GetThat( void ) unsigned int Thingy::GetThat( void )
{ {
CheckStaticInvariants checker( &Thingy::StaticIsValid ); CheckStaticFor::Invariants checker( &Thingy::StaticIsValid );
(void)checker; (void)checker;
return s_value; return s_value;
} }
@ -142,7 +137,7 @@ Thingy::Thingy( unsigned int value ) :
m_value( value ), m_value( value ),
m_counts() m_counts()
{ {
CheckInvariants checker( this, &Thingy::IsValid ); CheckFor::Invariants checker( this, &Thingy::IsValid );
(void)checker; (void)checker;
} }
@ -152,7 +147,7 @@ Thingy::Thingy( const Thingy & that ) :
m_value( that.m_value ), m_value( that.m_value ),
m_counts( that.m_counts ) m_counts( that.m_counts )
{ {
CheckInvariants checker( this, &Thingy::IsValid ); CheckFor::Invariants checker( this, &Thingy::IsValid );
(void)checker; (void)checker;
} }
@ -160,7 +155,7 @@ Thingy::Thingy( const Thingy & that ) :
Thingy & Thingy::operator = ( const Thingy & that ) Thingy & Thingy::operator = ( const Thingy & that )
{ {
CheckInvariants checker( this, &Thingy::IsValid ); CheckFor::Invariants checker( this, &Thingy::IsValid );
(void)checker; (void)checker;
if ( &that != this ) if ( &that != this )
{ {
@ -184,9 +179,9 @@ Thingy::~Thingy( void )
// A swap function gets 2 checkers - one for this, and another for that. // A swap function gets 2 checkers - one for this, and another for that.
void Thingy::Swap( Thingy & that ) void Thingy::Swap( Thingy & that )
{ {
CheckInvariants checker1( this, &Thingy::IsValid ); CheckFor::Invariants checker1( this, &Thingy::IsValid );
(void)checker1; (void)checker1;
CheckInvariants checker2( &that, &Thingy::IsValid ); CheckFor::Invariants checker2( &that, &Thingy::IsValid );
(void)checker2; (void)checker2;
const IntBlock counts( m_counts ); const IntBlock counts( m_counts );
@ -216,7 +211,7 @@ void Thingy::DoSomethingEvil( void )
// This example shows how to use the no-throw checker. // This example shows how to use the no-throw checker.
unsigned int Thingy::GetValue( void ) const unsigned int Thingy::GetValue( void ) const
{ {
CheckForNoThrow checker( this, &Thingy::IsValid ); CheckFor::NoThrow checker( this, &Thingy::IsValid );
(void)checker; (void)checker;
return m_value; return m_value;
} }
@ -226,7 +221,7 @@ unsigned int Thingy::GetValue( void ) const
// This example shows how to use the equality checker. // This example shows how to use the equality checker.
unsigned int Thingy::DoSomething( bool doThrow ) const unsigned int Thingy::DoSomething( bool doThrow ) const
{ {
CheckForEquality checker( this, &Thingy::IsValid ); CheckFor::Equality checker( this, &Thingy::IsValid );
(void)checker; (void)checker;
if ( doThrow ) if ( doThrow )
throw ::std::logic_error( "Test Exception." ); 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. // This example shows how to use the no-change checker.
void Thingy::DoSomethingElse( void ) const void Thingy::DoSomethingElse( void ) const
{ {
CheckForNoChange checker( this, &Thingy::IsValid ); CheckFor::NoChange checker( this, &Thingy::IsValid );
(void)checker; (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 // 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 pre-condition validator. Ths post-condition validator just makes sure
// the container has at least 1 element. // 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 ); 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 // 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- // 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. // 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 ) if ( m_counts.size() <= index )
return 0; return 0;
const unsigned int count = m_counts[ index ]; 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 // 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 pre-condition validator. Ths post-condition validator just makes sure
// the container has no elements. // 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(); 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 ) void DoSomething( void )
{ {
// This example shows how to use a checker in a stand-alone function. // This example shows how to use a checker in a stand-alone function.
CheckStaticForNoThrow checker( &AllIsValid ); ::Loki::CheckStaticFor::NoThrow checker( &AllIsValid );
(void)checker; (void)checker;
Thingy::ChangeThat(); Thingy::ChangeThat();
} }
@ -355,6 +344,8 @@ void ThrowTest( void )
int main( unsigned int argc, const char * const argv[] ) int main( unsigned int argc, const char * const argv[] )
{ {
(void)argc;
(void)argv;
try try
{ {
cout << "Just before call to ThrowTest." << endl; cout << "Just before call to ThrowTest." << endl;