Yes, it'd valid. Pre-processing of function like macros is described in the C standard by §6.10.3 Macro replacement. The pertinent parts are the following:
¶10 ...Each subsequent instance of the function-like macro name followed
by a (
as the next preprocessing token introduces the sequence of
preprocessing tokens that is replaced by the replacement list in the
definition (an invocation of the macro)....
6.10.3.1 Argument substitution
¶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. Before being substituted, each argument's preprocessing
tokens are completely macro replaced as if they formed the rest of the
preprocessing file; no other preprocessing tokens are available.
6.10.3.4 Rescanning and further replacement
¶1 After all parameters in the replacement list have been substituted
and # and ## processing has taken place, all placemarker preprocessing
tokens are removed. The resulting preprocessing token sequence is then
rescanned, along with all subsequent preprocessing tokens of the
source file, for more macro names to replace.
Except for section names and numbering, the same wording exists in the C++ standard too.
So when you plug X_LIST
in, the preprcoessor will replace X
by it after attempting to expand X_LIST
as though it was an object like macro. Since it's not, the tokens left with for X
is X_LIST
.
Then the preprocessor scans the line again. This time X_LIST
will be followed by a (
, and so will be expanded now.
Passing a function like macro's name to a "higher order function" is not unheard of. The Boost.Preprocessor library makes heavy use of this idiom.