|
Boost : |
From: Jesse Jones (jesjones_at_[hidden])
Date: 2001-04-17 21:23:32
>I've uploaded an updated header and a test file.
I just looked it over and I have some comments for you:
1) I think it'd be a good idea if Stack tested to see if nested
checks work properly. For example:
int& top()
{
BOOST_CHECK_INVARIANTS();
return _stack[_top];
}
void push(int i)
{
BOOST_REQUIRE(_top < _capacity);
BOOST_CHECK_INVARIANTS();
BOOST_OBSERVE(unsigned, _top);
do_push(i);
BOOST_ENSURE(BOOST_OLD(_top) == _top - 1);
BOOST_ENSURE(_stack[_top - 1] == i);
}
private:
void do_push(int i)
{
// break the invariant
unsigned old_cap = _capacity;
_capacity = 0;
// call a public method: invariant should not fire
top() = i;
++_top;
// restore the invariant
_capacity = old_cap;
}
2) If you make this change you'll see that the check_invariants
function is buggy. It should look more like this:
public: inline void check_invariants(bool entering) const { \
unsigned& count = _boost_invariant_count.get(); \
if (entering) { if (++count > 1) return; } \
if (!entering) { if (--count > 0) return; }
(I also switched the argument to a bool, but that's no big deal).
3) The check_invariants class is overly complex and does heap
allocations which is pretty nasty. You can fix that by using that
voodoo I mentioned earlier:
class check_invariants {
public:
template <typename T>
check_invariants(const T* object) {
_object = object;
_invoker = invoke<T>;
_invoker(_object, true);
}
~check_invariants() {
_invoker(_object, false);
}
template <class T>
static void invoke(const void* object, bool entering) {
void (T::*method)(bool) const = &T::check_invariants;
(static_cast<const T*>(object)->*method)(entering);
}
private:
void (*_invoker)(const void*, bool);
const void* _object;
};
4) It'd be nice if main printed a message for the expected errors.
You might also want to wrap all of main in a try/catch block to catch
any unexpected errors.
5) The code is setup so that the invariant is always an inline method
in the header. I really don't like this: invariants are often too big
to be good candidates for inlining and I don't want to touch a header
every time I change an invariant.
One possible solution is to get rid of BOOST_BEGIN_INVARIANTS
/BOOST_END_INVARIANTS, and add BOOST_SETUP_INVARAINT:
#define BOOST_SETUP_INVARAINT \
mutable boost::invariant_count _boost_invariant_count; \
void check_invariants(bool entering) const { \
unsigned& count = _boost_invariant_count.get(); \
if (entering) { if (++count > 1) return; } \
if (!entering) { if (--count > 0) return; } \
this->invariant(); \
}
In the header clients have to add " BOOST_SETUP_INVARAINT;" and
declare the invariant method. In the cpp they define the invariant
method as a normal method and use BOOST_INVARIANT to do the checks.
-- Jesse
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk