Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make #if #endif part of a macro

Tags:

c

macros

#define M(N)\
#if N == 5\
    /*several lines of code that 
      Can't be replaced with a   
      tertnary operator*/
#else\
    N;\
#endif

When I use this macro like so

M(5);

I expect the output to be

// everything within the #if #else block

But it doesn't compile.

I'm not surprised that it doesn't compile: I know #if cannot be used on a continuation line ie "\".

I've also tried

#define POUND_IF #if

And then to use POUND_IF but doesn't work.

Is this possible to do?

Is there some nifty Boost pre-processor stuff that can be used?

like image 261
Bob Avatar asked Oct 31 '15 02:10

Bob


2 Answers

Succinctly, you can't. You can rely on the optimizer instead, though:

#define M(N)\
    do { if (N == 5) { \
    /*several lines of code that 
      Can't be replaced with a   
      ternary operator*/ \
    } else { N; } } while (0)

If the compiler can determine that the value of N will be 5 when the code is run (e.g. you write M(5)), then only the code in the body of the if will be included in the generated code. If the compiler can determine that the value of N will not be 5 when the code is run, it will generate only the code in the body of the else clause. And if it can't determine what the value will be, the preprocessor would not have been able to do so either, but the compiler will include all the code.

like image 158
Jonathan Leffler Avatar answered Nov 10 '22 04:11

Jonathan Leffler


A macro definition cannot include preprocessor directives (anything beginning with a #).

To conditionally expand to certain values requires rather involved macrology and might not always work out the way you want. The example above could be written something like this:

#define ONE_OR_TWO(...) ONE_OR_TWO_(__VA_ARGS__, 2, 1,)
#define ONE_OR_TWO_(_1, _2, X, ...) X
#define CAT(A, B) CAT_(A, B)
#define CAT_(A, B) A ## B

#define M(N) CAT(IF_, ONE_OR_TWO(CAT(IS_, N)))({\
    code; \
    code; \
}, N)
#define IS_5 ,
#define IF_1(A, B) B
#define IF_2(A, B) A

M(5);  //-> { code; code; }
M(8);  //-> 8

This is incredibly fragile though. You can only use certain types of expression as the argument to M, for instance - they have to have a syntactic structure that allows for concatenation, and no unwrapped commas - and it only works for predetermined values (e.g. to compare against something more complicated than a simple number is not possible with this method, because you can't build a macro name out of a more complex expression).

But in principle, it can be done. You just need thousands upon thousands of lines of macro definitions to cover a useful set of cases beyond the trivial ones like this. You can get those by using a metaprogramming library like Order-PP or Boost.Preprocessor, but be prepared for obscure error messages if you slip up in the syntax even slightly.

like image 5
Leushenko Avatar answered Nov 10 '22 05:11

Leushenko