Writing a variadic macro that uses the names of the arguments passed

412 Views Asked by At

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

There are 6 best solutions below

0
On

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;
}
0
On

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
On

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

4
On

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);
}
0
On

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);
0
On

/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.