5

I have a problem with macro expansion deferral. Here is an example:

#include <stdio.h>

#define CONST_ABC 15
#define CONST_5 7
#define ABC 5

#define PRINT(x) printf("CONST=%d\n", CONST_ ## x)

// The problematic macro
#define PRINT2(x) PRINT(x)

int main(int argc, char *argv[])
{
    PRINT(ABC); // Prints 15 - OK
    PRINT2(ABC); // Prints 7 - Not OK.
}

How to define PRINT2 macro so that it will use PRINT and result would be 15? I'm getting:

CONST=15
CONST=7

And want to get:

CONST=15
CONST=15
Valeri Atamaniouk
  • 5,125
  • 2
  • 16
  • 18
  • If `PRINT` gives you the desired result, why don't you want to just use it? – rici Feb 16 '15 at 17:36
  • 5
    @rici Because this is a minimal example. There are other parameters and far more complicated logic. – Valeri Atamaniouk Feb 16 '15 at 17:37
  • Fair enough, but the basic idea is the same. Using the double macro indirection causes `x` to be re-expanded, don't do the double indirection. (Defining `PRINT2` as `PRINT(CONST_ ## x)` would probably work, with the obvious change to `PRINT`.) Without seeing something closer to the real problem, it's hard to know what else to say. – rici Feb 16 '15 at 17:40
  • @rici There is a macro I have to use. And I should not even know what it does internally. So I need to pass parameter as it is given (if that is at all possible). So the problem is described in a simple way: I get a literal, and I must pass it as a literal without expansion. – Valeri Atamaniouk Feb 16 '15 at 17:44

2 Answers2

4

It requires you to have at least a C99 compiler, since C99 allows empty macro arguments. However some compilers may allow them as an extension, even in C89 mode. Here is the code:

#include <stdio.h>

#define CONST_ABC 15
#define CONST_5 7
#define ABC 5

#define PRINT(x) printf("CONST=%d\n", CONST_ ## x)

// The problematic macro
#define PRINT2(x, y) PRINT(x ## y)

int main(int argc, char *argv[])
{
    PRINT(ABC); // Prints 15 - OK
    PRINT2(ABC,); // Prints 7 - Not OK.
}

The second argument (i.e. the y) is empty, making it an empty preprocessing token. The ## operator prevents argument expansion, so the result of the concatenation is the same as x argument.

C11 6.10.3.1/p1 Argument substitution (emphasis mine):

After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. A parameter in the replacement list, unless preceded by a # or ## preprocessing token or followed by a ## preprocessing token (see below), is replaced by the corresponding argument after all macros contained therein have been expanded. Before being substituted, each argument’s preprocessing tokens are completely macro replaced as if they formed the rest of the preprocessing file; no other preprocessing tokens are available.

rici
  • 234,347
  • 28
  • 237
  • 341
Grzegorz Szpetkowski
  • 36,988
  • 6
  • 90
  • 137
  • 1
    @gregorz: hope that helped. – rici Feb 16 '15 at 17:55
  • Is it possible to replace second parameter with some form of define? – Valeri Atamaniouk Feb 16 '15 at 17:55
  • @ValeriAtamaniouk: I am not sure what you mean. Second parameter is only needed to prevent the former from being replaced. Notice that `PRINT2(ABC,)` is effectively the same as `PRINT2(,ABC)`. – Grzegorz Szpetkowski Feb 16 '15 at 18:03
  • Ok. Fair enough. Though I would not be able to use it due to code review issues. Defining a macro parameter that shall not be used would not go through, unfortunately. :( – Valeri Atamaniouk Feb 16 '15 at 18:09
  • @ValeriAtamaniouk: I see your point. No, it's not possible as `##` prevents second argument from expansion. Any `#define` directive would be useless then. For example `#define EMPTY`, then `PRINT2(ABC, EMPTY);` makes a token `ABCEMPTY`, which I believe is not what you want. The same goes for function-like macros. They are not expanded, because of `##`. – Grzegorz Szpetkowski Feb 16 '15 at 18:24
1

The macro replacement, basically, proceeds as follows:

  1. A token is found, which is a macro name
  2. The arguments of the macro are collected
  3. The arguments are substituted for the formal parameters in the body of the macro definition
  4. Thus substituted parameters are completely macro replaced, not counting the rest the of input; stringification may be performed at this stage as well;
  5. The token paste operators are performed
  6. Thus substituted sequence is re-scanned, together with the rest of the input for further macro replacements

(plus some not well defined rules when a macro is forbidden for replacement)

The only way to prevent a macro argument from being macro-replaced in 4. is for it to be followed or preceded by a token paste operator (##).

However, in 5. the paste operator has to perform the operation with the argument under discussion and a special placemarker token. A placemarker token is inserted only for empty argument substitution.

Check this, it might give you an idea for your real code:

#define PRINT2(noreplace,x) PRINT(noreplace ## x)

PS. and yeah, "noreplace" is meant to be empty :)

PRINT2(,ABC)
chill
  • 16,470
  • 2
  • 40
  • 44