Introduction This article talks about ASSERTs usage and customizing our own ASSERTs. Background Asserts are to catch implementation errors. The developer can document all the assumptions made on the his/her program, by using ASSERTs. If you have used asserts in your code and if it triggers, then you can know, there is a problem in your code and needs to be solved. And thus ASSERTs are to find bugs in your code. These Asserts are available only for Developer loads or Debug loads. And are completely removed from Customer binaries or Release loads and so no issue of increase in the size of binary version nor the issue with the degrade in the performance of the application that is shipped to Customer. Assert is a macro provided by compiler and it returns True if the expression provided to it evaluates as True Or it returns False and abort the program, if the expression provided to it evaluates as False . And thus helps developer to find bugs beforehand. it is available in assert.h header file. When to use ASSERTs Developers should make it a practice to use Asserts wherever it is needed to test : 1. Validity of function arguments before using them 2. Validity of a pointer before using it 3. Testing any condition that you think, Must be True i.e. checking the values of a variable at some point in code, checking the expected range at some point etc. . Customizing our own ASSERTs Well, we can customize our own Assert to display a better informative/formated error messages and end the application in a proper way before aborting. It means releasing any held semaphores, deleting something or logging required infromation to files etc instead of ending the system abruptly. So this would help later debuging the code and fix the recorded problem. So, if you encounter a core/trap, think whether this core can be caught using our own customised ASSERT, before fixing it. This helps you next time when the same bug encounters in future testing like integration testing etc with a better explained message and correct position to analyze the bug, when that Assert triggers. Here is a sample code for MY_ASSERT Macro and it's usage: The code Code: #define DEBUG #include<iostream.h> #ifndef DEBUG //do nothing #else #define MY_ASSERT(x) \ logMsg(x, #x, __FILE__, __LINE__) #endif bool logMsg(bool x, const char *msg, char* file, unsigned int line) { if( false == x) { //do that extra logging information to a file etc. cout<<"On line " <<line<<":"; cout<<" in file " <<file <<":"; cout<<" Error !! Assert "<<msg << " failed\n"; abort(); return (true); } else { return(true); } } int main() { int x; x = 5; cout<<"Checking First assert...\n"; MY_ASSERT(5==x); cout<<"Checking Second assert...\n"; MY_ASSERT(x!=5); cout<<"Done...\n"; return(0); } output: Checking First assert... Checking Second assert... On line 37: in file assert.cc: Error !! Assert x!=5 failed Abort (core dumped) Here MY_ASSERT macro is valid only if DEBUG flag is on. Else, it is nothing in customer binary as shown above. When Not to USE ASSERTs It is bad to use Asserts to handle conditions that application does not have in it's control like out-of memory conditions, unable to open the file etc., which can occur even in Customer loads as well. Example for bad use of Assert: The code Code: Base *bptr = new Derived; MY_ASSERT(bptr); //incorrect usage of MY_ASSERT bptr->doSomething(); Error Handling in Customer Binaries If developer anticipates the ASSERTs that are introduced in code, might also come in customer load, then he/she can handle it through error handling techniques to take care or control bugs even in Customer loads. For example: The code Code: void func(unsigned int range) { MY_ASSERT(0 < range < 100); //this assert will not be there in Customer Binary, so if (0 > range) { range = 0; } } Here, the method "func" might receive the input "range" as less than 0 as well from external user, in that case, since in customer load, ASSERT would have been removed and so as part of error handling, we can modify the value of variable "range" to "0" and hence prevent any future bugs. ASSERTs in Customer Binary To handle extreme cases like, To detect improper/unreachable external conditions in the application Or Some part of the code, which is never executed like default case in switch statement as exaplined in example code below etc, We can go for special ASSERT Macros that can be there even in Customer Binaries as well. Here is a customized Macro called MY_RELEASE_ASSERT which is avaiable for both Developer loads and Customer loads as well. The code Code: #define DEBUG #include<iostream.h> // here is a DEBUG ASSERT #ifndef DEBUG //do nothing #else #define MY_ASSERT(x) \ logMsg(x, #x, __FILE__, __LINE__) #endif bool logMsg(bool x, const char *msg, char* file, unsigned int line) { if( false == x) { cout<<"On line " <<line<<":"; cout<<" in file " <<file <<":"; cout<<" Error !! Assert "<<msg << " failed\n"; abort(); return (true); } else { return(true); } } // here is a Customer Binary ASSERT #define MY_RELEASE_ASSERT(x) \ logMsgForRelase(x, #x, __FILE__, __LINE__) bool logMsgForRelase(bool x, const char *msg, char* file, unsigned int line) { if( false == x) { cout<<"On line " <<line<<":"; cout<<" in file " <<file <<":"; cout<<" Error !! Assert "<<msg << " failed\n"; abort(); return (true); } else { return(true); } } int main() { int x; x = 5; switch (x) { case 0: { //valid case, do something cout<<"Checking First assert...\n"; MY_RELEASE_ASSERT(5==x); break; } case 1: case 2: { //valid case, do something cout<<"Checking Second assert...\n"; MY_RELEASE_ASSERT(x!=5); break; } default: { //should never reach here MY_RELEASE_ASSERT(false); } } return(0); } output: On line 74: in file assert1.cc: Error !! Assert false failed Abort (core dumped) //infact this should never happen !! Here you can notice the macro MY_RELEASE_ASSERT is not wrapped with any conditinal flags and so available for both Debug and Customer loads. Bottom Line:- If you are unfamilier with ASSERTs, you should start using them now onwards, it really helps finding, analysing, debugging and fixing bugs beforhand in your DEBUG loads!! thanks Mridula.
Many many thanks Shabbir. I am feeling as if I have won the prize !! This is enough to keep going. many thanks mridula.
If the argument expression of this macro with functional form compares equal to zero, a message is written to the standard error device and abort is called, terminating the program execution. The specifics of the message shown depend on the specific implementation in the compiler, but it shall include: the expression whose assertion failed, the name of the source file, and the line number where it happened. A usual expression format is: Assertion failed: expression, file filename, line line number