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 ?
CodePudding user response:
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 writeFORMAT(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.
CodePudding user response:
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.