Using macros inside a printf string in C? - c

Given 3 doubles x, y and z, I make a lot of such printf calls:
printf("[%+-8.3lf, %+-8.3lf, %+-8.3lf]\n", x, y, z);
I then would like to have a macro of some sort to write something like this:
#define FORMAT(x,y) "+-x.ylf"
printf("[%FORMAT(8,3), %FORMAT(8,3), %FORMAT(8,3)]\n", a->x, a->y, a->z);
But of course, the compiler sees %F as a special string and doesn't get my macro inside the string.
Is there a way to achieve what I want ?

Using simple numbers as arguments to FORMAT
Your "something like this" code is close — but you need to use string concatenation (of adjacent string literals) and the # operator to 'stringize' macro arguments:
#define FORMAT(x,y) "%+-" #x "." #y "lf"
printf("[" FORMAT(8,3) ", " FORMAT(8,3) ", " FORMAT(8,3) "]\n",
a->x, a->y, a->z);
This is similar to using the macros from <inttypes.h> for printing types such as int64_t, except with those, you have to provide the % symbol (and any flags):
uint64_t x = 0x43218765CBA9;
printf("x = 0x%.12" PRIX64 "\n", x);
Using macros as arguments to FORMAT
Would there be a way to define my 8 and 3 values as macros too? Like instead of writing everywhere FORMAT(8,3), I would like to write FORMAT(X, Y) where I defined above #define X 8 and #define Y 3.
Yes, there is a way to do that. Introduce an extra macro:
#define STR(z) #z
And invoke that on the arguments to FORMAT, as shown here:
/* SO 7531-4669 */
#include <stdio.h>
#define STR(z) #z
#define FORMAT(x,y) "%+-" STR(x) "." STR(y) "lf"
#define Y 4
#define X 8
struct Point { double x, y, z; };
int main(void)
{
struct Point b = { 7123.4567, 6234.5678, 5345.6789 };
struct Point *a = &b;
printf("[" FORMAT(X, Y) ", " FORMAT(X, Y) ", " FORMAT(X, Y) "]\n",
a->x, a->y, a->z);
printf("[" FORMAT(8, 3) ", " FORMAT(8, 3) ", " FORMAT(8, 3) "]\n",
a->x, a->y, a->z);
return 0;
}
This works and produces the output:
[+7123.4567, +6234.5678, +5345.6789]
[+7123.457, +6234.568, +5345.679]
It demonstrates that you can use simple numbers or macros that map to simple numbers as the arguments to this FORMAT macro. What you can't do is have #define Y 3 and #define X (Y + 6) — that will stringify (3 + 6) which isn't valid in a printf() conversion specification. (Beware of making X too big; you can end up with spaces between your number and the following comma. Experiment with #define X 12 to see what I mean.)
The technique of invoking another macro triggers the expansion of the argument, which is often what you want. See How to make a char string from a C macro's value? and Macro directives in C — my code example doesn't work. The Q&A How can I concatenate twice with the C preprocessor and expand a macro as in "arg ## _ ## MACRO"? is about token concatenation rather than stringification, but the issues are closely related, and the solutions are similar.

You could adjust the macro to something like:
#define SPEC "lf"
#define FORMAT(x, y) "+-"#x"."#y SPEC
and call printf :
printf("%"FORMAT(3, 20), x)
It might also be a good idea to place % inside the macro.
The single hash (#), "converts" x and y arguments to string literals.
Example

Related

Get the value of a macro using a string with macro name

I have a set of macro definitions that the name only change on the number between "C_" and "_E". What I need is a macro that gets a integer variable and returns the integer value of the corresponding macro definition in case it exists, if it does not exist, it returns "-1" or gives a compile error. Is that possible? The code I need is something like this:
#include <stdio.h>
#define C_1_E 4
#define C_2_E 2
#define C_3_E 0
#define C_4_E 420
#define STR(x) #x
#define STR_MACRO(x) "C_" STR(x) "_E"
#define MACRO_VAL(x) ... // return the value of the macro C_x_E when x=1,2,3 or 4
void main() {
uint8_t n;
for(n=1;n<=4;n++) printf("val %u: %u\n",n, MACRO_VAL(STR_MACRO(n)));
}
Expected output:
val 1: 4
val 2: 2
val 3: 0
val 4: 420
According to my search, this is not possible, but I can swear I did cross this solution once, but I didn't need it back then although I thought it could be helpful.
If you need to have a macro specifically, not a function, then it must be that a macro that expands to a function call is not acceptable either. That makes sense to me only if you need the conversion of macro number to macro expansion to be performed at compile time, by the preprocessor. That doesn't appear to be a necessity for the example code, but there are cases where it would indeed be needed.
And that's too bad, because the C preprocessor then provides no way to achieve what you ask. Variables do not exist or have values at compile time, so there is no way at compile time for the compiler to convert a variable name to the value it represents, much less to build a macro name out of it, much less to expand such a name to its replacement text.
You could, however, do it with numeric literals instead of a variable:
#define EXPAND(x) x
#define MACRO_VAL(n) EXPAND(C_ ## n ## _E)
printf("val %d: %d\n",n, 1, MACRO_VAL(1));
printf("val %d: %d\n",n, 2, MACRO_VAL(2));
printf("val %d: %d\n",n, 3, MACRO_VAL(3));
printf("val %d: %d\n",n, 4, MACRO_VAL(4));
If you try to expand that with an argument that does not produce the name of a defined macro or in-scope variable then that (almost surely) will produce a compile-time error for a reference to an undefined variable.
If run-time evaluation were acceptable after all, then you could write a function that does it (which you could wrap in a macro if you wanted):
#define MACRO_VAL(n) lookup_macro(n)
#define EXPAND(x) x
#define MACRO_CASE(i) case i: return EXPAND(C_ ## i ## _E)
int lookup_macro(int n) {
switch (n) {
MACRO_CASE(1);
MACRO_CASE(2);
MACRO_CASE(3);
MACRO_CASE(4);
default: return -1;
}
}
That will return -1 for an arithmetic argument that is not covered by the defined cases.
You could also consider a lookup table, possibly wrapped in a function, but that would require somewhat more code to provide a -1 result in the event of an argument that doesn't match any macro, especially if the macro numbers are not all consecutive or if the least of them is not known in advance.
Token pasting approaches are inappropriate as x is a variable name.
Here is a simplistic approach that will work as long as the macro argument is an expression without side effects:
#include <stdio.h>
#define C_1_E 4
#define C_2_E 2
#define C_3_E 0
#define C_4_E 420
// return the value of the macro C_x_E when x=1,2,3 or 4
#define MACRO_VAL(x) ((x) == 1 ? C_1_E : \
(x) == 2 ? C_2_E : \
(x) == 3 ? C_3_E : \
(x) == 4 ? C_4_E : -1)
int main() {
int n;
for (n = 1; n <= 4; n++)
printf("val %u: %u\n", n, MACRO_VAL(n));
return 0;
}

Using a string macro in another macro [duplicate]

This question already has answers here:
Convert a preprocessor token to a string
(6 answers)
Closed 4 years ago.
How can I use another macro in a string macro in C?
I have this:
#define MAX_OPERATION_COUNT 10
#define MSG_TOO_MANY_OPERATIONS "Too many operations! Only the first 10 were applied."
But I would like the second macro to use the value of the first one instead. In Java for instance, I would have had something like:
public static final int MAX_OPERATION_COUNT = 10;
public static final String MSG_TOO_MANY_OPERATIONS = "Too many operations! Only the first " + MAX_OPERATION_COUNT + " were applied.";
Is there a way to do something similar in C?
Edit:
The solution to this turned out to be very similar to the solution to this question, but I believe the problem itself is quite different and should be treated separately.
Usually it's easier to deal with printf-style formatting, kind of "Too many operations! Only the first %u were applied". However, if you really want to make string substitution you have to do it twice:
#define STR(x) STR2(x)
#define STR2(x) #x
#define MAX_OPERATION_COUNT 10
#define MSG_TOO_MANY_OPERATIONS "Too many operations! Only the first " STR(MAX_OPERATION_COUNT) " were applied."
You could "parametrise" the 2nd macro:
#define STRINGYFY(x) # x
#define PASTE_AS_STRING(s) STRINGYFY(s)
#define MAX_OPERATION_COUNT 10
#define MANY_OPERATION_COUNT 2
#define MSG_TOO_MANY_OPERATIONS_TEMPLATE(n) \
"Too many operations! Only the first" PASTE_AS_STRING(n) " were applied."
#define MSG_TOO_MANY_OPERATIONS \
MSG_TOO_MANY_OPERATIONS_TEMPLATE(MAX_OPERATION_COUNT)
#define MSG_MUCH_TOO_MANY_OPERATIONS \
MSG_TOO_MANY_OPERATIONS_TEMPLATE(MANY_OPERATION_COUNT)
(Just saw Matt's answer, being more or less the same, but elder. Still, I leave this in here, as I feel this approach uses a slightly different structure.)
You could as well define variables and use the macros just only to initialise them.
To define arrays do
const char MSG_TOO_MANY_OPERATIONS[] =
MSG_TOO_MANY_OPERATIONS_TEMPLATE(MAX_OPERATION_COUNT)
const char MSG_MUCH_TOO_MANY_OPERATIONS[] =
MSG_TOO_MANY_OPERATIONS_TEMPLATE(MANY_OPERATION_COUNT)
or to define pointers do
const char * MSG_TOO_MANY_OPERATIONS =
MSG_TOO_MANY_OPERATIONS_TEMPLATE(MAX_OPERATION_COUNT)
const char * MSG_MUCH_TOO_MANY_OPERATIONS =
MSG_TOO_MANY_OPERATIONS_TEMPLATE(MANY_OPERATION_COUNT)
You need to make number 10 a string.
Then, in order to "concatenate" the strings, you would just close the double quotes, insert your macro that represents the number, and then simply open double quotes again (no need for any plus operator like in Java/C++ for a string), to write the rest of the message, like this:
#define MAX_OPERATION_COUNT "10"
#define MSG_TOO_MANY_OPERATIONS "Too many operations! Only the first " MAX_OPERATION_COUNT " were applied."
You can do:
#define FOO "1"
#define BAR "How many foos? " FOO " foo.\n"
BAR now expands to
"How many foos? " "1" " foo.\n"
which is a valid string literal.

Passing macro arguments to macro function

How do I pass macro arguments along with another integer variable to a macro function?
#define SUM(X, Y, Z) X + Y + Z
#define FOO 1, 2
void print(int a, int b)
{
printf("Sum: %d", a + b);
}
int main(void)
{
// Normal function works as expected
print(FOO);
// Macro function not working
int a = 3;
printf("\nMacro Sum: %d", SUM(FOO, a));
return 0;
}
I expect the output to be:
Sum: 3
Macro Sum: 6
However, I get the following error:
main.c:18:41: error: macro "SUM" requires 3 arguments, but only 2 given
printf("\nMacro Sum: %d", SUM(FOO, a));
Macro arguments are not expanded when the macro call is parsed. After the macro call is parsed, each use of a macro parameter in the macro definition text is replaced with the macro-expanded argument, except for macro parameters used with the # or ## operations (stringify and token paste), which are replaced with the unexpanded text of the macro argument. Then the # and ## operations are performed, and then the entire macro body is scanned one more time.
The consequence is that SUM(FOO, a) is parsed aa having two arguments. Since the macro requires three, thatvwon't compile.
You can work around this, to some extent, by using one extra level of macro expansion:
#define CALL(macro, ...) macro(__VA_ARGS__)
printf("\nMacro Sum: %d", CALL(SUM, FOO, a));
Now the use of the __VA_ARGS__ parameter (which happens to be a varargs parameter, although that makes absolutely no difference to the expansion order) will be expanded before the replacement text is rescanned, so FOO will be invoked with three arguments.
By the way, outputting a newline character at the beginning of an output line is a bad habit which will get you into trouble someday. Output lines should have a newline at the end of the line:
printf("Macro Sum: %d\n", CALL(SUM, FOO, a));
There is nothing wrong with also writing a blank line beforehand by putting a newline at the beginning as well, but regardless, you should almost always terminate outpur lines with the \n. Otherwise:
The line might not be written immediately. Line-buffered output is not actually sent to the output device/file until a newline is sent.
If the program is a console app and it terminates without properly closing stdout, you will find yourself typing the next shell command at rhe end if the last output line. This tends to confuse line-editing input libraries like readline.
Single macro argument would work:
#define SUM(X, Y, Z) X + Y + Z
#define FOO1 (1)
#define FOO2 (2)
void print(int a, int b)
{
printf("Sum: %d", a + b);
}
int main(void)
{
// Normal function works as expected
print(FOO1,FOO2);
int a = 3;
printf("\nMacro Sum: %d", SUM(FOO1, FOO2, a));
return 0;
}
Output:
Sum: 3
Macro Sum: 6

Arduino: How to print out value of defined constants?

I'm writing code in the Arduino (1.6.5) environment. In my code, I want to be able to define a string value, then use it and also Serial.println() it to the serial console.
For example:
#define THEVAL 12345 // Define the value
...
v = v + THEVAL; // Use the value in code.
...
Serial.println("The value is: #THEVAL"); // Show the value to user (for debugging)
However, the compiler doesn't replace constants inside quoted strings. I also tried this (C++ stringification) which indicates that you place the constant outside the quoted string
#define THEVAL 12345
...
Serial.println("This is the value: " #THEVAL);
but that yields a "Stray # character" error in the compiler.
I'd appreciate any insight! Thanks!
EDIT: ODD BEHAVIOR
On testing I discovered the following:
(Note: the IP address uses commas to separate the octets because each octet is passed as a separate parameter to the EthernetServer.begin in a byte array (byte ip[] = { a, b, c, d })
#define IP_ADDRESS 192,168,1,1
#define IP_ADDRESS_STRING(a,b,c,d) xstr(a)"."xstr(b)"."xstr(c)"."xstr(d)
#define xstr(a) str(a)
#define str(a) #a
If I do the following, I get the error "IP_ADDRESS_STRING requires 4 arguments, but only one given"
debug("IP Address is: " IP_ADDRESS_STRING(IP_ADDRESS));
but if I do the following, I get the error "macro 'str' passed 4 arguments, but just takes 1"
debug("IP ADDRESS: " xstr(IP_ADDRESS));
But if I do this, it works:
String ipAddressString(int a, int b, int c, int d)
{
return String(a) + "." + String(b) + "." + String(c) + "." + String(d);
}
debug("IP Address is: " + ipAddressString(IP_ADDRESS));
I'm confused - why does one macro consider IP_ADDRESS to be a single argument, and the other macro sees it as 4 arguments, while the function works correctly: it sees 4 arguments?
#define XSTR(s) STR(s)
#define STR(s) #s
....
#define THEVAL 12345
....
Serial.println("The value of " STR(THEVAL) " is " XSTR(THEVAL));
This would output:
The value of THEVAL is 12345
#define XSTR(s) STR(s)
#define STR(s) #s
....
#define THEVAL 12345
....
Serial.println("The value of " STR(THEVAL) " is " XSTR(THEVAL));
But if THEVAL is set to 123,456 this fails.
With a slight modification it also works:
#define STR(...) #__VA_ARGS__
suggest
Serial.print("This is the value: ");
Serial.println( THEVAL );
or
int x = THEVAL;
Serial.print("This is the value: ");
Serial.println( x );
I'm confused - why does one macro consider IP_ADDRESS to be a single
argument, and the other macro sees it as 4 arguments, while the
function works correctly: it sees 4 arguments?
In the invocation IP_ADDRESS_STRING(IP_ADDRESS), there is obviously only the one argument IP_ADDRESS, and this is true irrespective of how IP_ADDRESS is defined, because argument substitution takes place only after the arguments for the invocation of a function-like macro have been identified (ISO/IEC 9899:201x).
In the invocation xstr(IP_ADDRESS) with the definition #define xstr(a) str(a), according to the above the parameter a is then replaced by the one argument IP_ADDRESS after that macro has been expanded (macro replaced), yielding str(192,168,1,1), so str is passed 4 arguments.
In contrast to the first case, in a function call ipAddressString(IP_ADDRESS) the substitution of IP_ADDRESS takes place not after, but before the arguments for the function call are identified.
You can use the macro by the same two-stage technique that's utilized with xstr()/str():
#define IP_ADDRESS 192,168,1,1
#define XIP_ADDRESS_STRING(abcd) IP_ADDRESS_STRING(abcd)
#define IP_ADDRESS_STRING(a,b,c,d) xstr(a)"."xstr(b)"."xstr(c)"."xstr(d)
#define xstr(a) str(a)
#define str(a) #a
debug("IP Address is: " XIP_ADDRESS_STRING(IP_ADDRESS));

Printing the variable identifier formed by the preprocessor

How to retrieve the text expanded from a macro?
#include <stdio.h>
#define paste(front, back) front ## back
int main()
{
int number1 = 23;
int number2 = 64;
printf("%d\n", paste(number, 2));
return 0;
}
The output is: 64 because the macro is expanded as:
printf("%d\n", number2);
How can I print the identifier number2 as string by using the defined paste macro. I need to know how to create the output:
number2: 64 by not writing directly "number2" I don't want this solution:
printf("number2: %d\n", paste(number, 2))
I want a dynamic solution. I try to concatenate by doing:
printf("%s: %d\n", paste(number, 2), paste(number, 2));
But it doesn't work because number2 returned by the paste macro is an the identifier an integer How can I retrieve as a text (string)?
Use the stringizing directive #:
#define stringize_impl(x) #x
#define stringize(x) stringize_impl(x)
printf("%s: %d\n", stringize(paste(number, 2)), paste(number, 2));
On a side note: why are you trying to do this? Surely there must be a better solution.
If you're compiling with GCC you can call it with gcc -E filename.c to see the expansion of macros.
EDIT:
You can also use the stringize preprocessor operator # that effectively puts double-quotes around the right-hand symbol.
#include <stdio.h>
#define T(...) #__VA_ARGS__
#define paste(f, b) _paste(f, b)
#define _paste(front, back) front##back
int main()
{
int n = 5;
printf("macro expands to: '%s'\n", T(paste(number, 2), paste(number, 2)));
printf("macro expands to: '%s'\n", T(paste(n, 2)));
return 0;
}
This code hopefully answers the question.
You need to expand the macro one more time to expand the paste in the stringize. In other words, for the paste macro inside the stringize macro to expand, the preprocessor has to pass over the file one more time. That's why you pass it through another macro defined later in the file.
I'm not 100% sure of ALL the rules of the preprocessor, but this seems to hold pretty well. For every macro you want to expand inside another macro, you need to do some magic to force the preprocessor to pass over the file again :) There exist different ways of achieving this to my knowledge, but this is one.
EDIT2:
Edited the code. I am getting this output, is this what you want?
morten#laptop:/tmp$ ./a.out
macro expands to: 'paste(number, 2), paste(number, 2)'
macro expands to: 'paste(n, 2)'

Resources