Custom assertion macros

  • Follow


Do you use the standard assertion header, or do you roll your own? 
Pros/cons?



#include <string>

struct Assertion_error
{
         Assertion_error(char const* expr):
                 expression(expr) { }

         std::string expression;
};

#define ASSERT(expression) \
         if(!(expression)) { \
                 throw Assertion_error(#expression); \
         } else;

#include <iostream>

int main( )
{
         try
         {
                 ASSERT(3 == 5);
         }
         catch(Assertion_error const& e)
         {
                 std::cerr << "Assertion failed: "
                         << e.expression << '\n';
         }
}

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Jeffrey 11/7/2005 9:13:18 PM

Jeffrey Schwab skrev:

> Do you use the standard assertion header, or do you roll your own?

I roll my own, partly for historical reasons (I've never used the
assert macro for anything but toy programs).

> Pros/cons?
I have some nonportable code in there - activating the debugger if one
is available. I also have a finer grained assertion system, where some
"assertions" are allowed to stay in production-code. In that case they
can do some more advanced logging (stack-trace mainly). That's pretty
much it. .
Your example with catch-throw has also been part of my game. I've
skipped that for now. It should be possible to do what you have to do
with invoking the dangers of a stack-unwind.

/Peter
>
>
>
> #include <string>
>
> struct Assertion_error
> {
>          Assertion_error(char const* expr):
>                  expression(expr) { }
>
>          std::string expression;
> };
>
> #define ASSERT(expression) \
>          if(!(expression)) { \
>                  throw Assertion_error(#expression); \
>          } else;
>
> #include <iostream>
>
> int main( )
> {
>          try
>          {
>                  ASSERT(3 == 5);
>          }
>          catch(Assertion_error const& e)
>          {
>                  std::cerr << "Assertion failed: "
>                          << e.expression << '\n';
>          }
> }

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply peter 11/8/2005 10:39:44 AM


I use the C-style assertion for what they were intended for, assertions during
testing and debugging. That is assertions that will be removed in producion
code. For assertions that will remain in production code I believe it is a good
idea to use your own assertion handling that throws exceptions instead of
simply aborting. This makes it possible to handle assertion errors in
production code in a better way, also you could (I'm not saying you should)
have different typs of assertions depending on what you are asserting, allowing
more flexibility (such as RangeAssertion() that throws a range_error etc)

/Daniel Aarno

Jeffrey Schwab wrote:
> Do you use the standard assertion header, or do you roll your own?
> Pros/cons?
>
>
>
> #include <string>
>
> struct Assertion_error
> {
>          Assertion_error(char const* expr):
>                  expression(expr) { }
>
>          std::string expression;
> };
>
> #define ASSERT(expression) \
>          if(!(expression)) { \
>                  throw Assertion_error(#expression); \
>          } else;
>
> #include <iostream>
>
> int main( )
> {
>          try
>          {
>                  ASSERT(3 == 5);
>          }
>          catch(Assertion_error const& e)
>          {
>                  std::cerr << "Assertion failed: "
>                          << e.expression << '\n';
>          }
> }

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Daniel 11/8/2005 10:46:38 AM

I generally supplement the standard assertions with some of my own.  I
usually add a MSGASSERT(expr, msg) version which (depending upon the
OS) prints msg to std errror.  This can give a little more context to
some assertions.  I also have ASSERT() print out the expr that failed.
Though they don't get used much, I also usually add VERIFY() versions
of the above that leave the code in place in non debug, but print the
message/abort/pop into the debugger  in debug versions.  They are
useful for testing things like close() failures where you really don't
have much you can do in a failure anyway, but would like to know what's
going on.

joe


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Joe 11/8/2005 6:14:29 PM

jeff@schwabcenter.com (Jeffrey Schwab) wrote (abridged):
> Do you use the standard assertion header, or do you roll your own?
> Pros/cons?

I mostly use the one that comes with MFC, that understand the GUI and
about debuggers. It pops up a dialog that gives the option of continuing.

If I want continuing to lead to an exception, I code it explicitly:

     switch (v) {
     default: ASSERT( false ); throw "bad v";
     case 1: do_something(); break;
     case 2: do_something_else(); break;
     // ...
     }

-- Dave Harris, Nottingham, UK.

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply brangdon 11/9/2005 2:15:26 PM

"Jeffrey Schwab" <jeff@schwabcenter.com> wrote in message
news:14Jbf.2757$bU3.688187@twister.southeast.rr.com...
> Do you use the standard assertion header, or do you roll your own?
> Pros/cons?

Assertions are for developers, not for end users. The end users should never
see an assertion failure. Therefore assertions shouldn't throw exceptions, -
stack unwinding only hurts debugging. Sometimes people create more elaborate
macros than simple assert(), but it isn't really necessary. You will have to
debug the failure anyway, so assertion will tell you where and all other
information will be provided by debugger. Sometimes there is a need to
create assertions that work in optimized build. I had some cases when that
was necessary due to large time-consuming testing. In that case one can
create an assert() like macro, that is explicitly enabled or disabled. The
reason it should be a macro is because the most valuable information is the
file name and line number, which are compiler macros.

> #include <string>
>
> struct Assertion_error
> {
>         Assertion_error(char const* expr):
>                 expression(expr) { }
>
>         std::string expression;
> };
>
> #define ASSERT(expression) \
>         if(!(expression)) { \
>                 throw Assertion_error(#expression); \
>         } else;

This is an absolutely atrocious macro. First thing, avoid macros unless
absolutely necessary. There are very few legitimate cases when they are
really necessary. When you need a function - write a function, not a macro.
When creating macros don't use common names like ASSERT, I bet some compiler
somewhere already used that name.


-- Gene Bushuyev   ~   Cadence Design Systems


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Gene 11/9/2005 2:18:29 PM

On the page below you can see my 'improved' versions of the standard
assert. Especially the message version and the beep one could be
useful...

http://www.fs-games.com/FipS/fs_core.fsAssert.php

________________________________________________
+++ Filip STOKLAS (FipS), www.FS-Games.com/FipS/


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Filip 11/9/2005 6:31:13 PM

Gene Bushuyev wrote:
> "Jeffrey Schwab" <jeff@schwabcenter.com> wrote in message
>>#define ASSERT(expression) \
>>        if(!(expression)) { \
>>                throw Assertion_error(#expression); \
>>        } else;
> 
> This is an absolutely atrocious macro. First thing, avoid macros unless
> absolutely necessary. There are very few legitimate cases when they are
> really necessary.

For example when you want to use the # preprocessor operator...
__FILE__ and __LINE__ usually are handy too in an assert()-like
macro, and would not be available when replacing the macro by a
function.

Falk

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply ISO 11/10/2005 2:19:57 AM

Gene Bushuyev wrote:
> "Jeffrey Schwab" <jeff@schwabcenter.com> wrote in message
> news:14Jbf.2757$bU3.688187@twister.southeast.rr.com...
> 
>>Do you use the standard assertion header, or do you roll your own?
>>Pros/cons?
> 
> 
> Assertions are for developers, not for end users. The end users should never
> see an assertion failure. Therefore assertions shouldn't throw exceptions, -
> stack unwinding only hurts debugging.

The user doesn't necessarily see the assertion failure.  Throwing an 
exception just allows cleanup at a higher level.  For example, asserting 
that a resource allocation worked can allow failures to be reported from 
a level where alternative resources might be available.  Anyway, do you 
really think the end user is better off with an abort()?

I'm not sure why you think stack unwinding hurts debugging.  I think the 
exact location of the error can be important, but the path by which that 
code was reached can be equally important.

> Sometimes people create more elaborate
> macros than simple assert(), but it isn't really necessary. You will have to
> debug the failure anyway, so assertion will tell you where and all other
> information will be provided by debugger. Sometimes there is a need to
> create assertions that work in optimized build. I had some cases when that
> was necessary due to large time-consuming testing. In that case one can
> create an assert() like macro, that is explicitly enabled or disabled. The
> reason it should be a macro is because the most valuable information is the
> file name and line number, which are compiler macros.

Right, my usual assertion macro includes __FILE__ and __LINE__.  I 
should have included them here.

>>#include <string>
>>
>>struct Assertion_error
>>{
>>        Assertion_error(char const* expr):
>>                expression(expr) { }
>>
>>        std::string expression;
>>};
>>
>>#define ASSERT(expression) \
>>        if(!(expression)) { \
>>                throw Assertion_error(#expression); \
>>        } else;
> 
> 
> This is an absolutely atrocious macro. First thing, avoid macros unless
> absolutely necessary.

I would love to, but how else can I get a string that represents the 
failed assertion?  I hate doing this sort of thing:

	int error = call_into_vendor_library(
		complicated, list, of, args );

	if( error ) {
		std::cerr <<
			"error: call_into_vendor_library( "
			"complicated, list, of, args) "
			"returned non-zero status.\n";
	}

> There are very few legitimate cases when they are
> really necessary. When you need a function - write a function, not a macro.
> When creating macros don't use common names like ASSERT, I bet some compiler
> somewhere already used that name.

The lack of namespaces is probably what I hate most about the 
preprocessor.  It's also, IMHO, C's biggest disadvantage relative to C++.

> -- Gene Bushuyev   ~   Cadence Design Systems

You work for Cadence?  I use your products. :)

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Jeffrey 11/10/2005 11:56:40 AM

Jeffrey Schwab wrote:
> Do you use the standard assertion header, or do you roll your own?
> Pros/cons?

I've done both. Depends on what I need. Usually, the standard assertion
macro produces some kind of debugging info (like a core dump), writes a
message to the console and exits. If I want more than that, I roll my
own.

> #define ASSERT(expression) \
>          if(!(expression)) { \
>                  throw Assertion_error(#expression); \
>          } else;

I would never have an assertion macro throw an exception. Assertions
are for detecting programmer errors; when a programmer error occurs,
your system is in big trouble. Throwing an exception allows the stack
to unwind, which may cause further damage as well as throw away
information that could help you debug the program. Further, there's no
way to guarantee that the Assertion_error won't be caught and
swallowed, preventing the assertion from even being detected.

Bob


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Bob 11/10/2005 1:26:18 PM

Jeffrey Schwab wrote:
> Do you use the standard assertion header, or do you roll your own?

Yes.

> Pros/cons?

The reason I never use the C-style assert is that when triggered it
core dumps. I prefer to throw an "asserted failed" exception. Currently
there is no std way to do this.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply apm35 11/14/2005 4:46:27 PM

If you want to pass a message to custom assertion you'd usually use
stringstream (or it's cousins) to construct the message and then pass
it to the  assertion macro:
Like this:

File file;
int error = file.open();

stringstream msg;
msg << "Can't open file " << file.name() << ": open failed with error "
<< error;
ASSERT(error, msg.str());

This does not seem to be proper way to do this, since in the release
mode the message
construction lines will remain in the code and the fat piece of code
above obscures
the readability of the program.
It would be better to use a single line assert to pass complex message.

ASSERT(error,  "Can't open file ", file.name(), ": open failed with
error ", error );

How can this be achieved ?

Gregory


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Gregory 11/19/2005 10:22:53 AM

Hi, I already came up with some solution. Here is an ASSERT macro that
allows
passing complex message to it.:

#define ASSERT(exp, msg) \
	if (! (exp)) \
	{ \
		stringstream msgstr; \
		msgstr << msg; \
		cerr << "Assertion failed: [" << #exp << " " <<  "] at " << \
			__FILE__ << "(" << __LINE__ << ") : " << msgstr.str() << endl; \
                abort(); \
	}

And here is how it can be used:

File file;
int error = file.open();
....
ASSERT(error,  "Can't open file " << file.name() << ": open failed with
error " << error );

Since preprocessor substitutes macro parameters textually as is the
code above works.
Note that I did not not put brackets around 'msg' macro parameter,
otherwise the macro won't work.

What do you think about this solution ? Is there any better solution
around ?

Gregory


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Gregory 11/19/2005 4:12:46 PM

"Gregory" <g_greg@netvision.co.il> wrote in message 
news:1132406962.990746.286500@g49g2000cwa.googlegroups.com...
> Hi, I already came up with some solution. Here is an ASSERT macro that
> allows
> passing complex message to it.:
>
> #define ASSERT(exp, msg) \
> if (! (exp)) \
> { \
> stringstream msgstr; \
> msgstr << msg; \
> cerr << "Assertion failed: [" << #exp << " " <<  "] at " << \
> __FILE__ << "(" << __LINE__ << ") : " << msgstr.str() << endl; \
>                abort(); \
> }

Don't add to assertion unnecessary and unsafe constructs. The whole purpose of 
assertion is to fail immediately with as little effect on the code as possible. 
Assertion failures are supposed to be debugged, not to produce a nice error 
message.
Your solution can throw at every point, and the information it provides is 
unnecessary. The standard assert() provides both file name and line, that's 
enough to set the breakpoint in the debugger.
The guidelines for writing assertion can be summarized in one sentence: it must 
abort the program (producing core dump) immediately at the point where the bug 
detected, it must throw no exceptions and have no side effects.

-- Gene Bushuyev
----------------------------------------------------------------
There is no greatness where there is no simplicity, goodness and truth. ~ Leo 
Tolstoy 


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Gene 11/19/2005 7:28:13 PM

I do not use exceptions in my code so it's not real problem at least
for me :)

Message in assertions can help much in several cases:

- If some assertions are left in the release mode (VERIFY-like macro)
then it gives
you a clue what actually happened, what was the assertion context. It's
very useful when users of the program can't
quickly supply you a test case, or the test case is commercial
property.
- In debug mode sometimes you have assertion failure after a long run,
so it's
problematic to re-run the program in debugger just to figure out what
the problem was.
Using the assertion message you can often understandt what the problem
was and build a small test that reconstructs the problem.
- Also in debug mode if you can figure out what the problem is just
from the
assertion message (without opening debugger) why don't you use the
message ?

Look into Alexandrescu paper "Enhancing Assertions" he implements there
assertions that provides extra information on their context.
I'm sure you know the trick to print user message with standard assert
macro:

assert(error && "my message");

People invented this because they need this, so am I :)

Gregory


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Gregory 11/20/2005 4:55:38 PM

14 Replies
226 Views

(page loaded in 0.272 seconds)

Similiar Articles:








7/27/2012 2:32:17 AM


Reply: