BOOST_PP expand sequence in the empty sequence case - c++

Using BOOST_PP I can expand a macro into multiple comma separated values with an additional token, as can be seen in the below code.
However, it doesn't work in the no-argument case.
#define BOOST_PP_VARIADICS
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/variadic/to_seq.hpp>
#define ADD_TOKEN(r, token, i, e) \
BOOST_PP_COMMA_IF(i) token(e)
#define WRAP(...) \
BOOST_PP_SEQ_FOR_EACH_I(ADD_TOKEN, decltype, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))
#define MACRO(fmt, ...) \
Template<WRAP(__VA_ARGS__)>
MACRO("");
MACRO("", 0);
MACRO("", 0, 1);
The output when compiling with gcc -E main.cpp is
Template< decltype() >;
Template< decltype(0) >;
Template< decltype(0) , decltype(1) >;
How can I get calls to MACRO with no __VA_ARGS__ arguments expand to null?
That is, I'd like the output to be:
Template< >;
Template< decltype(0) >;
Template< decltype(0) , decltype(1) >;
How can I achieve this?

This answer uses a GNU extension. You stated in the comments that you're okay with that.
You can use BOOST_PP_TUPLE_SIZE((, ## __VA_ARGS__)): it will give you 1 if and only if the variadic arguments are omitted.
Templates are a bit tricky in that they can contain unparenthesised commas, which cause confusion when used in macro arguments. It takes a bit of work to write it in such a way that the WRAP macro is only expanded after BOOST_PP_IF has finished already:
#define MACRO(fmt, ...) \
Template< \
BOOST_PP_IF(BOOST_PP_EQUAL(BOOST_PP_TUPLE_SIZE((,##__VA_ARGS__)), 1), \
BOOST_PP_EXPAND, WRAP) (__VA_ARGS__) \
>
Note: I'm using BOOST_PP_EXPAND in the empty case, because BOOST_PP_EXPAND(__VA_ARGS__) will expand to nothing.

Related

Macro Take String with comma? [duplicate]

Say we have a macro like this
#define FOO(type,name) type name
Which we could use like
FOO(int, int_var);
But not always as simply as that:
FOO(std::map<int, int>, map_var); // error: macro "FOO" passed 3 arguments, but takes just 2
Of course we could do:
typedef std::map<int, int> map_int_int_t;
FOO(map_int_int_t, map_var); // OK
which is not very ergonomic. Plus type incompatibilities have to be dealt with. Any idea how to resolve this with macro ?
If you can't use parentheses and you don't like Mike's SINGLE_ARG solution, just define a COMMA:
#define COMMA ,
FOO(std::map<int COMMA int>, map_var);
This also helps if you want to stringify some of the macro arguments, as in
#include <cstdio>
#include <map>
#include <typeinfo>
#define STRV(...) #__VA_ARGS__
#define COMMA ,
#define FOO(type, bar) bar(STRV(type) \
" has typeid name \"%s\"", typeid(type).name())
int main()
{
FOO(std::map<int COMMA int>, std::printf);
}
which prints std::map<int , int> has typeid name "St3mapIiiSt4lessIiESaISt4pairIKiiEEE".
Because angle brackets can also represent (or occur in) the comparison operators <, >, <= and >=, macro expansion can't ignore commas inside angle brackets like it does within parentheses. (This is also a problem for square brackets and braces, even though those usually occur as balanced pairs.) You can enclose the macro argument in parentheses:
FOO((std::map<int, int>), map_var);
The problem is then that the parameter remains parenthesized inside the macro expansion, which prevents it being read as a type in most contexts.
A nice trick to workaround this is that in C++, you can extract a typename from a parenthesized type name using a function type:
template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define FOO(t,name) argument_type<void(t)>::type name
FOO((std::map<int, int>), map_var);
Because forming function types ignores extra parentheses, you can use this macro with or without parentheses where the type name doesn't include a comma:
FOO((int), int_var);
FOO(int, int_var2);
In C, of course, this isn't necessary because type names can't contain commas outside parentheses. So, for a cross-language macro you can write:
#ifdef __cplusplus__
template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define FOO(t,name) argument_type<void(t)>::type name
#else
#define FOO(t,name) t name
#endif
If your preprocessor supports variadic macros:
#define SINGLE_ARG(...) __VA_ARGS__
#define FOO(type,name) type name
FOO(SINGLE_ARG(std::map<int, int>), map_var);
Otherwise, it's a bit more tedious:
#define SINGLE_ARG2(A,B) A,B
#define SINGLE_ARG3(A,B,C) A,B,C
// as many as you'll need
FOO(SINGLE_ARG2(std::map<int, int>), map_var);
Just define FOO as
#define UNPACK( ... ) __VA_ARGS__
#define FOO( type, name ) UNPACK type name
Then invoke it always with parenthesis around the type argument, e.g.
FOO( (std::map<int, int>), map_var );
It can of course be a good idea to exemplify the invocations in a comment on the macro definition.
This is possible with P99:
#include "p99/p99.h"
#define FOO(...) P99_ALLBUTLAST(__VA_ARGS__) P99_LAST(__VA_ARGS__)
FOO()
The code above effectively strips only the last comma in the argument list. Check with clang -E (P99 requires a C99 compiler).
There are at least two ways to do this. First, you can define a macro that takes multiple arguments:
#define FOO2(type1, type2, name) type1, type2, name
if you do that you may find that you end up defining more macros to handle more arguments.
Second, you can put parentheses around the argument:
#define FOO(type, name) type name
F00((std::map<int, int>) map_var;
if you do that you may find that the extra parentheses screw up the syntax of the result.
The simple answer is that you can't. This is a side effect of the choice of <...> for template arguments; the < and > also appear in unbalanced contexts so the macro mechanism couldn't be extended to handle them like it handles parentheses. (Some of the committee members had argued for a different token, say (^...^), but they weren't able to convince the majority of the problems using <...>.)
You can actually include arguments containing commas without much fuss as long it's the last argument, by using __VA_ARGS__ in an amazingly hacky way:
#define EXAMPLE(_arg1_, _arg2_, ...) \
for (int i = _arg1_; i < _arg2_; i++){ __VA_ARGS__ }
since in the context of macros, __VA_ARGS__ simply represents the variadic arguments separated by commas, as in:
EXAMPLE(5, 3, int[2] = {i,i})
//macro sees: 5, 3, int[2] = {i, i}
because __VA_ARGS__ reinserts commas between the arguments, the macro will pass your code through just as intended.
I'm supposed to tell you not to do this, but you totally should because abusing macros is the best part of c++.

Variadic macro with zero-args for ISO C++

I wrote this variadic macro template<>-maker.
#define TNAME0()
#define TNAME1(_1) typename _1
#define TNAME2(_1,_2) typename _1, typename _2
#define TNAME3(_1,_2,_3) typename _1, typename _2, typename _3
#define TYPENAMES(_0,_1,_2,_3,n,...) TNAME##n
#define T(types...) template< TYPENAMES(,##types,3,2,1,0)(types) >
It works great with GNU C++ (e.g. T(),T(U),T(U,V),...), but fails with 0 args using ISO C++ standard (calls TYPES1 instead of TYPES0).
Is there a fix that works with both GNU and ISO c++?
This syntax for variadic macros isn't standard C++ at all:
#define T(types...)
Instead you can use an unnamed ..., which is referred to by __VA_ARGS__ in the expansion. Since C++20 you can also use __VA_OPT__(,) to include the initial , only if ... isn't empty:
#define T(...) template< TYPENAMES(__VA_OPT__(,) __VA_ARGS__,3,2,1,0)(__VA_ARGS__) >

Do macros not allow comma separated definitions in their arguments? [duplicate]

Say we have a macro like this
#define FOO(type,name) type name
Which we could use like
FOO(int, int_var);
But not always as simply as that:
FOO(std::map<int, int>, map_var); // error: macro "FOO" passed 3 arguments, but takes just 2
Of course we could do:
typedef std::map<int, int> map_int_int_t;
FOO(map_int_int_t, map_var); // OK
which is not very ergonomic. Plus type incompatibilities have to be dealt with. Any idea how to resolve this with macro ?
If you can't use parentheses and you don't like Mike's SINGLE_ARG solution, just define a COMMA:
#define COMMA ,
FOO(std::map<int COMMA int>, map_var);
This also helps if you want to stringify some of the macro arguments, as in
#include <cstdio>
#include <map>
#include <typeinfo>
#define STRV(...) #__VA_ARGS__
#define COMMA ,
#define FOO(type, bar) bar(STRV(type) \
" has typeid name \"%s\"", typeid(type).name())
int main()
{
FOO(std::map<int COMMA int>, std::printf);
}
which prints std::map<int , int> has typeid name "St3mapIiiSt4lessIiESaISt4pairIKiiEEE".
Because angle brackets can also represent (or occur in) the comparison operators <, >, <= and >=, macro expansion can't ignore commas inside angle brackets like it does within parentheses. (This is also a problem for square brackets and braces, even though those usually occur as balanced pairs.) You can enclose the macro argument in parentheses:
FOO((std::map<int, int>), map_var);
The problem is then that the parameter remains parenthesized inside the macro expansion, which prevents it being read as a type in most contexts.
A nice trick to workaround this is that in C++, you can extract a typename from a parenthesized type name using a function type:
template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define FOO(t,name) argument_type<void(t)>::type name
FOO((std::map<int, int>), map_var);
Because forming function types ignores extra parentheses, you can use this macro with or without parentheses where the type name doesn't include a comma:
FOO((int), int_var);
FOO(int, int_var2);
In C, of course, this isn't necessary because type names can't contain commas outside parentheses. So, for a cross-language macro you can write:
#ifdef __cplusplus__
template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define FOO(t,name) argument_type<void(t)>::type name
#else
#define FOO(t,name) t name
#endif
If your preprocessor supports variadic macros:
#define SINGLE_ARG(...) __VA_ARGS__
#define FOO(type,name) type name
FOO(SINGLE_ARG(std::map<int, int>), map_var);
Otherwise, it's a bit more tedious:
#define SINGLE_ARG2(A,B) A,B
#define SINGLE_ARG3(A,B,C) A,B,C
// as many as you'll need
FOO(SINGLE_ARG2(std::map<int, int>), map_var);
Just define FOO as
#define UNPACK( ... ) __VA_ARGS__
#define FOO( type, name ) UNPACK type name
Then invoke it always with parenthesis around the type argument, e.g.
FOO( (std::map<int, int>), map_var );
It can of course be a good idea to exemplify the invocations in a comment on the macro definition.
This is possible with P99:
#include "p99/p99.h"
#define FOO(...) P99_ALLBUTLAST(__VA_ARGS__) P99_LAST(__VA_ARGS__)
FOO()
The code above effectively strips only the last comma in the argument list. Check with clang -E (P99 requires a C99 compiler).
There are at least two ways to do this. First, you can define a macro that takes multiple arguments:
#define FOO2(type1, type2, name) type1, type2, name
if you do that you may find that you end up defining more macros to handle more arguments.
Second, you can put parentheses around the argument:
#define FOO(type, name) type name
F00((std::map<int, int>) map_var;
if you do that you may find that the extra parentheses screw up the syntax of the result.
The simple answer is that you can't. This is a side effect of the choice of <...> for template arguments; the < and > also appear in unbalanced contexts so the macro mechanism couldn't be extended to handle them like it handles parentheses. (Some of the committee members had argued for a different token, say (^...^), but they weren't able to convince the majority of the problems using <...>.)
You can actually include arguments containing commas without much fuss as long it's the last argument, by using __VA_ARGS__ in an amazingly hacky way:
#define EXAMPLE(_arg1_, _arg2_, ...) \
for (int i = _arg1_; i < _arg2_; i++){ __VA_ARGS__ }
since in the context of macros, __VA_ARGS__ simply represents the variadic arguments separated by commas, as in:
EXAMPLE(5, 3, int[2] = {i,i})
//macro sees: 5, 3, int[2] = {i, i}
because __VA_ARGS__ reinserts commas between the arguments, the macro will pass your code through just as intended.
I'm supposed to tell you not to do this, but you totally should because abusing macros is the best part of c++.

Is variadic macro subsitution for every argument possible?

I read quite a few questions on SO about variadic macros now, but it doesn't seem like anyone answered the most simple question:
#define IDENTITY(x) x
#define IDENTITY_FOR_ALL(...) ???
Is there a way to make IDENTITY_FOR_ALL expand to IDENTITY(X) for all arguments? Is it also possible for arbitrary numbers of arguments?
There is no such thing as a pack expansion for variadic macros as there is for variadic templates.
You could use Boost.Preprocessor (or its methods) though.
If you don't want any commas between the elements, use
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/variadic/to_seq.hpp>
#define ID_OP(_, func, elem) func(elem)
#define APPLY_TO_ALL(func, ...) \
BOOST_PP_SEQ_FOR_EACH( \
ID_OP, func, \
BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__) \
)
// example call:
#define SomeTransformation(x) #x // stringize the argument
APPLY_TO_ALL(SomeTransformation, 1, 2, 3) // expands to "1" "2" "3"
Demo.
With commas:
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/transform.hpp>
#include <boost/preprocessor/variadic/to_seq.hpp>
#define ID_OP(_, func, elem) func(elem)
#define APPLY_TO_ALL(func, ...) \
BOOST_PP_SEQ_ENUM( \
BOOST_PP_SEQ_TRANSFORM( \
ID_OP, func, \
BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__) \
))
// example call:
APPLY_TO_ALL(SomeTransformation, 1, 2, 3) // expands to "1", "2", "3"
Demo.
Check the preprocessor output with g++ -std=c++11 -E -P file.
Assuming you need a PP solution, you can use BOOST_PP_REPEAT:
//invoke IDENTITY_FOR_ALL_MACRO with each index and the given tuple
#define IDENTITY_FOR_ALL(...) \
BOOST_PP_REPEAT( \
BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), \
IDENTITY_FOR_ALL_MACRO, \
BOOST_PP_VARIADIC_TO_TUPLE(__VA_ARGS__) \
)
//use the index to access the right element of the passed tuple
#define IDENTITY_FOR_ALL_MACRO(z, n, data) \
IDENTITY(BOOST_PP_TUPLE_ELEM(n, data))
IDENTITY_FOR_ALL(abc, 123, "woopee")
//translated to abc 123 "woopee"
It would be decently straightforward to turn this into a more general macro that takes a macro to invoke and the list of single arguments to pass one by one if you need to do this with several different macros and not just IDENTITY.
I'm not 100% sure what you mean by an arbitrary number of arguments, but if you want to invoke IDENTITY with two arguments at a time instead of one, you can change the bottom macro to use BOOST_PP_MUL and BOOST_PP_INC to access the "2n"th and "2n+1"th elements of the tuple, and then invoke the macro only half as many times in the REPEAT call.

Comma in C/C++ macro

Say we have a macro like this
#define FOO(type,name) type name
Which we could use like
FOO(int, int_var);
But not always as simply as that:
FOO(std::map<int, int>, map_var); // error: macro "FOO" passed 3 arguments, but takes just 2
Of course we could do:
typedef std::map<int, int> map_int_int_t;
FOO(map_int_int_t, map_var); // OK
which is not very ergonomic. Plus type incompatibilities have to be dealt with. Any idea how to resolve this with macro ?
If you can't use parentheses and you don't like Mike's SINGLE_ARG solution, just define a COMMA:
#define COMMA ,
FOO(std::map<int COMMA int>, map_var);
This also helps if you want to stringify some of the macro arguments, as in
#include <cstdio>
#include <map>
#include <typeinfo>
#define STRV(...) #__VA_ARGS__
#define COMMA ,
#define FOO(type, bar) bar(STRV(type) \
" has typeid name \"%s\"", typeid(type).name())
int main()
{
FOO(std::map<int COMMA int>, std::printf);
}
which prints std::map<int , int> has typeid name "St3mapIiiSt4lessIiESaISt4pairIKiiEEE".
Because angle brackets can also represent (or occur in) the comparison operators <, >, <= and >=, macro expansion can't ignore commas inside angle brackets like it does within parentheses. (This is also a problem for square brackets and braces, even though those usually occur as balanced pairs.) You can enclose the macro argument in parentheses:
FOO((std::map<int, int>), map_var);
The problem is then that the parameter remains parenthesized inside the macro expansion, which prevents it being read as a type in most contexts.
A nice trick to workaround this is that in C++, you can extract a typename from a parenthesized type name using a function type:
template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define FOO(t,name) argument_type<void(t)>::type name
FOO((std::map<int, int>), map_var);
Because forming function types ignores extra parentheses, you can use this macro with or without parentheses where the type name doesn't include a comma:
FOO((int), int_var);
FOO(int, int_var2);
In C, of course, this isn't necessary because type names can't contain commas outside parentheses. So, for a cross-language macro you can write:
#ifdef __cplusplus__
template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define FOO(t,name) argument_type<void(t)>::type name
#else
#define FOO(t,name) t name
#endif
If your preprocessor supports variadic macros:
#define SINGLE_ARG(...) __VA_ARGS__
#define FOO(type,name) type name
FOO(SINGLE_ARG(std::map<int, int>), map_var);
Otherwise, it's a bit more tedious:
#define SINGLE_ARG2(A,B) A,B
#define SINGLE_ARG3(A,B,C) A,B,C
// as many as you'll need
FOO(SINGLE_ARG2(std::map<int, int>), map_var);
Just define FOO as
#define UNPACK( ... ) __VA_ARGS__
#define FOO( type, name ) UNPACK type name
Then invoke it always with parenthesis around the type argument, e.g.
FOO( (std::map<int, int>), map_var );
It can of course be a good idea to exemplify the invocations in a comment on the macro definition.
This is possible with P99:
#include "p99/p99.h"
#define FOO(...) P99_ALLBUTLAST(__VA_ARGS__) P99_LAST(__VA_ARGS__)
FOO()
The code above effectively strips only the last comma in the argument list. Check with clang -E (P99 requires a C99 compiler).
There are at least two ways to do this. First, you can define a macro that takes multiple arguments:
#define FOO2(type1, type2, name) type1, type2, name
if you do that you may find that you end up defining more macros to handle more arguments.
Second, you can put parentheses around the argument:
#define FOO(type, name) type name
F00((std::map<int, int>) map_var;
if you do that you may find that the extra parentheses screw up the syntax of the result.
The simple answer is that you can't. This is a side effect of the choice of <...> for template arguments; the < and > also appear in unbalanced contexts so the macro mechanism couldn't be extended to handle them like it handles parentheses. (Some of the committee members had argued for a different token, say (^...^), but they weren't able to convince the majority of the problems using <...>.)
You can actually include arguments containing commas without much fuss as long it's the last argument, by using __VA_ARGS__ in an amazingly hacky way:
#define EXAMPLE(_arg1_, _arg2_, ...) \
for (int i = _arg1_; i < _arg2_; i++){ __VA_ARGS__ }
since in the context of macros, __VA_ARGS__ simply represents the variadic arguments separated by commas, as in:
EXAMPLE(5, 3, int[2] = {i,i})
//macro sees: 5, 3, int[2] = {i, i}
because __VA_ARGS__ reinserts commas between the arguments, the macro will pass your code through just as intended.
I'm supposed to tell you not to do this, but you totally should because abusing macros is the best part of c++.