tags:

views:

226

answers:

7

In C++, is it possible to make a multi-statement macro with nested if statements inside of it like the one below? I've been attempting it for a while now and I'm getting a scope issue for the second if statement not being able to see 'symbol'. Maybe I need to understand macros further.

#define MATCH_SYMBOL( symbol, token)
     if(something == symbol){
          if( symbol == '-'){
          }else if (symbol != '-'){
          }
     other steps;
     }
+10  A: 

For a multi-line macro you need to add a \ character to the end of all but the last line to tell the macro processor to continue parsing the macro on the next line, like so:

#define MATCH_SYMBOL( symbol, token) \
     if(something == symbol){        \
          if( symbol == '-'){        \
          }else if (symbol != '-'){  \
          }                          \
     other steps;                    \
     }

Right now, it's trying to interpret it as a 1-line macro and then some actual code at the top of your file, which isn't what you want:

#define MATCH_SYMBOL( symbol, token)

// and then... wrongly thinking this is separate...

 if(something == symbol){ // symbol was never defined, because the macro was never used here!
      if( symbol == '-'){
      }else if (symbol != '-'){
      }
 other steps;
 }
Amber
And be careful with doing this because it's not obvious to the user that this macro contains an if statement. A typical problem case is when you use such a statement inside a series of nested `if`-`else` statements with no brackets.
Nate C-K
Thanks, made it quite clear.
Jesse O'Brien
The solution is to add `else (void)0` to the end of the macro, which both fixes block-scopes and forces a semicolon.
GMan
Or use the famous `do { } while(0)` trick.
Tim Sylvester
Might as well add an else to an existing if-statement, though.
GMan
See http://stackoverflow.com/questions/154136 for a thorough explanation of the pitfalls of multi-statement macros and the trick I mentioned.
Tim Sylvester
+1  A: 

You need to have a backslash (\) at the end of all the lines in the macro but the last one.

Gonzalo
A: 

also, see if you can replace the macro with a function.

?
MATCH_SYMBOL(Sym const & symbol, Tok const & token)
{
    ...
}
just somebody
+1  A: 

The way of the C++:

inline void MATCH_SYMBOL(const Symbol& symbol, const Token& token) {
    /* ... */
    if (something == symbol) {

     if ('-' == symbol) {
     /* ... */
     }
     else if ('-' != symbol) {
     /* ... */
     }
    }
    /* ...other steps... */
}
Justin
Correct me if I'm wrong, but I think the inline keyword is also in C99.
asveikau
@asveikau It is (though I am unsure why you brought that up (?) )
Justin
@justin: I imagine mostly because the original question isn't really limited to C++. In the cases where `inline` would be a suitable replacement for a macro in C++, it would be a suitable replacement in C99 as well.
quark
You said "the way of the C++". It's also pretty close to the way of the C99. (Though I didn't catch the use of references at the first glance.)
asveikau
Right, the C way is approximately 'static inline void MATCH_SYMBOL(const Symbol* const symbol, const Token* const token) { assert(symbol
Justin
+8  A: 

If you're using C++ you should avoid using macros altogether. They are not type-safe, they're not namespace-aware, they're hard to debug and just they're plain messy.

If you need a type-independent function, use templates:

template <typename T>
bool match_symbol(T symbol, T token) {
    if(something == symbol){
        if( symbol == '-'){
        }else if (symbol != '-'){
        }
    ...

or if the parameters can be different types:

template <typename T, typename V>
bool match_symbol(T symbol, V token) {
    if(something == symbol){
        if( symbol == '-'){
        }else if (symbol != '-'){
        }
    ...
Tim Sylvester
I'd +1 if you have used the keyword `inline`.
LiraNuna
@LiraNuna If it's in a header file you should include `inline`, but otherwise it's just a suggestion to the compiler that it's free to ignore. Whether to inline or not is irrelevant to my point.
Tim Sylvester
Function templates don't need to be marked inline, because implicitly instantiated templates are (in effect) exempt from the one definition rule already. It's fine for two different translation units to create the same instantiation of the template, and then be linked together.
Steve Jessop
Function name shouldn't be all in capital letters. +1 anyway.
Kirill V. Lyadvinsky
+3  A: 

Note that some of the answers here have a problem.

For example, for a normal statement you can do this:

if (foo)
   function();
else
   otherstuff();

If you followed some of the suggestions here, but if replace function with a macro, it might expand to:

if (foo)
   if (something) { /* ... */ }
   else           { /* ... */ }; // <-- note evil semicolon!
else
   otherstuff();

So a common (ugly) hack that people do to avoid this is:

#define MATCH_SYMBOL(symbol, token)    \
    do                                 \
    {                                  \
       if(something == symbol)         \
       {                               \
          if( symbol == '-')           \
          {                            \
          }                            \
          else if (symbol != '-')      \
          {                            \
          }                            \
          other steps;                 \
       }                               \
    }                                  \
    while (0) // no semicolon here

This is so that the "statement" MATCH_SYMBOL(a, b) can end with a semicolon just like a normal statement. You also have braces around the multiple statements.

If you think nobody's crazy enough to use this technique, think again. It's very common in the Linux kernel, for example.

asveikau
There's at least one problem with your approach. If that code in the original macro (before converting to the do-while method above) contains a "break", it won't break out of the scope enclosing the macro because it's in the middle of a do-while loop inside the macro. There are similar problems with "continue".
Adisak
Explanation of above comment: Let's say we have a while loop that updates a list item at the end of the loop. We make a silly macro to emulate continue with this: `#define CONTINUE item=item->next; continue` This is two code statements which presents problems to using it with `if` statements. But wrapping as you suggest also poses problems: `#define CONTINUE do{item=item->next; continue;}while(0)` has a problem that the continue no longer works. The only solution I know is to use a non-loop wrapper: `#define CONTINUE if(1){ item=item->next; continue; }else do{}while(0)`
Adisak
A: 

One can also define macro function and implement the function than

#define MATCH_SYMBOL( symbol, token) match_symbol(symbol,token)
Vivek
You can, but what's the point?
MSalters