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
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

View file

@ -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;