Boost logo

Boost :

From: John Torjo (john_at_[hidden])
Date: 2003-05-08 06:21:15


> > 4. In case an assertion has failed, two actions will occur:
> > - first, the assertion will be logged
> > - second, a message will be show to the user, and the user can choose
from
> > multiple actions, like Ignore, Retry, Abort, etc.
> >
> > These are SEPARATE actions, and are both customizable.
> > Customization can happen at RUNTIME.
>
> In my own assert library I added options for logging, but in the end never
> used that and always had it throw an exception. This is nice for two
reasons:
> 1. MFC debugger catches it and allows me to go back up the call stack
to
> where the assert happened. (I'm still struggling with gdb but I think it
can
> be set to do the same?)
>
> 2. Boost::test will catch the exception; if I want to test a function
> asserts with bad inputs I can use BOOST_CHECK_THROW.
>
> So I'd like to see three separate actions, and be allowed to switch off
the
> first two (with a compile-time flag).
>
> Given that, I'm wondering if instead of hard-coding two or three actions
> that can be customized if you wouldn't be better allowing any number of
> callbacks to be registered, and then supplying some ready-made callbacks
to do:
> 1. logging
> 2. user prompting
> 3. throw exceptions

Basically, the N actions will be able to be customized (that is, you'll be
able to add your own levels).
What you say, about throwing an exception, also will be possible, since
you'll be able to set a "handler" for each level of assertion.
So, for instance, for assertions with level Debug (default), you can set a
handler that will throw. Just like that! At run-time! (you can leave the
default handler for assertions, that will prompt the user, etc. , or just
set your own, which can throw an exception).

> > ------------------------
> > 10. specifying all ASSERT's arguments
> >
> > When using BOOST_ASSERT, you should specify all arguments involved in
the
> > assert; otherwise, if the assertion fails, you might not get enough
> > information.
> >
> > Example:
> > // OK - all 3 params have been cared for
> > BOOST_ASSERT( (i > 1) || (j < 0) || (k != i) )(i)(j)(k);
> >
> > // bad - in case an assertion fails, not enough info is outputted!
> > // (k is not outputted)
> > BOOST_ASSERT( (i > 1) || (j < 0) || (k != i) )(i)(j);
> >
> > (compile-time switch, can be on/off; on by default)
>
> I didn't follow this one - are you saying the compiler will complain about
> the bad assert that doesn't include "(k)"? If you could do that you
wouldn't
> need the user to specify the variables at all would you?

Unfortunately, finding out if all of the arguments have been set or not
cannot happen at compile-time. I knew that ;-)
The switch is compile-time, meaning that:
- if on, at RUNTIME, I'll check if all arguments have been specified
- if off, I won't check anything.

Of course, I thought about this, and cannot find a solution that would allow
this checking to happen at the beginning of the program (in other words,
this will happen only the first time the code reaches an ASSERT). Anyway, if
you know of a way, please let me know!

>
>
> > We can have a "fatal error" level, in which case an exception could be
> > triggered.
>
> I thought all asserts should be considered fatal, so as suggested above I
> think BOOST_ASSERT should always throw an exception, and maybe you need
> BOOST_WARNING for non-fatal conditions you'd like logged or to inform the
> user about.

Not true (in my oppinion).
It happens that code might be "over-ASSERTed" (sometimes, some ASSERTs are
not needed). What has happened to me was that I developed some code, and
others have picked up and continued developing it. Some of the initial
constraints have been loosened, therefore, some of the ASSERTs became
unnecessary.

Had such an ASSERT throw, some handler might catch it, and this could go
undetected.
What do you think?

>
> > I'll allow for custom printing of variables (when an assertion fails).
> > ...
> > For instance, for a non-null pointer, you might want to print (some of)
its
> > contents. Or, for an STL container, to print its elements (or at least
its
> > size)
>
> This is interesting; what syntax do you have in mind for using this?
>
> For some types of objects it might be useful to specify a function to call
> if it asserts; I often have a debug_info() function in my classes. E.g.
> BOOST_ASSERT(obj.status()==0)(obj.debug_info())
>
> Will that work if debug_info() returns void?

It should.
I was thinking of a general class, like:

class print_obj
{
    // with some specializations
    template< class type>
    void do_print( const type & val)
    { ...}
};

In case you're wondering why a class and not a function, is because I would
like it to hold context. For example, you might want something like this:

ASSERT( *first != *last)(boost::assert_range)(first)(last);

In case the assertion fails, I could log the whole first-to-last range.
Had the printer been a function, this would have not been possible.

As a bonus, you'll be able to write/derive your own printer class that will
do the printing (and I'll provide a few to choose from ;-)).

>
> Darren
>
> P.S. I like everything else you proposed, and the BOOST_ASSERT name is
fine.

Thanks.

> Though if aiming for inclusion in C++ standard in the future maybe
> SMART_ASSERT or similar is better?

Yes, I do prefer SMART_ASSERT as well.

Best,
John


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk