diff options
Diffstat (limited to 'shared/loki/Checker.h')
-rw-r--r-- | shared/loki/Checker.h | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/shared/loki/Checker.h b/shared/loki/Checker.h new file mode 100644 index 00000000..19350679 --- /dev/null +++ b/shared/loki/Checker.h @@ -0,0 +1,516 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// The Loki Library +// Copyright (c) 2008 Rich Sposato +// The copyright on this file is protected under the terms of the MIT license. +// +// Permission to use, copy, modify, distribute and sell this software for any +// purpose is hereby granted without fee, provided that the above copyright +// notice appear in all copies and that both that copyright notice and this +// permission notice appear in supporting documentation. +// +// The author makes no representations about the suitability of this software +// for any purpose. It is provided "as is" without express or implied warranty. +// +//////////////////////////////////////////////////////////////////////////////// + +// $Id$ + +/// @file Checker.h This file provides Loki's Checker facility. + + +// ---------------------------------------------------------------------------- + +#ifndef LOKI_CHECKER_H_INCLUDED +#define LOKI_CHECKER_H_INCLUDED + +#include <exception> // needed for calls to uncaught_exception. +#include <assert.h> + + +namespace Loki +{ + +/** @par ContractChecker and StaticChecker Overview + The ContractChecker and StaticChecker classes have two purposes: + - provide a mechanism by which programmers can determine which functions + violate class/data invariants, + - and determine which exception safety a function provides. + + @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 + 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 + return false. If the validator is a static member function, you can use it + with checkers in any function, but especially standalone functions and class + static functions. If the validator is a non-static member function, you can + use it only within non-static member functions. + + @par Exception Safety Levels + Years ago, David Abrahams formalized a framework for assessing the exception + safety level a function provides. His framework describes three levels of + guarantees. Any function which does not provide any of these levels is + considered unsafe. ContractChecker and StaticChecker determine a function's + safety level through the use of policy classes. Checker's policy classes can + show if a function provides any of these three guarantees. Since there is no + universal way to detect leaks, this facility provides no mechanism for finding + leaks, but users may create their own validators which do. StaticChecker's + policy classes only provide direct checking for the no-throw and invariant + guarantees. With some finesse, a programmer can write a validator for + StaticChecker that checks for the Strong guarantee. + + - No-throw guarantee: A function will not throw any exceptions. + - Strong guarantee: A function will not change data if an exception occurs. + (Which I call the no-change guarantee.) + - 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.) + */ + +// ---------------------------------------------------------------------------- + +/** @class CheckForNoThrow + + @par Exception Safety Level: + This exception-checking policy class for ContractChecker asserts if an + exception exists. Host classes can use this to show that a member function + provides the no-throw exception safety guarantees. + + @par Requirements For Host Class: + This policy imposes no requirements on a host class. + */ +template < class Host > +class CheckForNoThrow +{ +public: + + inline explicit CheckForNoThrow( const Host * ) {} + + inline bool Check( const Host * ) const + { + const bool okay = ( !::std::uncaught_exception() ); + assert( okay ); + return okay; + } +}; + +// ---------------------------------------------------------------------------- + +/** @class CheckForNoChange + + @par Exception Safety Level: + This exception-checking policy class for ContractChecker asserts only if a + copy of the host differs from the host object when an exception occurs. Host + classes can use this policy to show which member functions provide the strong + exception guarantee. + + @par Requirements: + This policy requires hosts to provide both the copy-constructor and the + equality operator, and is intended for classes with value semantics. + equality operator. + */ + +template < class Host > +class CheckForNoChange +{ +public: + + inline explicit CheckForNoChange( const Host * host ) : + m_compare( *host ) {} + + inline bool Check( const Host * host ) const + { + const bool okay = ( !::std::uncaught_exception() ) + || ( m_compare == *host ); + assert( okay ); + return okay; + } + +private: + Host m_compare; +}; + +// ---------------------------------------------------------------------------- + +/** @class CheckForNoChangeOrThrow + + @par Exception Safety Level: + This exception-checking policy class for ContractChecker asserts either if a + copy of the host differs from the original host object, or if an exception + occurs. Host classes can use this policy to show which member functions provide + the no-throw exception guarantee, and would never change data anyway. + + @par Requirements For Host Class: + This policy requires hosts to provide both the copy-constructor and the + equality operator, and is intended for classes with value semantics. + */ + +template < class Host > +class CheckForNoChangeOrThrow +{ +public: + + inline explicit CheckForNoChangeOrThrow( const Host * host ) : + m_compare( *host ) {} + + inline bool Check( const Host * host ) const + { + bool okay = ( !::std::uncaught_exception() ); + assert( okay ); + okay = ( m_compare == *host ); + assert( okay ); + return okay; + } + +private: + Host m_compare; +}; + +// ---------------------------------------------------------------------------- + +/** @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. + 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. + + @par Requirements For Host Class: + This policy requires hosts to provide both the copy-constructor and the + equality operator, and is intended for classes with value semantics. + */ + +template < class Host > +class CheckForEquality +{ +public: + + inline explicit CheckForEquality( const Host * host ) : + m_compare( *host ) {} + + inline bool Check( const Host * host ) const + { + const bool okay = ( m_compare == *host ); + assert( okay ); + return okay; + } + +private: + Host m_compare; +}; + +// ---------------------------------------------------------------------------- + +/** @class CheckForNothing + + @par Exception Safety Level: + This exception-checking policy class for ContractChecker does nothing when + called. Host classes can use this to show which member functions provide + neither the strong nor no-throw exception guarantees. The best guarantee such + functions can provide is that nothing gets leaked. + + @par Requirements For Host Class: + This policy imposes no requirements on a host class. + */ + +template < class Host > +class CheckForNothing +{ +public: + inline explicit CheckForNothing( const Host * ) {} + inline bool Check( const Host * ) const { return true; } +}; + +// ---------------------------------------------------------------------------- + +/** @class ContractChecker + This class determines if a function violated any class invariant, but it also + determines if a function fulfills its contract with client code. In the + "Design by Contract" paradigm, each function has certain pre-conditions and + post-conditions which may differ from the class invariants. This asserts if a + check for an invariant fails as well as if any pre- or post-condition fails. + It also demonstrate which exception safety level a function provides. + + @par Usage + -# Implement a function that checks each class invariant. The function must + have the signature similar to the Validator type. Something like: + "bool Host::IsValid( void ) const;" + - The function should return true if everything is okay, but false if + 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. + - If the function never throws, then use the CheckForNoThrow policy. + - If the function never changes any data members, then use CheckForEquality + policy. + - If the function's normal execution flow changes data, but must make sure + data remains unchanged when any exceptions occur, then use the + CheckForNoChange policy. + - Otherwise use the CheckInvariants policy. + -# Recompile a debug version of your program, run the program and all the unit + tests, and look for which assertions failed. + */ + +template +< + class Host, + template < class > class ExceptionPolicy +> +class ContractChecker : public ExceptionPolicy< Host > +{ + /// Shorthand for the ExceptionPolicy class. + typedef ExceptionPolicy< Host > Ep; + +public: + + /// Signature for the validation function. + typedef bool ( Host:: * Validator )( void ) const; + + /** The constructor makes sure the host is valid at the time the checker + was created, thus insuring the host object was not corrupt from the start. + @par host Pointer to host object. + @par validator Pointer to function that checks class invariants. + @par pre Optional pointer to function that checks pre-conditions. + @par post Optional pointer to function that checks post-conditions. + */ + inline ContractChecker( const Host * host, Validator validator, + Validator pre = 0, Validator post = 0 ) : + Ep( host ), + m_host( host ), + m_validator( validator ), + m_pre( pre ), + m_post( post ) + { + assert( Check() ); + if ( 0 != m_pre ) + assert( ( m_host->*( m_pre ) )() ); + } + + /** The destructor checks if any Host invariants failed, and then calls the + ExceptionPolicy's Check function to determine what to do in case of an + exception. + */ + inline ~ContractChecker( void ) + { + assert( Check() ); + if ( 0 != m_post ) + assert( ( m_host->*( m_post ) )() ); + assert( Ep::Check( m_host ) ); + } + + /** This first checks the invariants for ContractChecker, and then calls the + validator function for the host to make sure no class invariants were + broken by the host within the Host's member function body. The host + member function can call Check directly to verify the object remains valid + at any time. This does not care if the pre- and post-condition validator + pointers are null since a host class may pass in NULL pointers for either + to indicate the pre-conditions or post-conditions are the same as the + overall class invariants. + */ + inline bool Check( void ) const + { + assert( 0 != this ); + assert( 0 != m_host ); + assert( 0 != m_validator ); + // Now that this confirms the pointers to the host and validation + // functions are not null, go ahead and validate the host object. + const bool okay = ( m_host->*( m_validator ) )(); + assert( okay ); + return okay; + } + +private: + + /// Default constructor is not implemented. + ContractChecker( void ); + /// Copy constructor is not implemented. + ContractChecker( const ContractChecker & ); + /// Copy-assignment operator is not implemented. + ContractChecker & operator = ( const ContractChecker & ); + + /// Pointer to the host object. + const Host * m_host; + + /// Pointer to member function that checks Host object's invariants. + Validator m_validator; + + /// Pointer to member function that checks Host object's pre-conditions. + Validator m_pre; + + /// Pointer to member function that checks Host object's post-conditions. + Validator m_post; + +}; + +// ---------------------------------------------------------------------------- + +/** @class CheckStaticForNoThrow + + @par Exception Safety Level: + This exception-checking policy class for StaticChecker asserts if an exception + exists. Functions can use this to show they provide the no-throw exception + safety guarantee. + */ +class CheckStaticForNoThrow +{ +public: + inline bool Check( void ) + { + const bool okay = !::std::uncaught_exception(); + assert( okay ); + return okay; + } +}; + +// ---------------------------------------------------------------------------- + +/** @class CheckStaticForNothing + + @par Exception Safety Level: + This exception-checking policy class for StaticChecker does nothing when called. + Functions can use this to show they might provide the weak exception guarantee. + The best guarantee such functions can provide is that nothing gets leaked. + */ +class CheckStaticForNothing +{ +public: + inline bool Check( void ) { return true; } +}; + +// ---------------------------------------------------------------------------- + +/** @class StaticChecker + This class checks if a function provides the no-throw exception safety level + and if the function violated any invariants. Invariants for stand-alone and + static functions act as pre-conditions and post-conditions. + + @par Usage + -# Implement a function that checks the invariants associated with a function, + or with the static data for a class. The function must + have the signature similar to the Validator type. Something like: + "static bool Host::StaticIsValid( void );" or "bool IsOkay( void );" + - 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. + - If the function never throws, then use the CheckForNoThrow policy. + - Otherwise use the CheckInvariants policy. + -# Recompile a debug version of your program, run it, and see if an assertion + fails. + */ + +template +< + class ExceptionPolicy +> +class StaticChecker : public ExceptionPolicy +{ + /// Shorthand for the ExceptionPolicy class. + typedef ExceptionPolicy Ep; + +public: + + /// Signature for the validation function. + typedef bool ( * Validator )( void ); + + /** The constructor makes sure the host is valid at the time the checker + was created, thus insuring the host object was not corrupt from the start. + @par validator Pointer to function that checks class invariants. + @par pre Optional pointer to function that checks pre-conditions. + @par post Optional pointer to function that checks post-conditions. + */ + inline explicit StaticChecker( Validator validator, + Validator pre = 0, Validator post = 0 ) : + Ep(), + m_validator( validator ), + m_pre( pre ), + m_post( post ) + { + assert( Check() ); + if ( 0 != m_pre ) + assert( m_pre() ); + } + + /** The destructor checks if any Host invariants failed, and then calls the + ExceptionPolicy's Check function to determine what to do in case of an + exception. + */ + inline ~StaticChecker( void ) + { + assert( Check() ); + if ( 0 != m_post ) + assert( m_post() ); + assert( Ep::Check() ); + } + + /** This first checks its own invariants, and then calls the validator + function to make sure no invariants were broken by the function which + created this checker. That function can call Check directly to verify the + data remains valid at any time. This does not care if the pre- and post- + condition validator pointers are null since a host class may pass in NULL + pointers for either to indicate the pre-conditions or post-conditions are + the same as the overall class invariants. + */ + inline bool Check( void ) const + { + assert( 0 != this ); + assert( 0 != m_validator ); + // Now that this confirms the pointers to the host and validation + // functions are not null, go ahead and validate the host object. + const bool okay = m_validator(); + assert( okay ); + return okay; + } + +private: + + /// Default constructor is not implemented. + StaticChecker( void ); + /// Copy constructor is not implemented. + StaticChecker( const StaticChecker & ); + /// Copy-assignment operator is not implemented. + StaticChecker & operator = ( const StaticChecker & ); + + /// Pointer to member function that checks Host object's invariants. + Validator m_validator; + + /// Pointer to member function that checks Host object's pre-conditions. + Validator m_pre; + + /// Pointer to member function that checks Host object's post-conditions. + Validator m_post; + +}; + +// ---------------------------------------------------------------------------- + +}; // end namespace Loki + +#endif |