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

450 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
Basile Starynkevitch 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
Jesús Casado 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
Lee Duhem 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
glglgl 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
alk 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
hidefromkgb 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.