Variadic macro without arguments - c++

I am using some logging macros, which are supposed to print out the information provided by the __PRETTY_FUNCTION__ macro and if needed name and value of up to two arguments.
A simplified version of my code looks like
template<typename Value1, typename Value2>
void Log(std::string const& function,
std::string const& variable_1 = "", Value1 value_1 = Value1(0),
std::string const& variable_2 = "", Value2 value_2 = Value2(0)) {
std::cout << function << " "
<< variable_1 << " " << value_1 << " "
<< variable_2 << " " << value_2 << std::endl;
}
#define LOG0() Log(__PRETTY_FUNCTION__)
#define VARIABLE(value) #value, value
#define LOG1(value) Log(__PRETTY_FUNCTION__, VARIABLE(value))
#define LOG2(value, value1) Log(__PRETTY_FUNCTION__, VARIABLE(value), VARIABLE(value1))
#define LOG(arg0, arg1, arg2, arg, ...) arg
#define CHOOSE(...) LOG(,##__VA_ARGS__, LOG2, LOG1, LOG0)
#define Debug(...) CHOOSE(__VA_ARGS__)(__VA_ARGS__)
I can use these macros like
Debug();
int x = 0;
Debug(x);
int y = 1;
Debug(x, y);
When I compile this code with clang I get a nice output containing class and function information as well as name and value of the variables.
But I also get the warning that standard compliant code is not allowed to have zero variadic arguments.
warning: token pasting of ',' and __VA_ARGS__ is a GNU extension [-Wgnu-zero-variadic-macro-arguments]
#define CHOOSE(...) LOG(,##__VA_ARGS__, LOG2, LOG1, LOG0)
^
warning: must specify at least one argument for '...' parameter of variadic macro [-Wgnu-zero-variadic-macro-arguments]
Debug();
Gcc on the other hand fails to compile with
error: expected primary-expression before ‘)’ token
#define LOG1(value) Log(__PRETTY_FUNCTION__, VARIABLE(value))
^
Debug();
Obviously it is dangerous to work with zero variadic arguments.
Is there any way that I can turn this code into standard compliant code without removing the convenience of having just one macro that takes zero to two arguments?
If this is not possible, is there a way to make also gcc compile this code?

The hard part of this is distinguishing between Debug() and Debug(x). In both cases, you are technically passing a single argument to the macro Debug. In the first case, the token sequence of that argument is empty, and in the second case it contains a single token. These cases can be distinguished with a trick due to to Jens Gustedt.
Here's the trick:
#define COMMA_IF_PARENS(...) ,
Observe that COMMA_IF_PARENS X produces a comma if X starts with (...), and otherwise expands to a token sequence containing no additional (top-level) commas. Likewise, COMMA_IF_PARENS X () produces a comma if X is empty or starts with (...) and otherwise expands to a token sequence containing no additional (top-level) commas. (In each case, the token sequence also contains all the top-level commas from X itself.)
We can use that trick like this:
#define CHOOSE(...) \
LOG(__VA_ARGS__ \
COMMA_IF_PARENS __VA_ARGS__ \
COMMA_IF_PARENS __VA_ARGS__ (), \
CHOICES)
Note that:
COMMA_IF_PARENS __VA_ARGS__ produces the number of commas in __VA_ARGS__ plus 1 if __VA_ARGS__ starts with (...).
COMMA_IF_PARENS __VA_ARGS__ () produces the number of commas in __VA_ARGS__ plus 1 if __VA_ARGS__ is empty or starts with (...). (Note that this can fail if __VA_ARGS__ ends in the name of a function-like macro, and we don't address that potential problem here.)
Let c be the number of commas in __VA_ARGS__, p be 1 if __VA_ARGS__ starts with (...) and 0 otherwise, and e be 1 if __VA_ARGS__ is empty and 0 otherwise.
The number of macro arguments produced prior to CHOICES is 3 c + 2 p + e. Taken modulo 3, the number of commas is 0 or 2 for a normal argument, and 1 if we have an empty list of arguments.
This gives us 6 cases we care about:
#define CHOICES LOG2, impossible, LOG2, LOG1, LOG0, LOG1
#define LOG(a0, a1, a2, a3, a4, a5, arg, ...) arg
However, this doesn't quite work, because we need to delay expanding the LOG(...) macro invocation until after we expand the COMMA_IF_PARENS machinery. One way to do that is:
#define LPAREN (
#define EXPAND(...) __VA_ARGS__
#define CHOOSE(...) EXPAND(LOG LPAREN COMMA_IF_PARENS [...]))
We also should add another comma to the end of CHOICES so that we always have a (possibly empty) argument corresponding to the ... parameter of LOG.
Putting it all together, we get this:
#define COMMA_IF_PARENS(...) ,
#define LPAREN (
#define EXPAND(...) __VA_ARGS__
#define CHOOSE(...) \
EXPAND(LOG LPAREN \
__VA_ARGS__ COMMA_IF_PARENS __VA_ARGS__ COMMA_IF_PARENS __VA_ARGS__ (), \
LOG2, impossible, LOG2, LOG1, LOG0, LOG1, ))
#define LOG(a0, a1, a2, a3, a4, a5, arg, ...) arg
with everything else unchanged from your code. (This can be generalized much further, but the above is sufficient to demonstrate the technique.)

Related

pass dot as parameter to C macro

I've read different related answers on StackOverflow but none of them gives me the answer to this question.
I need to do concatenation of entities (not necessarily strings) passed to the macro with a separator between them (in my case dot). Here is my code:
#define STR(x) #x
#define XSTR(x) STR(x)
#define CONC_(a, b) a ## b
#define CONC(a, b) CONC_(a, b)
#define CONC3(_1, _2, _3, S) CONC2(CONC2(_1, _2, S), _3, S)
#define CONC2(_1, _2, S) CONC(_1, CONC(S, _2))
#define JOIN_UNDERSCORE(_1, _2, _3) XSTR(CONC3(_1, _2, _3, _))
#define JOIN_DOT(_1, _2, _3) XSTR(CONC3(_1, _2, _3, .)) // here I pass dot
Here is how you can use it
std::string underscore = JOIN_UNDERSCORE(one, two, three);
std::string dot = JOIN_DOT(one, two, three); // this one doesn't compile
I could use another technique to achieve this. Like:
#define APPEND_DOT(x) "." STR(x)
#define CONCSTR3(_1, _2, _3) CONCSTR2(_1, _2) APPEND_DOT(_3)
#define CONCSTR2(_1, _2) STR(_1) APPEND_DOT(_2)
#define CONCSTR1(_1) STR(_1)
#define JOIN_STR_DOT(_1, _2, _3) CONCSTR3(_1, _2, _3)
std::string dot_str = JOIN_STR_DOT(one, two, three);
and this would work with dot. But the question is how to make it generic by passing the separator.
Here is a link to play around https://godbolt.org/z/MDxsJS
The result of the ## operator must be a valid pre-processor token. one_ is valid, one. is not. That's why underscore works but dot doesn't.
If the intention is to create a string, you can utilize pre-processor string literal concatenation rathern than the token paste operator:
#define STR(x) #x
#define JOIN_UNDERSCORE(_1, _2, _3) STR(_1) "_" STR(_2) "_" STR(_3)
which can be written generically as:
#define STR(x) #x
#define JOIN_SYMBOL(_1, _2, _3, symbol) STR(_1) STR(symbol) STR(_2) STR(symbol) STR(_3)
...
const char* underscore = JOIN_SYMBOL(one, two, three, _);
const char* dot = JOIN_SYMBOL(one, two, three, .);
This can't be used to create variable names, but then dots can't be used to create variable names either.
It is possible to do this by skipping the concatenation entirely, but what good that would do, I have no idea:
#define DUMB_THING(_1, _2, _3, thingie) _1 thingie _2 thingie _3
#include <stdio.h>
int main (void)
{
typedef struct { int idea; } stupid_t;
typedef struct { stupid_t stupid; } really_t;
really_t really;
DUMB_THING(really, stupid, idea, .) = 42;
printf("%d", really.stupid.idea);
}
## allows to concatenate valid preprocessing tokens forming a valid preprocessing token.
In your case, you could use
std::string pi = CONCSTR2(3, 141592);
as 3, 3. and 3.141592 are all valid preprocessing tokens. But you can't use
std::string dot = JOIN_DOT(one, two, three);
as .two (which is the first concatenation you are doing) is not a valid preprocessing token. When your final goal is to form a string, better use the string literal concatenation available by juxtaposition. When your goal is an identifier, you can use your first method.

Using boost preprocessor to stringize a tuple

I am beginner in using boost preprocessor. I want to use this library to generate a name from a tuple, for example I have a tuple like this (float, string, int, bool) and I want to construct something like this "Foo<float, string, int, bool>". I thought that it must be easy by using BOOST_PP_STRINGIZE, BOOST_PP_CAT and BOOST_PP_REPEAT but I unfortunately could not find a way to generate the string I wanted.
Please give me a suggestion about creating this string. Foo is a class name and tuple is generated automatically during preprocess.
You can use BOOST_PP_TUPLE_ENUM to get a comma-separated expansion of the tuple elements. You can then use #__VA_ARGS__ to stringize the resulting list. See it live:
#define STRINGIZE_ALL_I(...) #__VA_ARGS__
#define STRINGIZE_ALL(...) STRINGIZE_ALL_I(__VA_ARGS__)
#define MAKE_STRING(tuple) STRINGIZE_ALL(Foo<BOOST_PP_TUPLE_ENUM(tuple)>)
// "Foo<float, string, int, bool>"
MAKE_STRING((float, string, int, bool))
STRINGIZE_ALL_I exists for the same reason you have an extra layer in STRINGIZE and CONCAT - to evaluate macros before stringizing. In this case, you would get a string containing BOOST_PP_TUPLE_ENUM((…)) if you neglected to have two layers.
Note that STRINGIZE_ALL is called with the argument list Foo<float, string, int, bool>. That's four arguments, not one.
If variadic macros are not available (e.g., C++98), you can take advantage of the fact that "abc" "def" will be turned into "abcdef" by the compiler. See it live:
#define STRINGIZE_ALL_MACRO(s, state, x) state "," BOOST_PP_STRINGIZE(x)
#define STRINGIZE_ALL(seq) \
BOOST_PP_SEQ_FOLD_LEFT( \
STRINGIZE_ALL_MACRO, \
BOOST_PP_STRINGIZE(BOOST_PP_SEQ_HEAD(seq)), \
BOOST_PP_SEQ_TAIL(seq) \
)
#define MAKE_STRING(size, tuple) \
STRINGIZE_ALL( \
BOOST_PP_TUPLE_TO_SEQ( \
size, \
(Foo<BOOST_PP_TUPLE_ENUM(size, tuple)>) \
) \
)
// "Foo<float" "," "string" "," "int" "," "bool>"
MAKE_STRING(4, (float, string, int, bool))
For this version, your autogenerated tuple must generate a size as well. It's entirely possible to generate a tuple (size, (elems)) that you can use with this macro.

How to remove the enclosing parentheses with macro?

No comma is allowed in a macro argument because it will be treated as more than one arguments and the preprocessing will be wrong. However, we can parenthesize the argument to let preprocessor treat it as one argument. Is there a macro or other techniques which can remove the enclosing parentheses?
For example, if I define a macro like
#define MY_MACRO(a, b) ...
and use it like
MY_MACRO( A<int, double>, text );
will be wrong. use it like
MY_MACRO( (A<int, double>), text)
with a macro or technique to remove the parentheses will be fine. Boost provides BOOST_IDENTITY_TYPE macro for only types but not general cases
#define ESC(...) __VA_ARGS__
then
MY_MACRO( ESC(A<int, double>), text );
might do what you want.
This macro trick is similar to Yakk's solution but removes the need to explicitly pass in another macro as a parameter.
#include <stdio.h>
#define _Args(...) __VA_ARGS__
#define STRIP_PARENS(X) X
#define PASS_PARAMETERS(X) STRIP_PARENS( _Args X )
int main()
{
printf("without macro %d %d %d %d %d %d\n", (5,6,7,8,9,10) ); // This actually compiles, but it's WRONG
printf("with macro %d %d %d %d %d %d\n", PASS_PARAMETERS((5,6,7,8,9,10)) ); //Parameter "pack" enclosed in parenthesis
return 0;
}
Of course you could get creative by making the PASS_PARAMETERS macro into a variadic macro and pass in multiple parameter packs.
Only maybe removing parentheses:
If you need to strip one layer of parenthesis, but only if there are parenthesis to strip, this longer set of macros does the trick:
#define DEPAREN(X) ESC(ISH X)
#define ISH(...) ISH __VA_ARGS__
#define ESC(...) ESC_(__VA_ARGS__)
#define ESC_(...) VAN ## __VA_ARGS__
#define VANISH
This may be needed if you want to use MY_MACRO for different sets of datatypes:
#define MY_MACRO(a, b) DEPAREN(a), b
MY_MACRO( ({x, y, z}), text )
//> {x,y,z}, text
MY_MACRO( singlearg, text )
//> singlearg, text
How it works:
We start with DEPAREN(X) ESC(ISH X). If X has parenthesis, we get ESC(ISH(X)). If X does not have parenthesis, we get ESC(ISH X).
We then expand ESC(...) into ESC_(__VA_ARGS__), which expands the interior.
ISH(...) turns into ISH __VA_ARGS__, which strips one layer of parentheses from X. Now, regardless of whether or not X originally had parenthesis, we have ESC_(ISH X).
We now need to get rid of ISH. However, because we already defined ISH(...), we can't also define it as #define ISH . That's why we concatenate it with another token (VAN) to get VANISH X.
VANISH is defined as , so we are finally left with X, sans parentheses.
A simple hack could be to use variadic macros:
#define MY_MACRO(a, b...) ...
Then you can use it like:
MY_MACRO(text, A<int, double>)
The comma in the second argument is still interpreted as the argument separator (meaning the macro is actually called with three arguments), but it's expanded inside the macro, making the behavior the same. The variadic argument has to be last in the macro, however.

C/C++ macro string concatenation

#define STR1 "s"
#define STR2 "1"
#define STR3 STR1 ## STR2
Is it possible to concatenate STR1 and STR2, to "s1"?
You can do this by passing args to another Macro function. But is there a direct way?
If they're both strings you can just do:
#define STR3 STR1 STR2
This then expands to:
#define STR3 "s" "1"
and in the C language, separating two strings with space as in "s" "1" is exactly equivalent to having a single string "s1".
You don't need that sort of solution for string literals, since they are concatenated at the language level, and it wouldn't work anyway because "s""1" isn't a valid preprocessor token.
[Edit: In response to the incorrect "Just for the record" comment below that unfortunately received several upvotes, I will reiterate the statement above and observe that the program fragment
#define PPCAT_NX(A, B) A ## B
PPCAT_NX("s", "1")
produces this error message from the preprocessing phase of gcc: error: pasting ""s"" and ""1"" does not give a valid preprocessing token
]
However, for general token pasting, try this:
/*
* Concatenate preprocessor tokens A and B without expanding macro definitions
* (however, if invoked from a macro, macro arguments are expanded).
*/
#define PPCAT_NX(A, B) A ## B
/*
* Concatenate preprocessor tokens A and B after macro-expanding them.
*/
#define PPCAT(A, B) PPCAT_NX(A, B)
Then, e.g., both PPCAT_NX(s, 1) and PPCAT(s, 1) produce the identifier s1, unless s is defined as a macro, in which case PPCAT(s, 1) produces <macro value of s>1.
Continuing on the theme are these macros:
/*
* Turn A into a string literal without expanding macro definitions
* (however, if invoked from a macro, macro arguments are expanded).
*/
#define STRINGIZE_NX(A) #A
/*
* Turn A into a string literal after macro-expanding it.
*/
#define STRINGIZE(A) STRINGIZE_NX(A)
Then,
#define T1 s
#define T2 1
STRINGIZE(PPCAT(T1, T2)) // produces "s1"
By contrast,
STRINGIZE(PPCAT_NX(T1, T2)) // produces "T1T2"
STRINGIZE_NX(PPCAT_NX(T1, T2)) // produces "PPCAT_NX(T1, T2)"
#define T1T2 visit the zoo
STRINGIZE(PPCAT_NX(T1, T2)) // produces "visit the zoo"
STRINGIZE_NX(PPCAT(T1, T2)) // produces "PPCAT(T1, T2)"
Hint: The STRINGIZE macro above is cool, but if you make a mistake and its argument isn't a macro - you had a typo in the name, or forgot to #include the header file - then the compiler will happily put the purported macro name into the string with no error.
If you intend that the argument to STRINGIZE is always a macro with a normal C value, then
#define STRINGIZE(A) ((A),STRINGIZE_NX(A))
will expand it once and check it for validity, discard that, and then expand it again into a string.
It took me a while to figure out why STRINGIZE(ENOENT) was ending up as "ENOENT" instead of "2"... I hadn't included errno.h.

C++ Macros: manipulating a parameter (specific example)

I need to replace
GET("any_name")
with
String str_any_name = getFunction("any_name");
The hard part is how to trim off the quote marks. Possible? Any ideas?
How about:
#define UNSAFE_GET(X) String str_##X = getFunction(#X);
Or, to safe guard against nested macro issues:
#define STRINGIFY2(x) #x
#define STRINGIFY(x) STRINGIFY2(x)
#define PASTE2(a, b) a##b
#define PASTE(a, b) PASTE2(a, b)
#define SAFE_GET(X) String PASTE(str_, X) = getFunction(STRINGIFY(X));
Usage:
SAFE_GET(foo)
And this is what is compiled:
String str_foo = getFunction("foo");
Key points:
Use ## to combine macro parameters into a single token (token => variable name, etc)
And # to stringify a macro parameter (very useful when doing "reflection" in C/C++)
Use a prefix for your macros, since they are all in the same "namespace" and you don't want collisions with any other code. (I chose MLV based on your user name)
The wrapper macros help if you nest macros, i.e. call MLV_GET from another macro with other merged/stringized parameters (as per the comment below, thanks!).
One approach is not to quote the name when you call the macro:
#include <stdio.h>
#define GET( name ) \
int int##name = getFunction( #name ); \
int getFunction( char * name ) {
printf( "name is %s\n", name );
return 42;
}
int main() {
GET( foobar );
}
In answer to your question, no, you can't "strip off" the quotes in C++. But as other answers demonstrate, you can "add them on." Since you will always be working with a string literal anyway (right?), you should be able to switch to the new method.