Macro Take String with comma? [duplicate] - c++

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

Related

Propagate __VA_ARGS__ macro types inside template methods

I was wondering if there was a clean method to extract types from __VA_ARGS__ and use them to fill template method or structure definitions?
Thanks in advance
I have the following problem:
my macro definition:
#define MY_MACRO(topic, callback_name, message_type, ...) \
Callback_data<message_type, ##__VA_ARGS__> callback_name##_data(callback_name, topic); \
Handler::add_callback<message_type, ##__VA_ARGS__>(callback_name##_data)
my macro call:
MY_MACRO("/my_topic_1", callback_1, bool, int a, int b);
what I'd like my macro writes:
Callback_data<bool, int, int> callback_1_data(callback_1, "/my_topic_1");
Handler::add_callback<bool, int, int>(callback_1_data);
what my macro actually writes:
Callback_data<bool,int a, int b> callback_1_data(callback_1, "/my_topic_1");
Handler::add_callback<bool,int a, int b>(callback_1_data);
//! error on both lines, template filling expecting only type definitions !
PS : If anyone knows of a modern way to do the same thing without using macros, I'm interested too!

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

Passing a template which requires a comma to a single-argument macro

I have some code that essentially condenses down to
#define FOO(a)
FOO(std::map<int, int>);
But it emits a compile error (too many actual parameters for macro FOO).
Obviously the preprocessor is thinking that I've supplied std::map<int and int> as arguments.
Is there a way round this? The preprocessor will not treat a quoted string with a comma in this way.
The comma is being treated as a macro argument seperator, it does not do this with commas within parenthesizes.
If you are using Boost, they provide BOOST_PP_COMMA:
#include <boost/preprocessor/punctuation/comma.hpp>
#define FOO(a)
FOO(std::map<int BOOST_PP_COMMA int>);
You can also define your own:
#define COMMA ,
FOO(std::map<int COMMA int>);
This should perhaps ideally be a comment, but SO doesn't support code in comments, so, you can do
#include <map>
#define T_ARGS( ... ) < __VA_ARGS__ >
#define FOO( a ) a x;
auto main() -> int
{
FOO( std::map T_ARGS( int, int ) );
(void) x;
}
or you can define a macro that resolves to comma, or you can use just about any scheme that's specific to some particular use case (e.g., passing template name separately).
Just add an extra set of parentheses:
#define FOO(a)
FOO((std::map<int, int>));
The preprocessor will only treat unparenthesised commas as a macro argument separator. So what you can do is rewrite std::map<int, int> into something that has parentheses around it. A simple one might be decltype(std::map<int, int>()).

BOOST_PP expand sequence in the empty sequence case

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.

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