Token Pasting (##) and Stringification (#) in C++
The C++ preprocessor offers metaprogramming capabilities that execute
during translation phase 4, well before the compiler analyzes the code's
syntax and semantics. Among its most distinct features are the
stringification operator (#) and the token-pasting operator
(##). This article explains how the preprocessor evaluates
these two operators, the mechanics of turning tokens into string
literals, how adjacent tokens merge to form new language constructs, and
the importance of macro argument prescan.
The Stringification Operator
(#)
The stringification operator is a unary operator represented by
# that operates exclusively on parameters within
function-like macro definitions. When applied to a macro parameter, it
converts the corresponding incoming argument into an ordinary string
literal.
How It Works
When the preprocessor encounters #parameter during macro
replacement:
- Token Conversion: It takes the exact sequence of
preprocessing tokens provided as the argument and encloses them in
double quotes (
"). - Whitespace Normalization: Leading and trailing whitespaces of the argument are removed. Any internal sequence of multiple whitespace characters is compressed into a single space.
- Character Escaping: Any double quotes
(
") or backslashes (\) occurring within the argument are automatically preceded by an escape backslash (\) to maintain proper string literal syntax.
#define TO_STRING(x) #x
const char* str1 = TO_STRING(int main()); // Expands to: "int main()"
const char* str2 = TO_STRING(He said "Hi"); // Expands to: "He said \"Hi\""The Token-Pasting Operator
(##)
The token-pasting (or concatenation) operator is a binary operator
represented by ##. It combines the token preceding it with
the token following it, merging them into a single, new preprocessing
token.
How It Works
During macro expansion, the preprocessor strips the ##
operator and any surrounding whitespace, taking the tokens directly on
the left and right sides and fusing them together:
#define MAKE_VAR(prefix, id) prefix##id
int MAKE_VAR(var_, 1) = 42; // Expands to: int var_1 = 42;Constraints and Undefined Behavior
The concatenated result must be a valid C++ preprocessing token (such as an identifier, keyword, numeric literal, or operator). If the concatenated tokens do not form a valid token, the behavior is undefined:
#define BAD_PASTE(a, b) a##b
BAD_PASTE(123, abc) // Valid: forms the pp-number token 123abc
BAD_PASTE(., >) // Undefined behavior: .> is not a valid tokenToken pasting cannot occur at the very beginning or end of a macro
replacement list; a ## operator must always have operands
on both sides.
The Macro Prescan Rule and Indirection
A critical aspect of both # and ## is how
they affect the evaluation order of macro arguments.
Normally, the preprocessor uses an argument prescan:
it fully expands all arguments before replacing them into the macro
body. However, operands directly attached to # or
## are exempt from this prescan. Their raw
token forms are stringified or pasted immediately, without expanding any
macros they might represent.
#define VERSION 2
#define GLUE(a, b) a##b
#define STRINGIFY(x) #x
GLUE(var_, VERSION) // Expands to: var_VERSION (not var_2)
STRINGIFY(VERSION) // Expands to: "VERSION" (not "2")Solving with Two-Level Macro Indirection
To expand macro arguments before stringifying or pasting them, an
additional layer of macro indirection is required. The outer macro
forces the argument to undergo prescan and expansion before handing it
off to the inner macro containing # or ##:
#define GLUE_IMPL(a, b) a##b
#define GLUE(a, b) GLUE_IMPL(a, b)
#define STRINGIFY_IMPL(x) #x
#define STRINGIFY(x) STRINGIFY_IMPL(x)
#define VERSION 2
int GLUE(var_, VERSION) = 10; // Expands to: int var_2 = 10;
const char* v = STRINGIFY(VERSION); // Expands to: const char* v = "2";Because VERSION is not adjacent to # or
## in the calling macro (GLUE or
STRINGIFY), the preprocessor fully expands
VERSION to 2 during the prescan, which is then
passed to the implementation macros.