Home > Blockchain >  In what situations does C preprocessor ## work and not work?
In what situations does C preprocessor ## work and not work?

Time:03-24

It seems that sometimes concatenation with ## does work and sometimes it does not work.

It is an unreliable feature even though it is clearly vitally necessary for some uses.

Is there a clear set of rules for using ##?

An example is:

file1.h
#define concat(a,b) a##b
#define BAR bar
extern int concat(fu,BAR) ();

Here concat produces fuBAR not fubar.

An example is:

file2.h
#define BAR bar
extern int fu##BAR ();

Here ## produces an error about a stray ## in the code.

CodePudding user response:

## obeys rules which are different than the rules you were expecting. That doesn't mean it "doesn't work" or "is an unreliable feature", it only means you have to use it differently than you thought.

When you write

#define concat(a,b) a##b

the C standard says that the arguments a and b are NOT macro expanded before the concatenation happens. (N1570 §6.10.3.1.) This is an intentional difference from the behavior when you don't apply ## or # to a macro argument.

You can get the behavior you wanted with double expansion:

#define concat(a,b) concat_(a,b)
#define concat_(a,b) a##b

With this definition, the arguments to concat are macro expanded before substitution into the macro body, since they are not being used as operands to ##. Then each argument becomes an argument to concat_, and isn't expanded again, but that's fine, because the expansion is done already.

And when you write

#define BAR bar
extern int fu##BAR ();

the C standard says that the ## operator is not recognized at all by the preprocessor (and therefore passes on to translation phase 7, where it is a valid token that isn't accepted by any grammar rule, and therefore causes a syntax error) because it's not part of a macro definition. (N1570 §6.10.3.3p2,3 — by implication only; it says that ## is recognized when it appears in the replacement list of a macro being expanded. This section doesn't say that it is recognized at any other time, and no other part of the standard gives ## a meaning in any other context.)

CodePudding user response:

Is there a clear set of rules for using ##?

The C standard 6.10.3.1:

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.

Basically the ## concatenation happens before token expansion, because the result of ## could result in a new preprocessor token. In case we have this:

#define concat(a,b) a##b
#define BAR bar
#define fuBAR "hello world"

Then puts(concat(fu,BAR)); will print "hello world".

In order to fix this, you need a helper macro which expands the preprocessor tokens before passing them to the macro where ## (or #) resides:

#define cc(a,b) a##b
#define concat(a,b) cc(a,b)

#define BAR bar
#define fuBAR "hello world"
#define fubar "fubar"

In this example a and b are expanded to fu and bar before cc is invoked. So now puts(concat(fu,BAR)); will print "fubar".


Here ## produces an error about a stray ## in the code.

Because you can only use # and ## inside macros, simple as that. extern int fu##BAR (); is not a macro.

CodePudding user response:

It seems that sometimes concatenation with ## does work and sometimes it does not work.

That's like saying "sometimes multiplication with * does work, and sometimes it doesn't", and then presenting examples such as

int six = 5;
int product = six * 2; // results in 10 instead of 12

and

void f(int a, int b, int a * b);  // rejected by the compiler

The specifications for the token-pasting operator are presented in 6.10.3.3 of the C17 language specification, and your first clue should be that this is a subsection of 6.10.3, which describes macro replacement. Not only the placement of the section but also its explicit text make it clear that token concatenation occurs (only) in the context of macro replacement:

For both object-like and function-like macro invocations, before the replacement list is reexamined for more macro names to replace, each instance of a ## preprocessing token in the replacement list (not from an argument) is deleted and the preceding preprocessing token is concatenated with the following preprocessing token.

(C17 6.10.3.3/3)

There are no other specifications for special treatment of ##, so that's its full scope.

Additionally,

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.

(C17 6.10.3.1/1; emphasis added)

Thus, where function-like macro arguments are operands of a ## (or #) operator, they are not themselves macro-expanded, though the concatenated result might be expanded as a macro when the result is re-scanned.

  • Related