|
Boost : |
From: Dan W. (danw_at_[hidden])
Date: 2004-01-06 20:33:01
Sorry it took me a while. Many problems of name collisions due to
multiple inheritance of the invariants base...
//-------------------------------------------------
//file: invariants.hpp
#ifndef INVARIANTS_HPP
#define INVARIANTS_HPP
/*
Terminology:
By the term 'invariants', it is meant relationships that must
hold true regarding the state of a class, at the time that a
public function is entered or exited.
By 'state' it is meant the combined values of its members.
By 'members' it is meant the "raw bits" of the class footprint.
Invariants-checks, as a debugging aid, may, but need not cover
all possible invariants. Expensive checks are better avoided,
as they will be invoked at every public function call.
Checks are only strictly necessary either at entry or at exit
points of functions, not both. See under 'Rationale'.
Invariants checks are not to be applied to private, protected
or const functions; nor, of course, to static functions; and
they are only compiled in debug mode.
Invariants must NOT themselves be thread-safe. More details on
this below, under Thread Safety.
Description:
Abstract class 'invariants' is imbues a derived class with a
basic mechanism for invariant checking, via inheritance.
Pure virtual function 'void verify_invariants() const' must be
defined in the derived class.
Nested class invariants::trigobj is a class whose destructor
triggers a call to invariants::verify_invariants(). It is to
be instantiated as the first automatic variable in every non-
-const public function of the derived class.
Rationale: The call to verify invariants could be made at the
entry and exit points of functions, but one of the two is
sufficient, provided the class does not have evil friends.
Checking only at exit was chosen as the policy in order to
catch a bug within the function where the bug resides, rather
than at the time of the next public function call.
This choice, however, would make the placing of calls to check
invariant more laborious and error-prone, as each function may
have multiple return points. Ergo, the use of a trigger class
to be instantiated first on the stack within each public, non-
-const function.
Invariants should also be checked after initialization and
before destruction. Functions 'void first_verify() const' and
'void last_verify() const' are provided for this purpose. The
call to first_verify can be made from the bodies of ctors, but
after any other state-modifying operations therein. The call
to last_verify may be placed as the first statement within the
bodies of destructors. A boolean member 'enabled_' is set to
true by first_verify, and to false by last_verify. Calls to
verify_invariants (triggered by the destructor of trigobj vars
upon returning from non-const public functions) should occur
while enabled_ is true, otherwise an internal assertion will
stop execution. Other internal assertions include checks to
ensure that enable_ isn't set to true (or false) repeatedly.
All member functions are no-throw, unless bad-alloc is thrown
during construction, which everybody knows, so I skipped the
no-throw comments.
Thread Safety:
Class invariants is not designed to be thread-safe, and should
not be. The rationale is that if the derived class itself is
thread-safe, so will class invariants within it. Otherwise,
there are two possibilities: either the derived class does not
need thread safety (as it may be in use within a single thread
environment, or only called from within one thread; or else
the derived class does need, but does not have, thread safety,
due to programmer error. In this last case, class invariants
will probably find the state of the object incoherent, at some
point, which will alert the programmer to the situation.
This last benefit would be lost if class invariants were itself
thread-safe.
To be done:
... nothing ...
*/
#define JUST_ME
#ifdef JUST_ME
# include "ensure.hpp"
#else
# include <cassert>
# define ENSURE(x) assert(x)
#endif
#ifdef _DEBUG
namespace boost { namespace DBC {
template <typename Derived>
struct invariants
{
typedef invariants<Derived> invars_t;
struct trigobj //instantiate in derived class public,
//non-const functions. May instantiate in const ones
//also, if paranoid. May NOT instantiate in private
//or protected functions!!!
{
trigobj( invariants const * pinv )
: pinv_( pinv )
{
ENSURE( pinv_ );
}
~trigobj()
{
ENSURE( pinv_ );
if( pinv_ )
pinv_->verf_invars();
pinv_ = 0;
}
private:
invariants const * pinv_;
trigobj(); //no default ctor.
trigobj( trigobj const & );
void operator=( trigobj const & );
};
//invariants base is copy-wise immutable...
invariants(invars_t const &) : enabled_(false) {}
invars_t & operator=(invars_t const &)
{
return *this;
}
void verf_invars() const
{
ENSURE( enabled_ ); //forgot to enable?
verify_invariants(this);
}
protected:
invariants() : enabled_(false) {}
void first_verify() const
{
ENSURE( ! enabled_ ); //enabled by ancestor ctor?
enabled_ = true;
verify_invariants(this);
}
void last_verify() const
{
ENSURE( enabled_ ); //forgot to ever enable?
verify_invariants(this);
enabled_ = false;
}
~invariants()
{
ENSURE( ! enabled_ ); //not disabled in derived dtor?
enabled_ = false;
}
mutable char enabled_;
private:
virtual void verify_invariants(invars_t const *) const = 0;
};
} }
# define INVARIANTS_DERIVED(x) x : public invariants<x>
# define _INVARIANTS_ friend struct invariants<type>; \
void verify_invariants(invariants<type> const *) const
# define INVARIANTS_CHECKED invariants<type>::trigobj TRIGOBJ(this);
# define FIRST_INVARIANTS_CHECK first_verify();
# define LAST_INVARIANTS_CHECK last_verify();
#else //release mode:
# define INVARIANTS_DERIVED
# define INVARIANTS template<int ZZZ> void zzz() const
# define INVARIANTS_CHECKED
# define FIRST_INVARIANTS_CHECK
# define LAST_INVARIANTS_CHECK
#endif
#endif
//-------------------------------------------------
//invariants_test.cpp
#include "invariants.hpp"
using namespace boost::DBC;
class INVARIANTS_DERIVED(my_class1)
{
typedef my_class1 type;
protected:
int a, b;
private:
_INVARIANTS_
{
ENSURE( a + b == 5 );
}
public:
my_class1() : a(2), b(3)
{
FIRST_INVARIANTS_CHECK
}
~my_class1()
{
LAST_INVARIANTS_CHECK
}
void f()
{
INVARIANTS_CHECKED
++a; --b;
}
void g()
{
INVARIANTS_CHECKED
++a;
}
};
class INVARIANTS_DERIVED(my_class2)
{
typedef my_class2 type;
protected:
int c, d;
private:
_INVARIANTS_
{
ENSURE( c + d == 9 );
}
public:
my_class2() : c(5), d(4)
{
FIRST_INVARIANTS_CHECK
}
~my_class2()
{
LAST_INVARIANTS_CHECK
}
void h()
{
INVARIANTS_CHECKED
++d;
}
void i()
{
INVARIANTS_CHECKED
--c; ++d;
}
};
class INVARIANTS_DERIVED(my_class3), public my_class1, public my_class2
{
typedef my_class3 type;
_INVARIANTS_
{
ENSURE( a + c == 7 );
ENSURE( b + d == 7 );
}
public:
void j()
{
INVARIANTS_CHECKED
f(); i();
}
void k()
{
INVARIANTS_CHECKED
h(); g();
}
void l()
{
INVARIANTS_CHECKED
++a;
}
};
int main()
{
my_class1 my_obj1;
my_obj1.f();
my_obj1.g(); //BOOM!
my_class2 my_obj2;
my_obj2.h(); //BOOM!
my_obj2.i();
my_class3 my_obj3;
my_obj3.f();
my_obj3.g(); //BOOM!
return 0;
}
//-------------------------------------------------
Tested on Digital Mars 8.38, but should compile on anything, IMHO.
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk