Is there a way to use C++ preprocessor stringification on variadic macro arguments?
Asked Answered
Y

3

12

My guess is the answer to this question is no, but it would be awesome if there was a way. To clarify, assume I have the following macro:

#define MY_VARIADIC_MACRO(X...) // Does some stuff here in the macro definition

What I would like to do is somehow perform stringification on all the variables of X before passing it to a variadic function; the keyword here is before. I realize there's no way to really access the individual arguments from within the macro definition, but is there a way to stringify all the arguments, with maybe something like the following?

#define MY_VARIADIC_MACRO(X...) some_variadic_function("some string", #X)
Yetty answered 10/5, 2011 at 23:37 Comment(0)
Y
12

Okay, I didn't mean to answer my own question here, but I've come up with a decent solution that is somewhat of a combination of Mark Wilkins answer and the example I gave in the question.

It is possible to stringify the entire set variadic set, which then includes the delimiting commas in the string. Here's a quick example:

#define MY_VARIADIC_MACRO(X...) printf(#X)

Using the above macro shows you that the entire set of arguments passed to the macro gets stringified.

Then you can then define a function to tokenize these arguments using the delimiting comma, thereby getting the set of tokenized strings by using the variadic macro:

#define MY_VARIADIC_MACRO(X...) tokenize_my_arguments(#X)

Then there's actually no longer the dependency of having the variadic macro call a variadic function and I can iterate nicely through my array of constant C strings rather than iterating through va_arg.

* New Stuff from Edit Follows *

Per Tim's comment, here's the details of the solution. Please forgive any errors since it was done in haste and I had to port from what I'm working on. Also, it's not meant to be copy/paste solution since it only outputs the stringification of the arguments to demonstrate POC, but should be sufficient enough to demonstrate the functionality.

Although this solution requires some run time computation, variadic macros often times call variadic functions and requires iterating through va_args, so the iteration takes place in finding the tokens, although a bit of performance is probably sacrificed. However, for maintainability, versatility, and ease of implementation, this seems to be the best option at the moment:

#define VARIADIC_STRINGIFY(_ARGUMENTS_TO_STRINGIFY...) Variadic_Stringification_Without_Variadic_Function(#_ARGUMENTS_TO_STRINGIFY)

void Variadic_Stringification_Without_Variadic_Function (const char* _stringified_arguments)
{
    strcpy(converted_arguments, _stringified_arguments);

    for(char* token = strtok(converted_arguments, ","); token != 0x0; token = strtok(0x0, ","))
        std::cout << token << std::endl;
}
Yetty answered 11/5, 2011 at 0:2 Comment(5)
Also, another nice feature of this is that I can easily find the number of arguments without having to have some type of information passed through the macro to determine where the arguments in the list ends.Yetty
Interesting, but before you mark this as a solution, post your actual code that solved this. I'm sure it will come in handy for others.Mella
@Mella Will do, I've already tested a simple case and will make it a nicer example... have some deadlines and will post either tomorrow (5/11/2011) or the following day.Yetty
@Mella Updated with the details.Yetty
+1. It seems a good solution. I did not realize C99 supported variadic macros. Nice. I am using a Microsoft compiler that doesn't support it (at least not in that form).Ariminum
S
22

You can use various recursive macro techniques to do things with variadic macros. For example, you can define a NUM_ARGS macro that counts the number of arguments to a variadic macro:

#define _NUM_ARGS(X100, X99, X98, X97, X96, X95, X94, X93, X92, X91, X90, X89, X88, X87, X86, X85, X84, X83, X82, X81, X80, X79, X78, X77, X76, X75, X74, X73, X72, X71, X70, X69, X68, X67, X66, X65, X64, X63, X62, X61, X60, X59, X58, X57, X56, X55, X54, X53, X52, X51, X50, X49, X48, X47, X46, X45, X44, X43, X42, X41, X40, X39, X38, X37, X36, X35, X34, X33, X32, X31, X30, X29, X28, X27, X26, X25, X24, X23, X22, X21, X20, X19, X18, X17, X16, X15, X14, X13, X12, X11, X10, X9, X8, X7, X6, X5, X4, X3, X2, X1, N, ...)   N

#define NUM_ARGS(...) _NUM_ARGS(__VA_ARGS__, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)

Then with that, you can write a FOREACH macro that expands another macro for each element of a list:

#define EXPAND(X)       X
#define FIRSTARG(X, ...)    (X)
#define RESTARGS(X, ...)    (__VA_ARGS__)
#define FOREACH(MACRO, LIST)    FOREACH_(NUM_ARGS LIST, MACRO, LIST)
#define FOREACH_(N, M, LIST)    FOREACH__(N, M, LIST)
#define FOREACH__(N, M, LIST)   FOREACH_##N(M, LIST)
#define FOREACH_1(M, LIST)  M LIST
#define FOREACH_2(M, LIST)  EXPAND(M FIRSTARG LIST) FOREACH_1(M, RESTARGS LIST)
#define FOREACH_3(M, LIST)  EXPAND(M FIRSTARG LIST) FOREACH_2(M, RESTARGS LIST)
        :

Which will in turn allow you to to define your macro that stringifies each of its arguments:

#define STRINGIFY(X)    #X
#define MY_VARIADIC_MACRO(...)    FOREACH(STRINGIFY, (__VA_ARGS__))
Sirreverence answered 11/5, 2011 at 1:39 Comment(3)
I'm aware of those recursive techniques, but as seen from above they are tedious and they also have the unfortunate restriction that you're limited to the number of args you account for at compile time. I'll update my answer soon with the details of that solution that is not as tedious to implement and is not restricted to a predefined max which is easier to maintain. It might be the best solution for all cases, but for what I need it is a better answer than above. Thanks for the input though!Yetty
Is there a reason behind the colon at the end of the second code block?Lauricelaurie
That's a vertical elipsis, eliding the many more FOREACH_ macros (possibly up to FOREACH_100) you need to allow loops that large.Sirreverence
Y
12

Okay, I didn't mean to answer my own question here, but I've come up with a decent solution that is somewhat of a combination of Mark Wilkins answer and the example I gave in the question.

It is possible to stringify the entire set variadic set, which then includes the delimiting commas in the string. Here's a quick example:

#define MY_VARIADIC_MACRO(X...) printf(#X)

Using the above macro shows you that the entire set of arguments passed to the macro gets stringified.

Then you can then define a function to tokenize these arguments using the delimiting comma, thereby getting the set of tokenized strings by using the variadic macro:

#define MY_VARIADIC_MACRO(X...) tokenize_my_arguments(#X)

Then there's actually no longer the dependency of having the variadic macro call a variadic function and I can iterate nicely through my array of constant C strings rather than iterating through va_arg.

* New Stuff from Edit Follows *

Per Tim's comment, here's the details of the solution. Please forgive any errors since it was done in haste and I had to port from what I'm working on. Also, it's not meant to be copy/paste solution since it only outputs the stringification of the arguments to demonstrate POC, but should be sufficient enough to demonstrate the functionality.

Although this solution requires some run time computation, variadic macros often times call variadic functions and requires iterating through va_args, so the iteration takes place in finding the tokens, although a bit of performance is probably sacrificed. However, for maintainability, versatility, and ease of implementation, this seems to be the best option at the moment:

#define VARIADIC_STRINGIFY(_ARGUMENTS_TO_STRINGIFY...) Variadic_Stringification_Without_Variadic_Function(#_ARGUMENTS_TO_STRINGIFY)

void Variadic_Stringification_Without_Variadic_Function (const char* _stringified_arguments)
{
    strcpy(converted_arguments, _stringified_arguments);

    for(char* token = strtok(converted_arguments, ","); token != 0x0; token = strtok(0x0, ","))
        std::cout << token << std::endl;
}
Yetty answered 11/5, 2011 at 0:2 Comment(5)
Also, another nice feature of this is that I can easily find the number of arguments without having to have some type of information passed through the macro to determine where the arguments in the list ends.Yetty
Interesting, but before you mark this as a solution, post your actual code that solved this. I'm sure it will come in handy for others.Mella
@Mella Will do, I've already tested a simple case and will make it a nicer example... have some deadlines and will post either tomorrow (5/11/2011) or the following day.Yetty
@Mella Updated with the details.Yetty
+1. It seems a good solution. I did not realize C99 supported variadic macros. Nice. I am using a Microsoft compiler that doesn't support it (at least not in that form).Ariminum
A
3

This is probably is not very close to what you are wanting, but something like the following might get you there:

#define MY_VARIADIC_MACRO(X) printf( "%s\n", #X )

Then use it like the following. Enclose the parameters in parens so that it appears as one parameter to the macro.

MY_VARIADIC_MACRO(( var1, var2, "string" ));

You could then have the macro call some function that strips off the outer parens or possibly parses the given string.

Ariminum answered 10/5, 2011 at 23:48 Comment(3)
That's almost there, but the idea is relieve the need to enclose the parenthesis... darn return adds the comment to quick, need to remember shift-return. I think the above is very close to a solution I'm working towards though. I'll vote up this answer since it is almost there, but can't mark it as the main answer since it doesn't fully satisfy the requirements.Yetty
@Zach: I guessed as much. I would love to see how that could be done. My understanding is that it is not possible, but I would like to be shown differently.Ariminum
I posted the answer above that I'm going to go with, pretty stoked about it, solves A LOT of the issues I was facing.Yetty

© 2022 - 2024 — McMap. All rights reserved.