c++递归宏不能在MSVC上编译?



我从[https://www.scs.stanford.edu/~dm/blog/va-opt.html]得到了这个源代码。在c++ 20中使用MSVC,它不能编译,但可以在其他编译器上编译。为什么?我怎么才能解决这个问题呢?

`/* compile with:
c++ -std=c++20 -Wall -Werror make_enum.cc -o make_enum
*/
#include <iostream>
#define PARENS ()
// Rescan macro tokens 256 times
#define EXPAND(arg) EXPAND1(EXPAND1(EXPAND1(EXPAND1(arg))))
#define EXPAND1(arg) EXPAND2(EXPAND2(EXPAND2(EXPAND2(arg))))
#define EXPAND2(arg) EXPAND3(EXPAND3(EXPAND3(EXPAND3(arg))))
#define EXPAND3(arg) EXPAND4(EXPAND4(EXPAND4(EXPAND4(arg))))
#define EXPAND4(arg) arg
#define FOR_EACH(macro, ...)                                    
__VA_OPT__(EXPAND(FOR_EACH_HELPER(macro, __VA_ARGS__)))
#define FOR_EACH_HELPER(macro, a1, ...)                         
macro(a1)                                                     
__VA_OPT__(FOR_EACH_AGAIN PARENS (macro, __VA_ARGS__))
#define FOR_EACH_AGAIN() FOR_EACH_HELPER
#define ENUM_CASE(name) case name: return #name;
#define MAKE_ENUM(type, ...)                    
enum type {                                     
__VA_ARGS__                                   
};                                              
constexpr const char *                          
to_cstring(type _e)                             
{                                               
using enum type;                              
switch (_e) {                                 
FOR_EACH(ENUM_CASE, __VA_ARGS__)              
default:                                      
return "unknown";                           
}                                             
}
MAKE_ENUM(MyType, ZERO, ONE, TWO, THREE);
void
test(MyType e)
{
std::cout << to_cstring(e) << " = " << e << std::endl;
}
int
main()
{
test(ZERO);
test(ONE);
test(TWO);
test(THREE);
}
/*
Local Variables:
c-macro-pre-processor: "c++ -x c++ -std=c++20 -E -"
End:
*/`
我无论如何也想不出问题是什么。我试过修改代码,对我来说,__VA_ARGS__似乎没有被接受,当它应该被接受的时候,或者它不能接受宏排序的方式。下面是错误:
1>make_enum.cc
1>C:UsersThomassourcereposoutoftouchoutoftouchmake_enum.cc(40,1): warning C4003: not enough arguments for function-like macro invocation 'FOR_EACH_HELPER'
1>C:UsersThomassourcereposoutoftouchoutoftouchmake_enum.cc(40,1): warning C4003: not enough arguments for function-like macro invocation 'ENUM_CASE'
1>C:UsersThomassourcereposoutoftouchoutoftouchmake_enum.cc(40,1): error C2059: syntax error: 'case'
1>C:UsersThomassourcereposoutoftouchoutoftouchmake_enum.cc(40,1): error C2065: 'ENUM_CASE': undeclared identifier
1>C:UsersThomassourcereposoutoftouchoutoftouchmake_enum.cc(40,1): error C3861: 'FOR_EACH_HELPER': identifier not found
1>C:UsersThomassourcereposoutoftouchoutoftouchmake_enum.cc(40,1): error C2059: syntax error: ')'
1>C:UsersThomassourcereposoutoftouchoutoftouchmake_enum.cc(40,1): error C2146: syntax error: missing ';' before identifier 'default'
1>C:UsersThomassourcereposoutoftouchoutoftouchmake_enum.cc(40,1): warning C4065: switch statement contains 'default' but no 'case' labels
1>Done building project "outoftouch.vcxproj" -- FAILED.

MSVC中的旧预处理器已知包含许多问题,例如参见Microsoft的这篇文章。他们的新预处理器可以通过/Zc:preprocessor标志使用,使预处理器更符合标准,也更接近其他主要编译器的功能。

在这个具体的情况下,我认为问题是旧的预处理器不正确地扩展FOR_EACH_AGAINFOR_EACH_HELPER早期;不应该,因为后面没有括号。比较相应的部分"重新扫描宏的替换列表";在上面提到的文章中。警告"not enough arguments for function-like macro invocation 'FOR_EACH_HELPER'"也暗示了这一点。

使用/Zc:preprocessor,代码编译没有问题。关于godbolt的示例。

最新更新