Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C preprocessor using the closing bracket of a parent macro

I have this code which works:

#include <stdio.h>
#define A(x) x B
#define B(x) C(x,
#define C(x,y) y x)
int main( void ) {
    printf( A("1") ("2") "3" );
}

It prints 132 (the point of the A macro is to swap the thing which follows its parameters in brackets with everything after that until another closing bracket)

But if I use that within another macro:

#define Z(x) x
printf( Z( A("1") ("2") "3" ) );

I get the compile error "Unterminated function-like macro invocation".

I realise that this happens because the compiler is trying to process the arguments of Z independently, but I need to use its closing bracket as a marker. Is there a way I can make this work within macros? Changing the calling syntax isn't really an option.


p.s. Before I get any responses talking about what an awful thing this is to do, rest assured: this is not for real code. It is a problem which came up while making a toy program which uses define to simulate a new language inside C.

like image 527
Dave Avatar asked Apr 07 '13 12:04

Dave


2 Answers

The easiest way to see what's going on is to change the test case a little.

#define A(x) x B
#define B(x) C(x,
#define C(x,y) y x]  /* note close square bracket instead of close paren */

Y(A(1)(2)3)

preprocesses to Y(1 3 2]. This is because an intermediate stage of expansion looked like

Y(1 C(2,3)

at which point C ate the close paren that appeared to belong to Y in the original text and replaced it with a close bracket.

Now, what happens differently if A(1)(2)3 is inside a macro argument?

#define Z(x) x
Z(A(1)(2)3)

Because of argument prescan, the analogous intermediate stage of expansion is not

Z(1 C(2,3)

but rather

1 C(2,3

with Z squirrelled away on a hidden "pending expansions" stack. The preprocessor is, in effect, enforcing the textual appearance that that final close paren belongs to Z, and C is not allowed to borrow it.

The least-invasive way I can think of to achieve your original goal is

#define _A(x) x B
#define B(x) C(x,
#define C(x,y) y x)

#define Z(x) ZZ((_##x))
#define ZZ(x) ZZZ x
#define ZZZ(x) [x]

Z(A(1)(2)3)

preprocesses to [1 3 2]. We use the token paste operator to prevent Z's argument from being prescanned, so we can add a temporary extra set of parentheses for use by C. ZZ and ZZZ then strip them off again. The catch is that it's an error if you don't paste x with something, so we have to add a leading underscore to the definition of A, and it will be an error if the first token of Z's argument is ever not something that can be token-pasted after an underscore.

You might want to consider using M4 instead of trying to shoehorn this into the C preprocessor.

like image 174
zwol Avatar answered Nov 05 '22 07:11

zwol


eclipse cdt is excellent to debug your questions. for eclipse, just hover over a macro to get started. here is detialed info on it:

C/C++ Software Development with Eclipse >> 2.1.7. Macro Expansion

for your second macro, eclipse shows the following:

int main (void) {
    printf( Z( A("1") ("2") "3" ) );
}

enter image description hereenter image description hereenter image description hereenter image description hereenter image description here

Spotting the Error

Notice in the expansion #3 C("2", "3" just 'disappears. I take this as CDT's way of saying 'unterminated argument list'. Whatever the case for it disappearing, this is the method I prefer to take when debugging macros.

  • Using this tool makes it clear that in Expansion#2 (third image) we have an unterminated set of brackets, thus locating the error.

Understanding a Solution

After fiddling around a bit using this tool, I think this is what you were after:

printf( Z( (A("1") ("2") "3") ) );

yields (using gcc -E main.c -c)

printf( ("1" "3" "2") );

enter image description here

like image 20
J-Dizzle Avatar answered Nov 05 '22 08:11

J-Dizzle