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

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>()).

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

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

Why is this "call" to assert seeing two arguments instead of just the one?

Given this test program:
#include <cassert>
#include <string>
#include <type_traits>
const std::string& const_string = "bla";
std::string const & string_const = "blabla";
static_assert(std::is_same<decltype(const_string), decltype(string_const)>::value, "Uhoh");
int main()
{
assert(std::is_same<decltype(const_string), decltype(string_const)>::value);
}
Which asserts that two types are the same at compile-time and at runtime using C's assert. All of Clang, MSVC2015, and GCC report the same error, so I'm quite sure it's me:
main.cpp:13:49: error: too many arguments provided to function-like macro invocation
assert(std::is_same<decltype(const_string), decltype(string_const)>::value);
^
/usr/include/assert.h:91:10: note: macro 'assert' defined here
# define assert(expr) \
^
I'm just not seeing two arguments in the assert. What's more, the static_assert works just fine... So what is going on here?
The C preprocessor does not recognise C++ template syntax, therefore template brackets < and > are not viewed as grouping tokens by the preprocessor, they're seen as simple characters.
This means that the preprocessor will view the comma between the template parameters as a macro parameter separator, like this:
assert(
std::is_same<decltype(const_string),
decltype(string_const)>::value);
To force the preprocessor to see your expression as a single statement, simply wrap your assert parameter in an additional set of brackets:
assert((std::is_same<decltype(const_string), decltype(string_const)>::value));
static_assert doesn't have this limitation because it is a C++ keyword, rather than a preprocessor macro like assert(). This means it fully supports C++ syntax and sees the template parameters correctly.
It's because of the < and > tokens. They mess up the preprocessor. Remember that assert is a macro, not a function.
Do this (add an extra set of parentheses):
assert((std::is_same<decltype(const_string), decltype(string_const)>::value));
^ ^

Inhibit macro expansion

Is there any way to inhibit preprocessor macro expansion? I have an existing C header file that uses #define to define a set of integers and I would like to copy it to a C++ enum that has the same value names. For example (using C++11):
enum MyEnum {
VALUE,
// ...
};
#define VALUE 0
MyEnum convert(int x) {
if (x == VALUE) {
return MyEnum::VALUE;
}
// ...
}
The problem of course is that MyEnum::VALUE gets translated to MyEnum::0, which causes a syntax error. The best solution is to replace the macros with enums, but unfortunately that is not an option in my situation.
I tried to use concatenation, but that didn't help (the compiler gave the same error).
#define CONCAT(a,b) a##b
// ...
return MyEnum::CONCAT(VA,LUE); // still results in MyEnum::0
Is there another solution that allows me to have the same name for the macro and for the enum value?
You can undefine a macro:
#undef VALUE
after including the header.

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