gcc preprocessor variables to string literals - c

I know there a bazillion question like this, but I couldn't find a good answer.
I'm compiling a source with preprocessor tokens defined in CFLAGS:
-D X=string1 -D Y=string2
I'm trying to create a string literal in my header file that combines these two values and use it in my sources like:
printf("%s", COMBINED);
But this doesn't work:
#define _COMBINED(x, y) x ## y
#define COMBINED _COMBINED(X, Y)
Thanks

To pass a string-ized token to a concatenating macro, you need a level of indirection. That means you pass it to a different macro.
#define COMBINED(a, b) (a##b)
#define COMBINE(a, b) COMBINED(a, b)
Then you pass off the params in COMBINE, in your case, X and Y
int main (void) // best practice is to void-erize non-param-ed functions
{
printf("%s\n", COMBINE(X,Y));
}
If you really want to do it "directly", you can do it like this:
#define DEFAULT_COMBINE COMBINE(X,Y)
And call:
printf("%s\n", DEFAULT_COMBINE);
Now, let's end with a caveat, as this is also about "best practice" so it's quite important even though it might not seem that way to you, it is, trust me on this one. Never ever, and i mean NEVER EVER start off your own preprocessor defines or constants with an underscore. Never, under any circumstances. Even if your brother works at Microsoft or worked on the Linux kernel.
In the standard and best-practice C and C++ world, the _COMBINED macro is illegal. No, not invalid, it just means you shouldn't name it like that. The short version is that constants, intrisincs and macros startign with an underscore are compiler or internal only. It's a "marker" to know that it's not something someone set.
UPDATE:
To concatenate them without being strings, you'll also need an intermediate step to stringify:
#define COMBINED(x1, x2) x1##x2
#define COMBINE_STRINGS(x1, x2) COMBINED(#x1,#x2)
#define COMBINE(x1, x2) COMBINE_STRINGS(x1, x2)

-D X=string1 creates a pre-processor token similar to #define X string1. Nothing so far makes this a string literal, you need to use the # "stringinizing" operator inside a function-like macro in order to do that.
Also when dealing with string literals there's no need to use token concatenation with ## since typing two string literals after each other means they get concatenated by the preprocessor.
As usual with # and ##, any macro argument that they are applied to needs to be expanded in advance before the operator is used, since these operators are always applied before token replacement. So we can't just do this:
#define STRCAT(x,y) #x #y
#define COMBINED STRCAT(X,Y)
// bad: result will BE "XY"
Instead use an additional helper macro:
#define STR(x) #x
#define STRCAT(x,y) STR(x) STR(y)
#define COMBINED STRCAT(X,Y)
// good: result will be "string1string2"

You did not show an example how you call the compiler, so I made some experiments. Since commas (and other non-alphanumerical characters) will get in the way, it boiled down to the need to pass quotation marks as delimiters of the replacement text of the macros.
So I think your real issue is how to pass the strings in an equivalent way to:
#define X "hello, "
#define Y "world!"
Note: Here extra difficult because of comma and exclamation mark.
If the macro replacement text is passed including the quotation marks, the C source gets simple. In C, you concatenate string without any intermediate operator. "abc" "def" is the same as "abcdef".
#include <stdio.h>
#define COMBINED X Y
int main(void) {
printf("%s\n", COMBINED);
}
Depending on your real use case, you could even simplify this to:
#include <stdio.h>
int main(void) {
printf("%s\n", X Y);
}
In a Bash you need to escape the command line arguments like this, for example:
gcc -Wall -pedantic -D 'X="hello, "' -D 'Y="world!"' combine.c -s -O3 -o combine
In Windows' CMD you can escape the quotation marks like this, for example:
gcc -Wall -pedantic -D "X=\"hello, \"" -D "Y=\"world!\"" combine.c -s -O3 -o combine.exe

Related

Confusion about macro expansion in C

#include <stdio.h>
#define MYNUMBER 123
int main()
{
printf("%d", MYNUMBER456);
}
Above code doesn't work because MYNUMBER and MYNUMBER456 are different token.
#include <stdio.h>
#define SECOND(a, b) printf(#a #b);
#define FIRST SECOND
int main()
{
FIRST(hello, world!)
}
But this one works well. My thought is FIRST and FIRST(hello, world!) are different so it should not work. What am I missing?
You can see the macro expansion by using the -E option (cc -E main.c), though you will see a lot of other stuff inserted because of your #include <stdio.h>, and without it you will probable see some default stuff inserted, in your example the main function becomes
int main()
{
printf("hello" "world!");
}
This is because you have defined FIRST to be the same as SECOND which takes two arguments and makes them strings and since there is no comma between them they get concatenated into a single string, macros are just string substitution, in C the preprocessing is traditionally handled by a seperate executable to the compiler and is not as sophisticated as a compiler, so the type matching you would expect in most languages doesn't apply to the preprocessor.
you are correct MYNUMBER and MYNUMBER456 are different and the pre-compiler wont know how to work with MYNUMBER456
however, when you defined FIRST as SECOND , the precompiler would expand FIRST by SECOND and then you actually have SECOND with 2 parameters so it it working

How to parametrize single string containing # in C preprocessor?

I want to create parameterized preprocessor macro for IBM Metal C Prolog.
The initial unparametrized form is
#pragma prolog(Foo, " #MCPROLG MAIN=(YES,16,132)")
The real prolog is more complex, but for the sake of the question the important part is that there are values within a string.
#pragma directive itself can't be part of a macro, so I replace that with _Pragmalike that:
_Pragma("prolog(Foo, \" #MCPROLG MAIN=(YES,16,132)\")")
I can parameterize Foo like this:
#define STR(...) #__VA_ARGS__
#define PROLOG(function) _Pragma(STR(prolog( function , " #MCPROLG MAIN=(YES,16,132)")))
How can I create macro that parameterizes the value 16?
It appears that I need to concatenate strings within preprocessor, I've tried following approaches. All use this stringization macro:
#define STR(...) #__VA_ARGS__
Token that is replaced with 16 (let's name it size) can't be within a string itself so that it is replaced.
#define PROLOG(function, size) _Pragma(STR(prolog( function , " #MCPROLG MAIN=(YES, size ,132)")))
_Pragma only accepts a single string, so I can't peruse C string concatenation like this:
#define PROLOG(function, size) _Pragma(STR(prolog( function , " #MCPROLG MAIN=(YES," #size ",132)")))
I can't stringize the whole second argument of prolog like this:
#define PROLOG(function, size) _Pragma(STR(prolog( function , STR( #MCPROLG MAIN=(YES, size ,132)))))
because #MCPROLG needs to stay within a string so that its # is not treated as stringization token.
To address issue 1 (need to expand a parameter in the replacement list), you need an indirect stringify macro:
#define STR(X) STR_I(X)
#define STR_I(X) #X
You may make this variadic if you want but it's not necessary here (the commas in your string are surrounded by parentheses; the preprocessor will match those; e.g., FOO(A=(B,C,D)), given FOO is a function-like macro, has one argument).
To address issue 2, yes, you need to stringify the whole thing. Effectively there's no such thing as string literal concatenation to the preprocessor (because it runs in translation phase 4, and string literal concatenation doesn't happen until translation phase 6).
To address issue 3, just produce a hash from another macro (root it at an object-like macro, where # has no special meaning):
#define HASH #
#define HASHM() HASH
The function-like variant allows you to produce the same hash right next to something else (HASHMCPROLG does nothing useful; HASH MCPROLG produces # MCPROLG, HASHM()MCPROLG produces #MCPROLG).
Those pieces in hand, the rest is easy:
#define PROLOG(FN_,SZ_) _Pragma(STR(prolog(FN_, STR( HASHM()MCPROLG MAIN=(YES,SZ_,132)))))
Here I'm assuming you also need an end parentheses around the pragma prolog and that in the question this was a typo; that is, it should be:
_Pragma("prolog(foo, \"#MCPROLG MAIN=(YES,16,132)\")")
...not:
_Pragma("prolog(foo, \"#MCPROLG MAIN=(YES,16,132)\"")

C Stringize result of equation

I have read lots on stringizing macros, but I obviously don't quite understand. I wish to make a string where the argument to the macro needs to be evaluated first. Can someone please explain where I am going wrong, or perhaps how to do this better?
#define SDDISK 2 // Note defined in a library file elsewhere ie not a constant I know)
#define DRIVE_STR(d) #d ":/"
#define xDRIVE_STR(x) DRIVE_STR(x)
#define FILEPATH(f) xDRIVE_STR(SDDISK + '0') #f
const char file[] = FILEPATH(test.log);
void main(void)
{
DebugPrint(file);
}
The output is: "2 + '0':/test.log",
But I want "2:/test.log"
The C PREprocessor runs before the compiler ever sees the code.
This means that the equation will not be evaluated before it is stringified; instead, the preprocessor will just stringize the whole equation.
In your case just removing the +'0' will solve the problem as the value of SDDISK does not need casting to a char before it is stringified.
However, should you actually need to perform a calculation before stringizing you should either:
Use cpp's constexpr.
Complain to your compiler vendor that a constant expression was not optimized.
Use a preprocessor library to gain the wanted behaviour.

Is there a one-macro way to prefix and quote C macro argument

So when looking into getting my define macro to work, I found the # and ## macro helpers, and used them to simplify my macro. The key part of the macro sets a variable to a string containing the name of the variable (but not the variable name alone). As a simplified example, let's take a macro called SET(X) that should expand SET(something) into something = "pre_something".
The only way I've found to do it so far is with two macros like #define QUOTE(X) #X and #define SET(X) X = QUOTE(pre_##X). However, using multiple macros seems excessive, and may cause problems with further macro expansion (I think). Is there a cleaner, one-line way of doing the same thing?
#define SET(x) x = "pre_"#x
C does string concatenation at compile time, so two string literals next to each other are concatenated.
"hello " "world" -> "hello world"

Renaming a macro in C

Let's say I have already defined 9 macros from
ABC_1 to ABC_9
If there is another macro XYZ(num) whose objective is to call one of the ABC_{i} based on the value of num, what is a good way to do this? i.e. XYZ(num) should call/return ABC_num.
This is what the concatenation operator ## is for:
#define XYZ(num) ABC_ ## num
Arguments to macros that use concatenation (and are used with the operator) are evaluated differently, however (they aren't evaluated before being used with ##, to allow name-pasting, only in the rescan pass), so if the number is stored in a second macro (or the result of any kind of expansion, rather than a plain literal) you'll need another layer of evaluation:
#define XYZ(num) XYZ_(num)
#define XYZ_(num) ABC_ ## num
In the comments you say that num should be a variable, not a constant. The preprocessor builds compile-time expressions, not dynamic ones, so a macro isn't really going to be very useful here.
If you really wanted XYZ to have a macro definition, you could use something like this:
#define XYZ(num) ((int[]){ \
0, ABC_1, ABC_2, ABC_3, ABC_4, ABC_5, ABC_6, ABC_7, ABC_8, ABC_9 \
}[num])
Assuming ABC_{i} are defined as int values (at any rate they must all be the same type - this applies to any method of dynamically selecting one of them), this selects one with a dynamic num by building a temporary array and selecting from it.
This has no obvious advantages over a completely non-macro solution, though. (Even if you wanted to use macro metaprogramming to generate the list of names, you could still do that in a function or array definition.)
Yes, that's possible, using concatenation. For example:
#define FOO(x, y) BAR ##x(y)
#define BAR1(y) "hello " #y
#define BAR2(y) int y()
#define BAR3(y) return y
FOO(2, main)
{
puts(FOO(1, world));
FOO(3, 0);
}
This becomes:
int main()
{
puts("hello " "world");
return 0;
}

Resources