4

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 that Commented Jan 7, 2014 at 8:59
  • 1
    I'm pretty sure it can't be done. Commented Jan 7, 2014 at 9:43
  • 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). Commented Jan 7, 2014 at 9:52
  • 2
    Which problem are you trying to solve? Commented Jan 7, 2014 at 9:57
  • 2
    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. Commented Jan 7, 2014 at 10:15

6 Answers 6

5

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);
}
Sign up to request clarification or add additional context in comments.

4 Comments

@Dipto Yes, you are right, and that is why I said 'close but not exactly' :-)
Typo: not signal but single variable!
@leeduhem Feel free to rollback. Looks like my edit erased yours.
@remyabel 'single' is fine. But I think 'single expression' is more accurate, this macro also works for x+y.
1

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

Comments

1

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.

Comments

1

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

Comments

1

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

Comments

-1

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

1 Comment

It does not also display the name x (just its value) which is what the OP wants.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.