Search code examples
cassert

Better Assert in C


Sometimes I have to send the result of an assert over canbus, sometimes its local. I use C only, Embitz compiler using GCC, STM32F407 or STM32F103. My present assert is: .h file:

extern char *astrbuf;
#define assert(left,operator,right)\
if(!((left) operator (right))) \
 {asprintf(&astrbuf,"\nAssert error %s %d %ld %ld\n",__FILE__, __LINE__,\
  (u32)(left),(u32)(right));\
  asserted();\
 }

.c file:

void asserted(void)
{ dprint("%s",astrbuf);

followed by the display code or canbus code. Example: assert(millis,<,maxtime); This works very well, but will be better if the operator can be indicated. I simply do not see how to display or send the operator, which can be ==, <, or >.


Solution

  • Why not use the standard assert interface and include the whole expression?

    #define assert(EXPR) \
    if (!(EXPR)) \
     {asprintf(&astrbuf, "\nAssert error %s %d %s\n",__FILE__, __LINE__, #EXPR); \
      asserted(); \
     }
    

    ... using the # macro stringification operator.

    By the way, why is half of your code in the macro and the other half in the asserted function? Why not do it all in one place?

    #define assert(EXPR) \
    if (!(EXPR)) \
     { \
      asserted(__FILE__, __LINE__, #EXPR); \
     }
    

    with

    void asserted(const char *file, int line, const char *expr) {
        char *astrbuf;
        asprintf(&astrbuf, "%s: %d: assertion failed: %s\n", file, line, expr);
        dprint("%s", astrbuf);
        ...
    }
    

    Now you don't need a global variable anymore.

    There's another potential issue. If you use your macro like this:

    if (foo())
        assert(x > 42);
    else
        bar();
    

    ... the else bar(); part will attach to the if statement hidden in assert, not the outer if. To fix this, you can wrap the whole thing in a do while loop:

    #define assert(EXPR) \
        do { \
            if (!(EXPR)) { \
                asserted(__FILE__, __LINE__, #EXPR); \
            } \
        } while (0)
    

    Or alternatively make sure the whole macro expands to a single expression:

    #define assert(EXPR) \
        ((void)((EXPR) || (asserted(__FILE__, __LINE__, #EXPR), 0)))
    

    Of course you could also put the conditional logic in the function:

    #define assert(EXPR) asserted(!!(EXPR), __FILE__, __LINE__, #expr)
    
    void asserted(int cond, const char *file, int line, const char *expr) {
        if (cond) {
            return;
        }
        ...
    }