0

I've written a basic macro which asserts regardless of debug or not:

#define ASSERT_ALWAYS(cond) \
    do                      \
    {                       \
        if (!cond)          \
        {                   \
            std::abort();   \
        }                   \
    } while(0)              \

The strange thing is it compiles if I use a variable:

const bool s = myObj.anEnum != AnEnum::C;
ASSERT_ALWAYS(s);

but without the variable this doesn't compile:

ASSERT_ALWAYS(myObj.anEnum != AnEnum::C);

I get this compiler error:

error: no match for ‘operator!’ (operand type is ‘AnEnum::C’)
no known conversion for argument 1 from ‘AnEnum::C’ to ‘bool’

AnEnum is declared like this:

enum class AnEnum : char {
  A = '0',
  B = '2',
  C
};

I get similar compiler errors for other types I try to change, so is the problem with my macro?

bcguy
  • 21
  • 3
  • 2
    `if (!(cond))` may help. Note the extra parenthesis. – Retired Ninja May 13 '22 at 13:45
  • Is `MY_ASSERT` the same thing as `ASSERT_ALWAYS`? – molbdnilo May 13 '22 at 13:47
  • 1
    Does this answer your question? [C macros and use of arguments in parentheses](https://stackoverflow.com/questions/7186504/c-macros-and-use-of-arguments-in-parentheses) – Retired Ninja May 13 '22 at 13:48
  • You can get the same behavior with a function, instead of a macro. Why not go for the simpler solution (the function)? – paolo May 13 '22 at 13:48
  • Macros work by text substitution. So `ASSERT_ALWAYS(myObj.anEnum != AnEnum::C);` expands to `... if(!myObj.anEnum != AnEnum::C) ... `, which in turn is trying to check whether `!myObj.anEnum` is not equal to `AnEnum::C`. The suggested parentheses around `(cond)` will impose the correct precedence. But you could also just make this a function that accepts a `bool` instead of using a macro at all. – Nathan Pierson May 13 '22 at 13:49
  • 1
    Why not just use `assert`? It gives a nicer error when it aborts too. – Goswin von Brederlow May 13 '22 at 17:25

1 Answers1

5

When you use the expression, the preprocessor pastes

if(!myObj.anEnum != AnEnum::C)

so the compiler processes

if( (!myObj.anEnum) != (AnEnum::C) )

and the error makes more sense.

What you need is if(!(cond)).

limserhane
  • 1,022
  • 1
  • 6
  • 17
  • @bcguy Most ide's will tell you what an (macro) expression is being expanded to, when you hover it. – limserhane May 13 '22 at 13:54
  • 2
    @bcguy -- that's one of the drawbacks with macros: they don't behave the way you expect them to. The rule of thumb is to always put parentheses around uses of the macro arguments and, if the macro is just a computation, put parentheses around the whole thing. `#define my_expr(x) ((x)*3)`. – Pete Becker May 13 '22 at 13:54