3

Is it possible to define a preprocessor macro that will expand in the following way.

MACRO1(x) (y,z,w)
--> MACRO2(x,y,z,w)

Can the expansion of MACRO1 somehow consume the left parenthesis of the list that follows the macro invocation and replace it with MACRO2(x, so that the preprocessor accepts the result as a valid macro invocation (assuming MACRO2 is defined) and not raise an unterminated argument list error?

I've tried to do something like this

#define STRANGE_MACRO(...) __VA_ARGS__
#define STRIP_PAREN(...) __VA_ARGS__)
#define PREPEND_AND_APPLY_STRANGE(x) STRANGE_MACRO(x,STRIP_PAREN

Calling it like this:

PREPEND_AND_APPLY_STRANGE(x) (y,z,w)

produces the unerminated argument list error. Is there a way to make it work?

As for the reason I'd like to have this behavior, it is for esetics, I think it looks better to have macro invocation like this

MACRO1(identifier) (
    more
    complex
    arguments
)

than

MACRO2(identifier,
    more
    complex
    arguments
)

I vould just like the former to be transfromed into the later. If it is not possible within the preprocessor rules, no big deal, I'll live with it, but if it is, I'd like to know the trick.

AstroCB
  • 12,337
  • 20
  • 57
  • 73
irpbc
  • 851
  • 1
  • 8
  • 16
  • 3
    Your idea of "estetics" [sic] is brutally different from mine. :) If it's a macro invocation, it should look like one, not like a syntax error. – unwind Apr 30 '14 at 10:14
  • Probably you looking for [something like that](http://stackoverflow.com/a/23238813/2549281) – Dabo Apr 30 '14 at 10:26

1 Answers1

3

If you want to incorporate an unmatched opening parenthesis in a replacement list, you need to add a layer of indirection:

#define LPAREN (

#define PREPEND_AND_APPLY_STRANGE(x) STRANGE_MACRO LPAREN x,STRIP_PAREN

This way the parenthesis won't be considered part of an invocation until the correct syntax is complete.

Unfortunately there's a limitation to this technique: by the time the call to STRANGE_MACRO is fully constructed, it's past the point where it would be eligible for rescanning - since the start and end of the expression were built across two different calls that took place at the same level, the whole thing never appeared in one rescan list - and will never actually expand; you'll just get STRANGE_MACRO ( x,y,z,w) dumped in your output. You need to force a rescan at the top level:

#define EXPAND(...) __VA_ARGS__

The macro does nothing, but does mean its argument gets to be put in a rescan list as a complete unit and will thus finally expand. So the closest you can get to your desired syntax would be this:

EXPAND(

MACRO1(identifier) (
    ....
)
MACRO1(identifier) (
    ....
)

)

...so you don't need to disfigure each of your custom blocks with its own EXPAND, but you do need to wrap the whole program in one. And no you can't hide the wrapper by putting an #include directive within EXPAND, as directives can't appear within invocations. (Still, perhaps you can make something useful of it by reinventing namespace or something similar that your syntax might need.)

This also has the disadvantage that the C compiler will perceive your entire program as being on one line, which will hurt error reporting somewhat - although you were already halfway to this problem, as each declaration block would also have seemed to be on only one line had the original version worked.

Alex Celeste
  • 12,824
  • 10
  • 46
  • 89
  • Damn, that poor preprocessor :) – dutt Apr 30 '14 at 22:44
  • @Leushenko This confirms my suspicion. I was afraid that a macro invocation composed out of results of two different invocations on the same level cannot be invoced by itself without wraping it in `EXPAND()`. – irpbc May 01 '14 at 08:31
  • I've found some interesting in the C++ standard. 16.3.4.1 says that "the resulting preprocessing token sequence is rescanned, along with all subsequent preprocessing tokens of the source file". So if I understand this correctly, the compilers are not complient with the standard because they are rescaning the macro ouput isolated from the rest of the file. – irpbc May 01 '14 at 08:45
  • *"Subsequent"* preprocessing tokens: after expanding `PREPEND_AND_...` it resets the scan position to the start of that replacement, i.e. to `STRANGE_MACRO`. It doesn't find a valid argument list for `STRANGE_MACRO` so it moves onto the next token (`LPAREN`). At this point the scan position *has moved past* `STRANGE_MACRO` and will not look at it again, regardless of what forms after it (at least, until we force it to with `EXPAND`). This is in totally line with the standard. – Alex Celeste May 01 '14 at 08:50
  • Oh yes, I see the problem. If we have a `#define MACRO1(x) MACRO2(x,STRIP_PAREN` than `MACRO1(x)(y,z,w)` would expand to `MACRO2(x,STRIP_PAREN(y,z,w)`. Now this will fail because the preprocessor must match an open with a closing paren for any macro invocation before expansion of arguments. So this cannot be done within the standard rules. – irpbc May 01 '14 at 12:12