c++cmacrosc-preprocessorstring-literals

How can I generate a preprocessor definition using arithmetic?


I want to dynamically create preprocessor literal strings where part of the string is created by some arithmetic, for example: math(x) x - 0x1234. The generated definitions would be made by the macro: get_tex_uniform_name(unit), and the results would be the same values as the following numerically ordered definitions:

// Texture uniform naming
#define TEX_UNIFORM_BASE_NAME        "tex"
#define TEX_UNIFORM_0                TEX_UNIFORM_BASE_NAME"0"  // "tex0" and so on
#define TEX_UNIFORM_1                TEX_UNIFORM_BASE_NAME"1"
#define TEX_UNIFORM_2                TEX_UNIFORM_BASE_NAME"2"
#define TEX_UNIFORM_3                TEX_UNIFORM_BASE_NAME"3"

I have tried generating the string from scratch with STRINGIFY (#):

#define get_tex_unit_num(unit) unit - GL_TEXTURE0
// create string literal from whatever is put in
#define STRINGIFY(x) #x
#define LITERAL_STRINGIFY(x) STRINGIFY(x)
#define get_tex_uniform_name(unit) TEX_UNIFORM_BASE_NAME LITERAL_STRINGIFY(get_tex_unit_num(unit))

// Issue: result is "texunit - 0x84C0", 0x84C0 being GL_TEXTURE0

I have tried token pasting to work with existing definitions:

#define get_tex_unit_num(unit) unit - GL_TEXTURE0
#define get_tex_uniform_name(unit) TEX_UNIFORM_ ## get_tex_unit_num(unit)

// Error: TEX_UNIFORM_get_tex_unit_num is undefined

And have been trying to get some sort of bit masking to work:

#define TEX_UNIFORM_BASE_NAME        "tex "

#define get_tex_unit_num(unit) unit - GL_TEXTURE0
#define get_tex_uniform_name(unit) TEX_UNIFORM_BASE_NAME & (0xffffff00 + (char)get_tex_unit_num(unit))

// Error: expression must have integral or unscoped enum type

I know that this could be done with functions or by just using the concatenated literal strings as defined in the first example. I am not necessarily looking for a reason why my examples don't work. I am looking for a way to get these strings dynamically, and am having trouble using arithmetic inside macros to create a string literal.


Solution

  • Your first code sample should work, so it is not necessary to paste string literals together in the preprocessor. It is also not possible.

    It is not possible because ## pastes together two preprocessing tokens, and the result must be a valid preprocessing token. A preprocessing token is a header name, identifier, preprocessing number, character constant, string literal, punctuator, or single non-white-space character. You cannot paste together two string literals and remove the two quote marks that end up between them. However, it is usually unnecessary to paste them together; simply letting them expand next to each other suffices because adjacent string literals are concatenated.

    Your first code sample, which generates results like "tex" "0" is fine. After preprocessing and before analysis and translation (compilation), adjacent string literals are converted to a single concatenated string literal. So there will not be any problems of precedence or other syntax and semantics.