1

I have a macro, SOME_MACRO. It takes an argument.

Definition of SOME_MACRO:

#define SOME_MACRO(arg) __SOME_MACRO(arg)

Further I want __SOME_MACRO(arg) to expand to "ABC" if arg is 0. And if arg is not zero, __SOME_MACRO(arg) should expand to "DEF"

Suppose I call with argument 0, that is: SOME_MACRO(0) I need to test at preprocessing time, whether the argument is zero or not.

#if <argument is zero>     // line 1
#define __SOME_MACRO(arg) "ABC"
#elif
#define __SOME_MACRO(arg) "DEF"
#endif

My question is: What should be at line 1?

Taking an example:

SOME_MACRO(2) should expand to "DEF"

SOME_MACRO(0) should expand to "ABC"

  • What do you want for `SOME_MACRO(1+3-4)`? – Eric Postpischil May 31 '20 at 11:13
  • Generally, the C standard does not provide facilities for conditional expansion of macros. There are some kludges that serve in some cases, but they should be avoided when possible. Usually, when somebody is trying to do this sort of thing, there is a better way to accomplish their actual goal. What are you trying to accomplish with this—why do you need it? – Eric Postpischil May 31 '20 at 11:15
  • @EricPostpischil. `1+3-4` is not very critical. I believe this case won't occur (in my code). SO i don't care basically for `1+3-4`. In strict sense I would like it to expand to `"ABC"` but still I am not very concerned about this scenario. –  May 31 '20 at 11:16
  • 2
    `#define SOME_MACRO(arg) ((arg) ? "DEF" : "ABC")` – pmg May 31 '20 at 12:02
  • @NeedSomeLuck `__SOME_MACRO` Please do not use identifiers with two leading underscores. Such identifiers are reserved by C standard. – KamilCuk May 31 '20 at 20:01
  • @KamilCuk: Note the tag [tag:linux-kernel]... – Nate Eldredge May 31 '20 at 22:43

2 Answers2

2

This is not possible. The expression given to #if is only evaluated once, at the line where #if is encountered. There is no way to have it evaluated again each time you invoke your macro.

As Eric Postpischil says, C does not have any general way to make the expansion of a macro conditional on its arguments.

A couple of possible alternatives:

  • Use an expression with the ternary ?: operator, as pmg suggested in a comment:

    #define __SOME_MACRO(arg) ((arg) ? "DEF" : "ABC")
    

    Then SOME_MACRO(0) will expand to ((0) ? "DEF" : "ABC"). Any reasonable compiler will notice that the condition is a constant, and compile this just as if you had only written "ABC"; it should not generate code for a test nor for the unreachable branch. Conditionals with the ternary operator can even be used in expressions that must be compile-time constants, as long as the condition is itself a compile-time constant; e.g. __SOME_MACRO(0) can be used to initialize a global or static variable.

    Of course this will not work if the desired expansion is not syntactically an expression, e.g. if you are trying to use your macro to create declarations or definitions or something more syntactically elaborate.

  • If you know that only a small set of possible arguments will be used, you could use token pasting with the ## operator to expand the macro as one of another set of macros:

    #define __SOME_MACRO_0 "ABC"
    #define __SOME_MACRO_1 "DEF"
    #define __SOME_MACRO_2 "DEF"
    #define __SOME_MACRO(arg) __SOME_MACRO_ ## arg
    

    Then __SOME_MACRO(0) expands to "ABC" and __SOME_MACRO(2) expands to "DEF". However __SOME_MACRO(3) will not compile unless you write a corresponding __SOME_MACRO_3 macro, and __SOME_MACRO(0+1) cannot be made to work at all, at least not in any way that I know of.

    (As written, __SOME_MACRO(0+1) expands to "ABC"+1, which is equivalent to "BC" and will probably cause you a lot of confusion before you figure out what is wrong. __SOME_MACRO(1-1) is even worse...)

    Also, #define ZERO 0 followed by __SOME_MACRO(ZERO) doesn't work as it stands, thought it can be fixed by doing

    #define SECOND_MACRO(arg) __SOME_MACRO_ # arg
    #define __SOME_MACRO(arg) SECOND_MACRO(arg)
    
Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82
  • OK I have edited my question. Can you please provide other ways to solve the problem . Thanks –  May 31 '20 at 18:43
  • "and __SOME_MACRO(1+1) cannot be made to work at all" ...[why not](http://coliru.stacked-crooked.com/a/6611b100a0c825ff)? – H Walters Jun 01 '20 at 04:45
  • 1
    @HWalters: Okay, but now `__SOME_MACRO(1-1)` doesn't work, nor does `__SOME_MACRO(0+1)`. – Nate Eldredge Jun 01 '20 at 19:17
2

Here is a solution for the requested behavior. Do not use it, it is bad practice, and you should not be attempting this.

/*  Define __SOME_MACRO:

        Defer is used so that its arguments will be macro-replaced before
        Kludge is processed.

        "Kludge" is pasted to arg.  If this makes "Kludge0", it will be
        replaced by two arguments (an empty argument, a comma, and the argument
        "abc").  Otherwise, if it forms a single token, that is one argument.
        (If the pasting does not form a proper token, the behavior is
        undefined.  Most likely, the compiler will report an error.)

        The resulting argument(s) are then passed to Kludge, followed by "DEF".

        Kludge is replaced by its second argument.  If __SOME_MACRO's argument
        was "0", this second argument is "ABC".  Otherwise, it is "DEF".
*/
#define Kludge0             , "ABC"
#define Kludge(x, y,...)    y
#define Defer(x, ...)       Kludge(x, __VA_ARGS__)
#define __SOME_MACRO(arg)   Defer(Kludge##arg, "DEF",)

__SOME_MACRO(0)
__SOME_MACRO(2)
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312