Home arrow C++ arrow Page 4 - C++ Preprocessor: Always Assert Your Code Is Right
C++

C++ Preprocessor: Always Assert Your Code Is Right


Are you looking for a way to speed up the debugging process in C++? This article explains how to use asserts to do just that, showing that not all macros are evil.

Author Info:
By: J. Nakamura
Rating: 4 stars4 stars4 stars4 stars4 stars / 49
November 07, 2005
TABLE OF CONTENTS:
  1. · C++ Preprocessor: Always Assert Your Code Is Right
  2. · Implementing Assert
  3. · Implementing a Simplified Assert
  4. · Refining Assert

print this article
SEARCH DEVARTICLES

C++ Preprocessor: Always Assert Your Code Is Right - Refining Assert
(Page 4 of 4 )

The simplest solution is to just expand the assert macro and make it accept a message as well as a condition:

#ifndef NDEBUG
# define ASSERT( isOK, message ) \
 if ( !(isOK) ) { \
  (void)printf(“ERROR!! Assert ‘%s’ failed on line %d “ \
   “in file ‘%s’\n%s\n”, \
      #isOK, __LINE__, __FILE__, #message); \
   __asm { int 3 } \
  }
#else
# define ASSERT( unused, message ) do {} while ( false )
#endif

Again the stringize operator helps us to stuff the message we want into the printf statement. We could call it a day now, but I am a very lazy coder… I do not want to be forced to put messages into the assert, sometimes I just want to assert.

Of course it is possible to write an ASSERT(condition) and an ASSERTm(condition, message) macro, but did I mention I am a forgetful coder too? I’d much rather have a single ASSERT statement that can do both.

The first thing that comes to mind is the fact I could do this easily with a function:

void MyAssert(bool isOK, char const *message=””) {
 if ( !isOK ) {
  (void)printf(“ERROR!! Assert ‘%s’ failed on line %d “
   “in file ‘%s’\n%s\n”,
   __LINE__, __FILE__, message);
  __asm { int 3 } \
 }
}

So maybe if I declared another function:

void NoAssert(bool isOK, char const *message=””) {}

And then defined assert as:

#ifndef NDEBUG
# define ASSERT MyAssert
#else
# define ASSERT NoAssert
#endif

While this seems like a quick solution, I have completely lost my extra debug information! The line information is the same now for every assert… oh… the compiler substitutes __LINE__ with the actual line number it is compiling at that moment, and since we are making a function call – all line numbers lead to the MyAssert function!

Alexandrescu demonstrates a great way around this problem [Alexandrescu]. (It is also a great article showing how you can take assertions to a higher level after this one, by making them throw exceptions!)

#ifndef NDEBUG
#define ASSERT \
 struct MyAssert { \
 MyAssert(bool isOK, char const *message=””) { \
  if ( !isOK ) { \
   (void)printf(“ERROR!! Assert failed in “ \
    “file ‘%s’\n%s\n”, __FILE__, message); \
    __asm { int 3 } \
   } \
  } \
 } myAsserter = MyAssert
#endif

For some reason my Visual C++ 7.1 compiler will not accept the __LINE__ macro next to the __FILE__ macro in the code above. The strange thing is that the __FILE__ macro works fine, but with __LINE__ it complains:

error C2065: '__LINE__Var' : undeclared identifier

It is never easy, but I do not want to give up at this point. Since the macro is expanded as a single line into the place where we are calling it, and since the compiler apparently has no objection to me assigning __LINE__ as a default parameter in a constructor, let's try again:

#ifndef NDEBUG
#define ASSERT \
 struct MyAssert { \
 int mLine; \
 MyAssert(int line=__LINE__) : mLine(line) {} \
 MyAssert(bool isOK, char const *message=””) { \
  if ( !isOK ) { \
   (void)printf(“ERROR!! Assert failed on “ \
    “line %d in file ‘%s’\n%s\n”, \
    MyAssert().mLine, __FILE__, message); \
   __asm { int 3 } \
  } \
 }\
 } myAsserter = MyAssert
#endif

Now that we have our line information back, we are nearly there; as soon as we add a second assert, the compiler complains that we are redefining the struct MyAssert! If only we could keep the struct declaration local… and again Alexandrescu shows us how [Alexandrescu]:

#ifndef NDEBUG
#define ASSERT \
 if ( false ) {} else struct LocalAssert { \
  int mLine; \
 LocalAssert(int line=__LINE__) : mLine(line) {} \
 LocalAssert(bool isOK, char const *message=””) { \
  if ( !isOK ) { \
   (void)printf(“ERROR!! Assert failed on “ \
    “line %d in file ‘%s’\n%s\n”, \
    LocalAssert().mLine, __FILE__, message); \
   __asm { int 3 } \
 } \
 } myAsserter = LocalAssert
#else
#define ASSERT \
 if ( true ) {} else struct NoAssert { \
 NoAssert(bool isOK, char const *message=””) {} \
 } myAsserter = NoAssert
#endif

There is a lot of fun to be had with macros and sometimes it is possible to create the wildest incantations with them [Niebler/Alexandrescu]. I hope you are convinced that despite the fact that they can be considered evil, there is something magical about them as well.

As a final example I will show you how to create personal and customizable debug streams in the next article… all with macros.


References

 [STL] – The Standard Template Library

< comes standard with your compiler but this one is very portable>

http://www.stlport.org

[BOOST] – Boost C++ Libraries

http://www.boost.org

[ACE] – The ADAPTIVE Communication Environment

http://www.cs.wustl/edu/~schmidt/ACE.html

[Niebler] – Eric Niebler

“Conditional Love: FOREACH Redux”

http://www.artima.com/cppsource/foreach.html

[Alexandrescu] – Andrei Alexandrescu

Assertions

http://www.cuj.com/documents/s=8248/cujcexp2104alexandr/


DISCLAIMER: The content provided in this article is not warranted or guaranteed by Developer Shed, Inc. The content provided is intended for entertainment and/or educational purposes in order to introduce to the reader key ideas, concepts, and/or product reviews. As such it is incumbent upon the reader to employ real-world tactics for security and implementation of best practices. We are not liable for any negative consequences that may result from implementing any information covered in our articles or tutorials. If this is a hardware review, it is not recommended to open and/or modify your hardware.

blog comments powered by Disqus
C++ ARTICLES

- Intel Threading Building Blocks
- Threading Building Blocks with C++
- Video Memory Programming in Text Mode
- More Tricks to Gain Speed in Programming Con...
- Easy and Efficient Programming for Contests
- Preparing For Programming Contests
- Programming Contests: Why Bother?
- Polymorphism in C++
- Overview of Virtual Functions
- Inheritance in C++
- Extending the Basic Streams in C++
- Using Stringstreams in C++
- Custom Stream Manipulation in C++
- General Stream Manipulation in C++
- Serialize Your Class into Streams in C++

Watch our Tech Videos 
Dev Articles Forums 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Contact Us 
Site Map 
Privacy Policy 
Support 

Developer Shed Affiliates

 




© 2003-2017 by Developer Shed. All rights reserved. DS Cluster - Follow our Sitemap
Popular Web Development Topics
All Web Development Tutorials