-1

I recently saw some code like this:

#define JOIN(lhs, rhs)   JOIN_(lhs, rhs)
#define JOIN_(lhs, rhs)  JOIN__(lhs, rhs)
#define JOIN__(lhs, rhs) lhs##rhs

I tested the code, calling like these:

JOIN(Foo, 0);
JOIN_(Foo, 1);
JOIN__(Foo, 2);

JOIN(Foo, JOIN(A,B));
JOIN_(Foo, JOIN(A,B));
JOIN__(Foo, JOIN(A,B));

The macros expand into the following symbols:

Foo0
Foo1
Foo2
FooAB
FooAB
FooJOIN

I get the purpose, it's resolving arguments differently. Calling any of the variations of JOIN is clearly not the same on the last case. But how are these macros being expanded? Why do the arguments behave differently?

Edit: Here's the file

André Fratelli
  • 5,920
  • 7
  • 46
  • 87
  • I think, last macro `JOIN__(Foo, JOIN(A,B));' should be expanded as `FooJOIN(A,B)` and not just `FooJOIN`, please clarify. – Nitin Tripathi Sep 26 '15 at 20:18
  • It's possible. I get `warning: implicit declaration of function 'FooJOIN' is invalid in C99`, so I don't really see which arguments it's getting. But what you are saying makes sense – André Fratelli Sep 26 '15 at 20:21

2 Answers2

1

The ## tokenize operator doesn't evaluate (macro-expand) its arguments. Function-like macro expansions do evaluate arguments, however, and that is why you get expected (evaluated) output for the first cases.

Technically, the macro JOIN_ is unnecessary, since lhs and rhs in JOIN are evaluated when expanding JOIN__. This would suffice:

#define JOIN(lhs, rhs)   JOIN__(lhs, rhs)
#define JOIN__(lhs, rhs) lhs##rhs
owacoder
  • 4,815
  • 20
  • 47
1

EDIT

3.9.6 Argument Prescan

Macro arguments are completely macro-expanded before they are substituted into a macro body, unless they are stringified or pasted with other tokens. After substitution, the entire macro body, including the substituted arguments, is scanned again for macros to be expanded. The result is that the arguments are scanned twice to expand macro calls in them.

Macros that call other macros that stringify or concatenate. If an argument is stringified or concatenated, the prescan does not occur. If you want to expand a macro, then stringify or concatenate its expansion, you can do that by causing one macro to call another macro that does the stringification or concatenation.

For instance, if you have

#define AFTERX(x) X_ ## x
#define XAFTERX(x) AFTERX(x)
#define TABLESIZE 1024
#define BUFSIZE TABLESIZE

then AFTERX(BUFSIZE) expands to X_BUFSIZE, and XAFTERX(BUFSIZE) expands to X_1024. (Not to X_TABLESIZE. Prescan always does a complete expansion.)

CASE1

#define JOIN__(lhs, rhs) lhs##rhs => since it has a token pasting operator, it will concatenate the macro arguments those are not completely macro-expansion before substituted. --> which is a bad way to expand and on first place we don't know, what will be the arguments it will be passed to it, it won't wait for its expansion and it will simply concatenate it.

hence, when ever you call JOIN__(Foo, JOIN(A,B));, it won't allow JOIN(A,B) to expand and it will concatenate it to FOOJOIN(A,B).

CASE2 now, on the other hand, #define JOIN_(lhs, rhs) JOIN__(lhs, rhs) => here, there is no token pasting operator, Macro arguments are completely macro-expanded before they are substituted into a macro body. hence, it will allow lhs and rhs to expanded and is called with expanded parameters JOIN__(FOO,AB), hence now, JOIN__ has a token pasting operator, it will simply concatenate it arguments FOO and AB i.e. FOOAB. which is the appropriate way to do it.

CASE3 #define JOIN(lhs, rhs) JOIN_(lhs, rhs) => same as CASE2.

Hope, it explains the reason behind multi-level expansion paradigm.

ORIGINAL The preprocessor operator ## provides a way to concatenate actual arguments during macro expansion. If a parameter in the replacement text is adjacent to a ##, the parameter is replaced by the actual argument, the ## and surrounding white space are removed, and the result is re-scanned. For example, the macro paste concatenates its two arguments:

#define  paste(front, back)  front ## back

so paste(name, 1) creates the token  name1.
Nitin Tripathi
  • 1,224
  • 7
  • 17