Why does macro expansion follow template expansion? - c++

As a rule, code example first:
void f1(int)
{}
#define f2(a) f1(a)
template<class F>
void f3(F f)
{
f(0);
}
int main()
{
f3(f2); // error C2065: 'f2' : undeclared identifier
return 0;
}
Compiled by VC++ 2012.
My question is:
Why does macro expansion follow template expansion? I think it is extremly counter-intuitive and error-prone.

Why does macro expansion follow template expansion?
Um, it doesn't. Macro expansion is done by the preprocessor, template expansion is done by the parser/compiler stage (which runs only after preprocessing).
What you're missing here is the style of the macro. f2() is a function-style macro. So if you write f2 (without the parentheses), the preprocessor won't substitute it to f1. If you want such a substitution, simply define it as
#define f2 f1
Side note: as currently standing, this code doesn't make much sense. Even if you used the parentheses and wrote f2(), you would get a compiler error, since the f2() macro takes exactly one argument. You should supply an argument to it if it's a function-style macro that has an argument and is not variadic.

It doesn't.
The problem is you've defined f2 using the function-style macro definition, e.g.
#define f2(foo) bar
But in your source code you're only using f2 as a token, not using it as a function call, e.g.
f3(f2)
Since you're not using it as a function call, it doesn't match the function-style macro definition, and thus doesn't get substituted.

Related

My c++ code failed on a macro+template compilation, why this error?

I was using old version of gcc so I tried to implement several useful type_traits elements, like is_base_of and static_assert, like this:
template <typename Base, typename Derived>
struct my_is_base_of{
struct Yes{char _;};
struct No{char _[2];};
static Yes _test(const Base*);
static No _test(void*);
static const bool value=sizeof(_test((Derived*)0))==sizeof(Yes);
};
template<bool b>struct _static_assert_test{static char _;};
template<>struct _static_assert_test<false>{};
#define _static_assert(x) _static_assert_test<x>::_
struct Base{};
struct Derived : Base {};
struct C {};
#include<iostream>
int main()
{
std::cout<<std::boolalpha<<my_is_base_of<Base,Derived>::value<<std::endl;
_static_assert(sizeof(int)==4);
_static_assert(my_is_base_of<Base,Derived>::value);//fails to compile
return 0;
}
Well, the 1st line in main functions compiles and prints "true". So does the 2nd line. But the 3rd line fails to compile. My gcc 4.1.2 says:
derive.cpp:22:54: error: macro "_static_assert" passed 2 arguments, but takes just 1
derive.cpp: In function ‘int main()’:
derive.cpp:22: error: ‘_static_assert’ was not declared in this scope
How to fix my case? Thanks a lot.
It is worth noting that C++ macros are expanded before the parsing phase of compilation and this is done by a textual replacement of each parameter of the macro to matched places. my_is_base_of<Base,Derived>::value is here interpreted by the macro as two parameters as it uses comma operator: my_is_base_of<Base becomes first parameter and Derived>::value becomes second. This behavior is precisely due to the fact that macro does not (cannot) perform parsing and as such it is unable to know that comma is used in the context of template parameters. To workaround the problem you need to put the statement in parenthesis:
_static_assert((my_is_base_of<Base,Derived>::value));
compiles without problems.

Is it legal for a function-like macro to "steal" commas from a parenthesized template argument list?

I was just surprised that providing a type with two template arguments to a function-like macro resulted in the compiler complaining.
This (conceptually similar) example code:
template<typename T> struct foo{};
template<typename T, U> struct bar{};
#define p(x) printf("sizeof(" #x ") = %u\n", sizeof(x));
int main()
{
p(foo<int>); // works, of course
p(bar<int,int>); // does not work
p((bar<int,int>)); // does not work either
return 0;
}
makes GCC (6.2.0) complain macro "p" passed 2 arguments, but takes just 1.
Well, of course, the preprocessor is a preprocessor doing text replacement, it's not a real C++ compiler which understands templates or all other rules of the language.
Maybe I'm asking too much by expecting that the preprocessor recognizes the angle brackets, granted... but at least parentheses are explicitly mentioned in the specification.
16.3 (paragraphs 10 to 12) states outermost parentheses delimiting the bounded sequence of tokens. The word "outermost" suggests that there may possibly also be further (not-outermost) parentheses which the preprocessor recognizes.
Also, it explicitly states "skipping intervening matched pairs of left and right parenthesis" as well as "comma preprocessing tokens between matching inner parentheses do not separate arguments" -- which means that if I am reading correctly, then at least the last line should in my understanding pass.
What am I understanding wrong?
p((bar<int,int>)) is a valid invocation of the p macro with a single macro argument, which is (bar<int,int>). Your understanding so far is correct.
Unfortunately, its expansion includes sizeof((bar<int,int>)), and sizeof does not accept doubly-parenthesised types.
Variadic macros (C++11) work well here as an alternative.
#define p(...) printf("sizeof(" #__VA_ARGS__ ") = %u\n", sizeof(__VA_ARGS__));

C++ preprocessors are not aware of template arguments?

As it appears, C++ preprocessor fails if a template instantiation with multiple arguments passed to a macro as an argument.
See an example below.
#include <stdio.h>
#define FOO(v) printf("%d\n",v::val())
template<int N>
struct bar {
static int val() { return N; }
};
template<int N, int M>
struct baz {
static int val() { return N+M; }
};
int main() {
printf("%d\n",bar<1>::val());
printf("%d\n",baz<1,2>::val());
FOO(bar<10>); // OK
FOO(baz<20,30>); // error: too many arguments provided to function-like macro invocation
FOO((baz<20,30>)); // error: '::val' has not been declared
}
Tested with clang++ and g++
Should it be considered as a bug?
No, it's not a bug.
The c preprocessor is a different beast from the rest of the language and it plays by its own rules. Changing this would break compatibility in a massive way, CPP is highly rigorously standardized.
The usual way to work around these comma issues is,
typedef baz<20,30> baz2030_type;
FOO(baz2030_type);
The C/C++ preprocessor recognizes commas as macro argument separators unless they are nested inside parentheses. Just parentheses. Brackets, braces and template markers don't count:
The individual arguments within the list are separated by comma preprocessing tokens, but comma preprocessing tokens between matching inner parentheses do not separate arguments. (C++14 §16.3/11; C11 §6.10.3/11)
(A side effect of the above is that you can use unbalanced braces and brackets as macro arguments. That's usually not a very good idea, but you can do it if you have to.)
Problems occasionally crop up as a result; a common one is unwanted multiple arguments when the argument is supposed to be a block of code:
MY_FANCY_MACRO(1000, { int i=0, j=42; ... })
Here, the macro is called with (at least) 3 arguments, although it was probably written to accept 2.
With modern C++ (and C) compilers, you have a few options. In a fairly subjective order:
Rewrite the macro as an inline function. If the argument is a code block, consider using a templated function which could accept a lambda or other functor. If the argument is a type, make it a template argument instead.
If surrounding the argument with redundant parentheses is syntactically valid, do that. But in such a case it is almost certainly the case that suggestion (1) above would have worked.
Define:
#define COMMA ,
and use it where necessary:
FOO(baz<20 COMMA 30>);
This doesn't require modifying the macro definition in any way, but it will fail if the macro passes the argument to another macro. (The replacement will be done before the inner macro call is parsed, so the multiple argument problem will just be deferred to the inner call.)
If you expect that one macro argument might contain unprotected commas, and it is the last or only argument, and you're in a position to modify the macro, and you're using C++11/C99 or better (or gcc, which has allowed this as an extension for some time), make the macro variadic:
#define FOO(...) printf("%d\n",__VA_ARGS__::val())
The macro's argument is treated as plain text string and the arguments are separated using commas. Hence the comma in the template will be treated as a delimiter. Thus the preprocessor will think that you have passed on two arguments to a single argument macro, hence the error.
BOOST_IDENTITY_TYPE is the solution for that: https://www.boost.org/doc/libs/1_73_0/libs/utility/identity_type/doc/html/index.html
You can also wrap the type into decltype: decltype(std::pair<int, int>()) var; which also adds an extra parentheses, but this unfortunately does an extra ctor call.

Is the `gnu-zero-variadic-macro-arguments` safe to ignore?

Consider the following code (live example):
#define TEST_VA(mX, ...) TEST
#define STRINGIFY_IMPL(mX) #mX
#define STRINGIFY(mX) STRINGIFY_IMPL(mX)
#include <iostream>
int main()
{
std::cout << STRINGIFY(TEST_VA(1)) << std::endl;
std::cout << STRINGIFY(TEST_VA()) << std::endl;
return 0;
}
clang++ 3.4 complains:
main.cpp:9:37: warning: must specify at least one argument for '...' parameter of variadic macro [-Wgnu-zero-variadic-macro-arguments]
std::cout << STRINGIFY(TEST_VA(1)) << std::endl;
^
main.cpp:1:9: note: macro 'TEST_VA' defined here
#define TEST_VA(mX, ...) TEST
^
main.cpp:10:33: warning: must specify at least one argument for '...' parameter of variadic macro [-Wgnu-zero-variadic-macro-arguments]
std::cout << STRINGIFY(TEST_VA()) << std::endl;
^
main.cpp:1:9: note: macro 'TEST_VA' defined here
#define TEST_VA(mX, ...) TEST
I use some variadic macros like in the example code in my projects.
Is it safe to pass no arguments for the ... parameter of variadic macros? (Ignoring the warning, basically)
Or could it lead to problems?
Is it explicitly forbidden by the standard?
Starting from C++20, passing zero arguments to a variadic macro parameter is allowed, but Clang (at least Clang 12) doesn't respect this change yet.
So if you're targeting C++20 or newer and using -pedantic-errors, then -Wno-gnu-zero-variadic-macro-arguments is recommended.
[cpp.replace.general]/5
... there shall be at least as many arguments in the invocation as there are parameters in the macro definition (excluding the ...).
It's technically forbidden by the standard. From C++11 §16.3/4 (emphasis added):
If the identifier-list in the macro definition does not end with an ellipsis, the number of arguments (including
those arguments consisting of no preprocessing tokens) in an invocation of a function-like macro shall equal
the number of parameters in the macro definition. Otherwise, there shall be more arguments in the invocation than there are parameters in the macro definition (excluding the ...). There shall exist a ) preprocessing
token that terminates the invocation.
Some compilers may allow this as an extension, but if you want standards-compliant code, you should make sure to always include at least one argument for the ellipsis parameter of a variadic macro.

Create an alias to a function

I want to create an alias to CPPUNIT_ASSERT_EQUAL_MESSAGE(string message, T expected, T actual). I tried:
template<class T> void (&_assert)(string, T, T) = &CPPUNIT_ASSERT_EQUAL_MESSAGE;
Not sure if its right at all, but I am getting errors like
Error 1 error C2530: '_assert' : references must be initialized h:\dropbox\sch\cs3202\code\test\testqueryevaluator\testgetcandidatelist.h 22
Error 2 error C2998: 'void (__cdecl &__cdecl _assert)(std::string,T,T)' : cannot be a template definition h:\dropbox\sch\cs3202\code\test\testqueryevaluator\testgetcandidatelist.h 22
Whats the right syntax?
Just create a forwarding function:
template<class T>
inline void _assert(const string& message, T expected, T actual)
{ CPPUNIT_ASSERT_EQUAL_MESSAGE(message, expected, actual); }
Simple put, there is no right syntax, because, as phoeagon noted, that is a macro, no function:
See the reference
CPPUNIT_ASSERT_EQUAL_MESSAGE is a macro and not a function, and so you can either "wrap" it with an actual definition of an inline function (as a previous answer suggested) or simply #define an alias macro:
#define _assert CPPUNIT_ASSERT_EQUAL_MESSAGE
Of the two, I would choose the wrapper function method so it could be declared in a namespace and to avoid naming conflicts.