Boost logo

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