1

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 ?

BobDeTunis
  • 165
  • 8
  • The compiler doesn't do any parsing of strings literals, except maybe replace escape sequences like `\n` with their actual character representations. – Some programmer dude Feb 01 '23 at 18:39
  • 3
    Adjacent string literals get automatically concatenated, e.g. `"foo " "%d"` becomes `"foo %d"`. You can possibly use this fact to accomplish what you want. – pmacfarlane Feb 01 '23 at 18:40

2 Answers2

4

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

alex01011
  • 1,670
  • 2
  • 5
  • 17
4

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.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Would there be a way to define my 8 and 3 variable as a 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. I tried several things but it keeps getting read as "%+-X..." which is plain wrong – BobDeTunis Feb 01 '23 at 19:12
  • I found a way by defining my X and Y as string macros: #define X "8" #define Y "3" and redefine my FORMAT macro to #define FORMAT "%+-"X"."Y"lf" but I find it a bit ugly... Is there a prettier way ? – BobDeTunis Feb 01 '23 at 19:20
  • @renaldo_kurifu: Note that there is a more elegant way to handle 'macros as arguments to the FORMAT macro' — see the updated answer. – Jonathan Leffler Feb 01 '23 at 21:21