4
votes

I want to write a variadic macro that somehow knows the names of the arguments passed. For example:

The code:

int x = 2;
float f = 4.6;
char c = 'A';
char* str = "Bla bla";
PRINT("%d  %f %c  %s", x, f, c, str);     // calling the macro

shall produce the output

x=2 f=4.6 c=A str=Bla bla.

Hope someone knows the answer to that.

6
for the char print iss %c and not %s I updated the question with thatMOHAMED
I'm pretty sure it can't be done.JeremyP
To the OP: are you sure you want exactly that? It cannot be easily done! (But if you spend several days or weeks of work, you might get something close).Basile Starynkevitch
Which problem are you trying to solve?Mr Lister
It's impossible in the exact way of your example: a string literal like "%d %f %c %s" can't be picked apart, augmented in the middle or modified by the C preprocessor. It can only work for single parameters because string concatenation allows to prepend another string to a string literal.Jens

6 Answers

5
votes

Close but not exactly (only works for single expression) what the asker required:

#define PRINT(fmt, var) printf(#var " = " fmt, (var))

Here is an example:

#include <stdio.h>
#include <stdlib.h>

#define PRINT(fmt, var) printf(#var " = " fmt, (var))

int
main(int argc, char *argv[])
{
    int x = 2, y = 3;
    float f = 4.6;
    char c = 'A';
    char *str = "Bla bla";
    PRINT("%d\n", x);
    PRINT("%f\n", f);
    PRINT("%c\n", c);
    PRINT("%s\n", str);
    PRINT("%d\n", x+y);
    exit(EXIT_SUCCESS);
}
1
votes

I don't think you can achieve what you want.

But something like this might be yours:

#define PRINT(fmt, val) fprintf(stderr, "%s=" fmt, #val, val)

...

int x = 2;
float f = 4.6;
char c = 'A';
char* str = "Bla bla";
// calling the macro:
PRINT("%d  ", x);
PRINT("%f ", f);
PRINT("%c  ", c);
PRINT("%s\n", str);
1
votes

Read carefully the documentation of cpp. In particular, about macro arguments, stringification, concatenation, and variadic macros. It is well explained there.

You probably might not be able to achieve exactly what you want, because you need to split the format string.

Perhaps lower your goals (e.g. accept only one argument for PRINT, see this or that answers) or consider using a more powerful preprocessor like GPP.

You could also perhaps customize GCC (by adding your builtins) with e.g. MELT but that is probably not worth the weeks of efforts (for a newbie) required to do so.

1
votes

Slightly what you may want:

#include <stdio.h>

#define STRINGIFY(x) #x, (x)
#define FPRINTF(file, fmt, ...) fprintf(file, fmt, __VA_ARGS__)
#define PRINTF(fmt, ...) FPRINTF(stdout, fmt, __VA_ARGS__)

int main(void)
{
  int i = 42;
  char ch = 'a';
  char str[4] = "alk";

  PRINTF("%s=%s, %s=%c, %s=%d\n", 
    STRINGIFY(str), 
    STRINGIFY(ch), 
    STRINGIFY(i)
  );

  /* of just use printf directly: */
  printf("%s=%s, %s=%c, %s=%d\n", 
    STRINGIFY(str), 
    STRINGIFY(ch), 
    STRINGIFY(i)
  );

  return 0;
}
1
votes

/Puts on Indiana Jones` hat/

I may be a tad too late, but I`m here to say that this problem does have a proper(-ish) solution.

First, some prerequisite defines (explanation here):

#define L(c, ...) \
L4(c,1,0,,,,,,,,,,,,,##__VA_ARGS__) L4(c,0,1,,,,,,,,,##__VA_ARGS__) \
L4(c,0,2,,,,,        ##__VA_ARGS__) L4(c,0,3,        ##__VA_ARGS__)

#define L4(c, f, n, ...) \
L3(c,f,n##0,,,,__VA_ARGS__) L3(c,0,n##1,,,__VA_ARGS__) \
L3(c,0,n##2,,  __VA_ARGS__) L3(c,0,n##3,  __VA_ARGS__)

#define L3(...) L2(__VA_ARGS__, \
1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,  0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, )

#define L2(c, f, \
n00,n01,n02,n03, n04,n05,n06,n07, n08,n09,n0A,n0B, n0C,n0D,n0E,n0F, \
a00,a01,a02,a03, a04,a05,a06,a07, a08,a09,a0A,a0B, a0C,a0D,a0E,a0F, \
s, ...) L##s(c, f, n00, a00)

#define L1(c, f, n, a) c##f(n, a)
#define L0(c, f, n, a)

Then the code, which is actually an extension of @alk`s answer:

#include <stdio.h>

#define STRING1(n, a) #a, (a)
#define STRING0(n, a) , STRING1(n, a)
#define PRINTF(fmt, ...) printf(fmt, L(STRING, __VA_ARGS__))

int main(int argc, char *argv[]) {
    int i = 42;
    char ch = 'a';
    char str[4] = "alk";
    /** every var must be preceded with '%s' for its name to be shown **/
    PRINTF("%s=%s, %s=%c, %s=%d\n", str, ch, i);
    return 0;
}

This version is only suited for [0..16] arguments, but it can be easily extended to any argument count, especially to powers of 2. However, the more arguments it supports, the less elegant it looks.

P.S.: @BasileStarynkevitch has already provided all the right links to clarify how this works.

-1
votes

The next works for me in gcc 4.7 :

#include <stdio.h>
#define PRINT(...) fprintf (stderr, __VA_ARGS__)

int main()
{
int x = 2;
float f = 4.6;
char c = 'A';
char* str = "Bla bla";
PRINT("%d  %f %c  %s", x, f, c, str); // calling the macro
}

(please note that i have edited the macro invocation changing a %s for a %c)

Regards