Search code examples
c++arduinoc-preprocessordirectivecomparison-operators

Using Comparison Operators in preprocessor directives C++


I want to define some functions based on a constant value:

#define mode 5

#if mode & 2 != 0
  // function 1
#endif

#if mode & 4 != 0
  // function 2
#endif

This may sound and look weird but I want to use one constant to define and activate some program modules.
defining mode = 2 includes function 1, mode = 4 includes function 2 and mode = 6 includes both functions.
There is one problem: Comparison Operators like ==, !=, > or < doesn't seem to work in directives and #if statements are always executed.

What am I doing wrong? Am I trying to do a stupid or impossible thing?


Solution

  • & has lower precendence then !=. So:

    MODE & 2 != 0
    

    is the same as

    MODE & (2 != 0)
    

    The 2 != 0 is logically true, so the result of != operator is a 1. Thus this is the same as

    MODE & 1
    

    It's just checking the first bit. While you want:

    (MODE & 2) != 0
    

    To check if the second bit is set. But really just remove the != part and do:

    #if MODE & 2
    

    Remember to prefer upper case names for macro names.

    This may sound and look weird

    No, that sounds completely normal. I would go with a lot more descriptive names, then plain & 2 - magic numbers are confusing. Like:

    #define MODE  (MODE_ENABLE_FUNC_1 | MODE_ENABLE_FUNC_2)
    
    #define MODE_ENABLE_FUNC_1  (1<<0)
    #define MODE_ENABLE_FUNC_2  (1<<1)
    #define MODE_ENABLE_FUNC_3  (1<<2)
    #define MODE_IS_ENABLED(mode, feature) ( ((mode) & (feature)) != 0)
    #if MODE_IS_ENABLED(MODE, MODE_ENABLE_FUNC_1)
    // etc.
    

    If possible, prefer to use C++ templates and SFINAE, rather then plain C macros.

    What am I doing wrong?

    You are assuming that != has lower precedence then &.

    Am I trying to do a stupid or impossible thing?

    No.


    I remember the part The development of C language by Dennis M. Ritchie and from section name Neonatal C he writes:

    [...] In converting from B to C, one wants to replace & by && in such a statement; to make the conversion less painful, we decided to keep the precedence of the & operator the same relative to ==, and merely split the precedence of && slightly from &. Today, it seems that it would have been preferable to move the relative precedences of & and ==, and thereby simplify a common C idiom: to test a masked value against another value, one must write

    if ((a&mask) == b) ...
    

    where the inner parentheses are required but easily forgotten.

    No worries - you are not the first and not the last to forget about the braces in & and another operator context.