Search code examples
c++templatesdesign-by-contractobject-lifetimeinvariants

checking invariants in C++


Are there any established patterns for checking class invariants in C++?

Ideally, the invariants would be automatically checked at the beginning and at the end of each public member function. As far as I know, C with classes provided special before and after member functions, but unfortunately, design by contract wasn't quite popular at the time and nobody except Bjarne used that feature, so he removed it.

Of course, manually inserting check_invariants() calls at the beginning and at the end of each public member function is tedious and error-prone. Since RAII is the weapon of choice to deal with exceptions, I came up with the following scheme of defining an invariance checker as the first local variable, and that invariance checker checks the invariants both at construction and destruction time:

template <typename T>
class invariants_checker
{
    const T* p;

public:

    invariants_checker(const T* p) : p(p)
    {
        p->check_invariants();
    }

    ~invariants_checker()
    {
        p->check_invariants();
    }
};

void Foo::bar()
{
    // class invariants checked by construction of _
    invariants_checker<Foo> _(this);

    // ... mutate the object

    // class invariants checked by destruction of _
}

Question #0: I suppose there is no way to declare an unnamed local variable? :)

We would still have to call check_invariants() manually at the end of the Foo constructor and at the beginning of the Foo destructor. However, many constructor bodies and destructor bodies are empty. In that case, could we use an invariants_checker as the last member?

#include <string>
#include <stdexcept>

class Foo
{
    std::string str;
    std::string::size_type cached_length;
    invariants_checker<Foo> _;

public:

    Foo(const std::string& str)
    : str(str), cached_length(str.length()), _(this) {}

    void check_invariants() const
    {
        if (str.length() != cached_length)
            throw std::logic_error("wrong cached length");
    }

    // ...
};

Question #1: Is it valid to pass this to the invariants_checker constructor which immediately calls check_invariants via that pointer, even though the Foo object is still under construction?

Question #2: Do you see any other problems with this approach? Can you improve it?

Question #3: Is this approach new or well-known? Are there better solutions available?


Solution

  • Answer #0: You can have unnamed local variables, but you give up control over the life time of the object - and the whole point of the object is because you have a good idea when it goes out of scope. You can use

    void Foo::bar()
    {
        invariants_checker<Foo>(this); // goes out of scope at the semicolon
        new invariants_checker<Foo>(this); // the constructed object is never destructed
        // ...
    }
    

    but neither is what you want.

    Answer #1: No, I believe it's not valid. The object referenced by this is only fully constructed (and thus starts to exist) when the constructor finished. You're playing a dangerous game here.

    Answer #2 & #3: This approach is not new, a simple google query for e.g. "check invariants C++ template" will yield a lot of hits on this topic. In particular, this solution can be improved further if you don't mind overloading the -> operator, like this:

    template <typename T>
    class invariants_checker {
    public:
      class ProxyObject {
      public:
        ProxyObject(T* x) : m(x) { m->check_invariants(); }
        ~ProxyObject() { m->check_invariants(); }
        T* operator->() { return m; }
        const T* operator->() const { return m; }
      private:
        T* m;
      };
    
    invariants_checker(T* x) : m(x) { }
    
    ProxyObject operator->() { return m; } 
    const ProxyObject operator->() const { return m; }
    
    private:
       T* m;
    };
    

    The idea is that for the duration of a member function call, you create an anonymous proxy object which performs the check in its constructor and destructor. You can use the above template like this:

    void f() {
      Foo f;
      invariants_checker<Foo> g( &f );
      g->bar(); // this constructs and destructs the ProxyObject, which does the checking
    }