30

I know that my question is similar to this one or this one, but I find that it is not really the same and, more, the second one has not an answer accepted, I decided to ask if it is correct to add preprocessor directives when a function-like macro is called?

In my case I have a function-like macro:

#define FUNC_MACRO(a, b)  // do something with the variables

and somewhere in the code I call it with specific difference if some other macro is defined:

// ...
FUNC_MACRO(aVal
#ifdef ANOTHER_MACRO
                + offset
#endif // ANOTHER_MACRO
           , bVal);
// ...

I tested on my machine (linux, with gcc 4.8) and it worked ok (with and without the preprocessor directives, and with and without ANOTHER_MACRO defined), but is it safe to do so?

I read the 16.3/9 paragraph from the answer of the first similar question, but is it true for my case too?

Community
  • 1
  • 1
sop
  • 3,445
  • 8
  • 41
  • 84
  • 8
    _" but is it safe to do so?"_ Define _safe_ please. – πάντα ῥεῖ Sep 16 '16 at 15:02
  • 4
    Is this C or C++? In C++ there are better alternatives. – Bathsheba Sep 16 '16 at 15:03
  • It's fine, but other macro related stuff can go quite wrong. If you're using C++, get away from this. – DeiDei Sep 16 '16 at 15:03
  • 8
    In general, macros should be avoided if possible. Macros obfuscating the code should be avoided definitely. – Eugene Sh. Sep 16 '16 at 15:03
  • 2
    The mentioned macro and ifdefs feel very wrong. You should try to find another way in the logic before trying to call a macro like that – bgeschka Sep 16 '16 at 15:08
  • `#define FUNC_MACRO_OPTIONAL_OFFSET(a, offset, b)` – Peter Sep 16 '16 at 16:04
  • 1
    This code reminds me of the adage "When you use Regular Expressions to solve a bug, now you have 2 bugs." – Engineer Sep 16 '16 at 21:29
  • 2
    This is too broad, there should be one question per programming language and the answer is not necessarily the same for each language. – Vality Sep 16 '16 at 21:59
  • As the tags say, it is C/C++ code, so I wouldn't say it is board. Anyway. thanks for the comments and answers. It's much more clear now. – sop Sep 18 '16 at 12:46
  • @Bathsheba Can you mention the alternatives, please? – sop Sep 19 '16 at 05:58
  • @sop: A header file can be C/C++ code, but when you feed a file to a compiler it treats it either as C or C++ (and not some wierd mixture). They are different languages, and there is syntax that is valid in one, but not the other; there is also source which is valid as C and as C++, but produces different output in the different languages. – Martin Bonner supports Monica Sep 21 '16 at 14:56
  • @MartinBonner: well, the project is big and it has both C and C++ code/files. But to be more precisely, these preprocessor directives in a function-like macro are in a C file, so I suppose it's compiled as C code. Can you answer both in C and C++, as you mentioned, it would be a good one? – sop Sep 26 '16 at 07:56
  • 1
    You already have an answer for the language you are most interested in: it is UB – Martin Bonner supports Monica Sep 26 '16 at 07:59

2 Answers2

45

The C language leaves this as undefined behavior in 6.10.3 Macro replacement, ¶11:

If there are sequences of preprocessing tokens within the list of arguments that would otherwise act as preprocessing directives, the behavior is undefined.

So indeed it's wrong to do it.

GCC and perhaps other popular compiles don't catch it, which is probably why many users of the language are not aware. I encountered this when some of my code failed to compile on PCC (and promptly fixed the bug in my code).

Update: PJTraill asked in the comments for a case where it would be "misleading or meaningless" to have preprocessor directives inside a macro expansion. Here's an obvious one:

    foo(a, b,
#ifdef BAR
        c);
#else
        d);
#endif

I'm not sure whether it would have been plausible for the language to specify that balanced preprocessor conditionals inside the macro expansion are okay, but I think you'd run into problems there too with ambiguities in the order in which they should be processed.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • 2
    This is nice to know, but given how often Microsoft needs to "adjust" methods to work across ASCII and Unicode or "normal" and "secure", etc, I would hope their compilers actually support this. IMHO, the whole point of a macro is that you shouldn't need to know its a macro. – Mark Hurd Sep 21 '16 at 05:21
  • 2
    That seems conclusive, but at first glance there seems to be an intuitive reasonable interpretation of the code — can you motivate the language standard with a case where it would be misleading or meaningless? – PJTraill Sep 21 '16 at 11:55
  • 1
    Obviously, you can explicitly set up a scenario where the `#if` is intended to be evaluated "inside" a macro, but I would assume the most understandable behaviour would always be the evaluate any `#if`-like preprocessing tokens before macro epansion. – Mark Hurd Oct 09 '16 at 03:21
23

Do the following instead?

#ifdef ANOTHER_MACRO
FUNC_MACRO(aVal + offset, bVal);
#else
FUNC_MACRO(aVal, bVal);
#endif

EDIT: Addressing concern raised by comment; I do not know if the OP's method is specifically wrong (I think other answers cover that). However, succinctness and clarity are two aspects held to be pretty important when coding with C.

As such I would much prefer to find better ways to achieve what the OP seems to be trying, by slightly rethinking the situation such as I have offered above. I guess the OP may have used a triviallised example but I usually find with most C situations that if something is becoming overly complex or attempting to do something it does not seem like the language should allow, then there are better ways to achieve what is needed.

Toby
  • 9,696
  • 16
  • 68
  • 132
  • 2
    That's a solution, not an answer. A thing that sure does work, to avoid another one that perhaps doesn't, but we really don't know. He wants to know it for sure, though. – ABu Sep 16 '16 at 19:06
  • 7
    @Peregring-lk You're incorrect. [How to Answer](http://stackoverflow.com/help/how-to-answer) explicitly welcomes answers that give alternative solutions: "The answer can be 'don’t do that', but it should also include 'try this instead'." This isn't the *best* answer, since a great answer would also explain why it's bad along with the alternative, but it is a legitimate answer to the question. – jpmc26 Sep 16 '16 at 20:16