Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Macro concatenation using compiler define

This should be simple, but I'm struggling to figure it out. I have PROJECT_NAME as a compiler (g++) -D define, and I want to concatenate it with some other text to form a namespace name. My current approach is this:

#define VERSION_NAMESPACE PROJECT_NAME ## Versioning

For my current project, I expect VERSION_NAMESPACE to be Syren_DLLVersioning. Instead I get a compiler error:

error: 'PROJECT_NAMEVersioning' has not been declared

But according to the g++ call, PROJECT_NAME is being defined properly:

ccache g++ ... -DPROJECT_NAME=Syren_DLL ...

Why is PROJECT_NAME not being replaced before the concatenation takes place?

like image 900
cmannett85 Avatar asked Dec 15 '22 12:12

cmannett85


1 Answers

A macro name is not expanded when it appears next to the ## operator, so you need more layers of indirection:

#define P_VERSION2(foo)   foo ## Versioning
#define P_VERSION(foo)    P_VERSION2(foo)
#define VERSION_NAMESPACE P_VERSION(PROJECT_NAME)

so that PROJECT_NAME is expanded as the argument of P_VERSION and then concatenated in P_VERSION2.

In section 16.3.3 [cpp.concat], paragraph 3, it is specified

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.

that preprocessing tokens adjacent to a ## preprocessing token are concatenated before macro-replacement is done on the replacement list. Therefore, PROJECT_NAME must be passed through another (function-like) macro for it to be replaced and concatenated with Versioning.

But in 16.3.1 [cpp.subst], paragraph 1, the standard specifies (emphasis added by me)

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.

that macro parameters are not subject to further macro-expansion if adjacent to a ## preprocessing token. Therefore, the function-like macro that receives PROJECT_NAME as argument must not directly concatenate its argument with Versioning, but to expand PROJECT_NAME it must call another function-like macro that finally does the concatenation.

So in the above, with the invocation ccache g++ ... -DPROJECT_NAME=Syren_DLL ..., PROJECT_NAME is replaced with Syren_DLL when P_VERSION(PROJECT_NAME) is expanded, resulting in P_VERSION2(Syren_DLL) which then leads to the concatenation of Syren_DLL and Versioning.

like image 137
Daniel Fischer Avatar answered Dec 28 '22 00:12

Daniel Fischer