Is there a way to use C++ preprocessor stringification on variadic macro arguments?
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;
}
Is it possible to stringify a variadic macro?
Um, no. Preprocessing happens before the code is even compiled. It exists in a completely different universe from the executing program. One thing you can do, is run just the pre-processing step and examine the output (use the gcc switch -E
to print the preprocessor output).
About the most you can do is to redirect this to a file, and then read the file in the program.
On further thought, let me back-off from "no", and change it to "maybe". Take a look at this other answer of mine, that implements a "foreach" macro for variadic macros.
So, using APPLYXn
(and, implicitly, PPNARG
), you could apply a STR(x) #x
macro to the args like this (n.b. as written, APPLYXn
can handle up to 15 arguments):
#define X(x) #x
#define ERROR_MESSAGE(priority, fmt, ...) \
"do {MODULE_LOG(" X(priority) X(fmt) APPLYXn(__VA_ARGS__) ");} while(0)"
int main() {
printf("%s\n", ERROR_MESSAGE(3, "%d", 5) );
return 0;
}
gcc -E
produces
# 1 "strvar.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "strvar.c"
# 71 "strvar.c"
int main() {
printf("%s\n", "do {MODULE_LOG(" "3" "\"%d\"" "5" ");} while(0)" );
return 0;
}
And the compiler will concatenate all these string literals into one string.
Preprocessor: Concatenate string to each argument in __VA_ARGS__
Here's one scalable approach. First, some general utility macros:
#define EVAL(...) __VA_ARGS__
#define VARCOUNT(...) \
EVAL(VARCOUNT_I(__VA_ARGS__,9,8,7,6,5,4,3,2,1,))
#define VARCOUNT_I(_,_9,_8,_7,_6,_5,_4,_3,_2,X_,...) X_
#define GLUE(X,Y) GLUE_I(X,Y)
#define GLUE_I(X,Y) X##Y
#define FIRST(...) EVAL(FIRST_I(__VA_ARGS__,))
#define FIRST_I(X,...) X
#define TUPLE_TAIL(...) EVAL(TUPLE_TAIL_I(__VA_ARGS__))
#define TUPLE_TAIL_I(X,...) (__VA_ARGS__)
#define TRANSFORM(NAME_, ARGS_) (GLUE(TRANSFORM_,VARCOUNT ARGS_)(NAME_, ARGS_))
#define TRANSFORM_1(NAME_, ARGS_) NAME_ ARGS_
#define TRANSFORM_2(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_1(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_3(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_2(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_4(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_3(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_5(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_4(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_6(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_5(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_7(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_6(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_8(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_7(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_9(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_8(NAME_,TUPLE_TAIL ARGS_)
Semantically, VARCOUNT
counts arguments; GLUE
is a typical indirect paster; FIRST
extracts the first argument; EVAL
expands to its arguments (with intent to evaluate), and TUPLE_TAIL
returns the tail of a tuple (i.e., it discards the first argument).
TRANSFORM
here is the main idea; TRANSFORM(FOO,(X,Y,Z))
takes a tuple (X,Y,Z)
to (FOO(X),FOO(Y),FOO(Z))
.
This in place, here's the special purpose code:
#define Z_ARG(X) GLUE(X,Arg)
#define MAKE_INITIALIZER(...) { __VA_ARGS__ }
#define FIELD_DECLARATION(FNAME_, ...) \
GLUE(field_, FNAME_) = EVAL(MAKE_INITIALIZER TRANSFORM(Z_ARG, (__VA_ARGS__)));
Given the above, this should be readable, but just to explain... Z_ARG
pastes Arg
to an item; MAKE_INITIALIZER
transforms a preprocessor tuple to an initialization list; and FIELD_DECLARATION
is your macro. Note that EVAL
wraps the MAKE_INITIALIZER
/transformed tuple so it will actually call that macro.
Note: Moved EVAL
to the top and used it in a few more places, such that this will work in MSVC as well.
Demonstration, original code
Demonstration, current code
stringify arbitrary number of variables
I'm not sure that I understood what you're trying to do. The code below tokenizes, at compile time, __VA_ARGS__
. It does not check the syntax: it blindly replaces the white-space and commas with '\0'
, stores the start of identifiers in arg
and the number of arguments in argc
.
#include <iostream>
template < unsigned N > constexpr
unsigned countarg( const char( &s )[N], unsigned i = 0, unsigned c = 0 )
{
return
s[i] == '\0'
? i == 0
? 0
: c + 1
: s[i] == ','
? countarg( s, i + 1, c + 1 )
: countarg( s, i + 1, c );
}
template < unsigned N > constexpr
unsigned skipid( char( &s )[N], unsigned i = 0 )
{
return s[i] == '\0' || s[i] == ' ' || s[i] == '\t' || s[i] == ','
? i
: skipid( s, i + 1 );
}
template < unsigned N, unsigned M > constexpr
unsigned tokenize( char( &s )[N], const char*(&a)[M], unsigned i = 0, unsigned j = 0 )
{
return s[i] == '\0'
? i
: s[i] == ' ' || s[i] == '\t' || s[i] == ','
? ((s[i] = '\0'),
tokenize( s, a, ++i, j ))
: ((a[j] = s + i),
i = skipid( s, i ),
tokenize( s, a, i, ++j ));
}
#define TOKENIZEVA( ... ) char orig[] = #__VA_ARGS__; const unsigned argc = countarg(#__VA_ARGS__); const char* arg[argc]; tokenize( orig, arg );
#define PRINT( ... ) { TOKENIZEVA(__VA_ARGS__) for ( auto s : arg ) std::cout << s << std::endl; }
int main()
{
PRINT( first, second, third, fourth );
return 0;
}
Is it possible to stringify a C macro that contains a comma?
If your preprocessor supports variadic macros, __VA_ARGS__
will do the trick:
#define CAKE , something
#define MAKE_STRING(...) #__VA_ARGS__
#define STRING(x) MAKE_STRING(x)
#include <stdio.h>
int main()
{
printf("%s\n", STRING(CAKE) );
}
Related Topics
Why Is Std::Iterator Deprecated
Std::Map Default Value for Build-In Type
Why Does 'Int ;' Compile Fine in C, But Not in C++
How to Get the Mouse Position in a Console Program
How Are Local and Global Variables Initialized by Default
How to Use Dynamic Name for Variables in C++
Changing the Directory from Inside a C Program Under Windows Using System Command
Why Does Nvcc Fails to Compile a Cuda File with Boost::Spirit
Initializing Std::String from Char* Without Copy
Why Is "Operator Void" Not Invoked with Cast Syntax
What Exactly Is or Was the Purpose of C++ Function-Style Casts
Why Do String Literals (Char*) in C++ Have to Be Constants
Why Does My Program Hang When Opening a Mkfifo-Ed Pipe
How to Use C++ Preprocessor Stringification on Variadic MACro Arguments