1. We have moved from vBulletin to XenForo and you are viewing the site in the middle of the move. Though the functional aspect of everything is working fine, we are still working on other changes including the new design on Xenforo.
    Dismiss Notice

ASSERTs in C++

Discussion in 'C++' started by Mridula, Jul 2, 2009.

  1. Mridula

    Mridula New Member

    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.
     
    SaswatPadhi and shabbir like this.
  2. shabbir

    shabbir Administrator Staff Member

    I really like your articles and vote for the same as well.
     
  3. Mridula

    Mridula New Member

    Many many thanks Shabbir.
    I am feeling as if I have won the prize !! This is enough to keep going.

    many thanks
    mridula.
     
  4. SaswatPadhi

    SaswatPadhi ~ Б0ЯИ Τ0 С0δЭ ~

    Yeah, when the admin himself likes your article, you are really really lucky !!!!!! :)
     
  5. naimish

    naimish New Member

    yeah...one of the lucky :)
     
  6. LenoxFinlay

    LenoxFinlay Banned

    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
     
    Last edited: Jul 18, 2009
  7. shabbir

    shabbir Administrator Staff Member

  8. mayjune

    mayjune New Member

    congrats...
     
  9. shabbir

    shabbir Administrator Staff Member

Share This Page