我在Windows 7 + VisualStudio 2012环境中成功使用了一些“流行”的宏。上周我想将项目移植到Linux(没有平台相关代码,小代码库)。使C ++代码构建起来非常容易,但我对宏不尽相同。
我把这个简单的测试放在一起:
/// concatenates tokens, even when the tokens are macros themselves
#define PP_JOIN_HELPER_HELPER(_0, _1) _0##_1
#define PP_JOIN_HELPER(_0, _1) PP_JOIN_HELPER_HELPER(_0, _1)
#define PP_JOIN_IMPL(_0, _1) PP_JOIN_HELPER(_0, _1)
#define PP_JOIN_2(_0, _1) PP_JOIN_IMPL(_0, _1)
#define PP_JOIN_3(_0, _1, _2) PP_JOIN_2(PP_JOIN_2(_0, _1), _2)
#define PP_JOIN_4(_0, _1, _2, _3) PP_JOIN_2(PP_JOIN_3(_0, _1, _2), _3)
#define PP_JOIN_5(_0, _1, _2, _3, _4) PP_JOIN_2(PP_JOIN_4(_0, _1, _2, _3), _4)
#define PP_JOIN_6(_0, _1, _2, _3, _4, _5) PP_JOIN_2(PP_JOIN_5(_0, _1, _2, _3, _4), _5)
#define PP_JOIN_7(_0, _1, _2, _3, _4, _5, _6) PP_JOIN_2(PP_JOIN_6(_0, _1, _2, _3, _4, _5), _6)
#define PP_JOIN_8(_0, _1, _2, _3, _4, _5, _6, _7) PP_JOIN_2(PP_JOIN_7(_0, _1, _2, _3, _4, _5, _6), _7)
#define PP_JOIN_9(_0, _1, _2, _3, _4, _5, _6, _7, _8) PP_JOIN_2(PP_JOIN_8(_0, _1, _2, _3, _4, _5, _6, _7), _8)
#define PP_JOIN_10(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) PP_JOIN_2(PP_JOIN_9(_0, _1, _2, _3, _4, _5, _6, _7, _8), _9)
#define PP_JOIN_11(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) PP_JOIN_2(PP_JOIN_10(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9), _10)
#define PP_JOIN_12(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) PP_JOIN_2(PP_JOIN_11(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10), _11)
#define PP_JOIN_13(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) PP_JOIN_2(PP_JOIN_12(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11), _12)
#define PP_JOIN_14(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) PP_JOIN_2(PP_JOIN_13(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12), _13)
#define PP_JOIN_15(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) PP_JOIN_2(PP_JOIN_14(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13), _14)
#define PP_JOIN_16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) PP_JOIN_2(PP_JOIN_15(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14), _15)
/// chooses a value based on a condition
#define PP_IF_0(t, f) f
#define PP_IF_1(t, f) t
#define PP_IF(cond, t, f) PP_JOIN_2(PP_IF_, PP_TO_BOOL(cond))(t, f)
/// converts a condition into a boolean 0 (=false) or 1 (=true)
#define PP_TO_BOOL_0 0
#define PP_TO_BOOL_1 1
#define PP_TO_BOOL_2 1
#define PP_TO_BOOL_3 1
#define PP_TO_BOOL_4 1
#define PP_TO_BOOL_5 1
#define PP_TO_BOOL_6 1
#define PP_TO_BOOL_7 1
#define PP_TO_BOOL_8 1
#define PP_TO_BOOL_9 1
#define PP_TO_BOOL_10 1
#define PP_TO_BOOL_11 1
#define PP_TO_BOOL_12 1
#define PP_TO_BOOL_13 1
#define PP_TO_BOOL_14 1
#define PP_TO_BOOL_15 1
#define PP_TO_BOOL_16 1
#define PP_TO_BOOL(x) PP_JOIN_2(PP_TO_BOOL_, x)
/// Returns 1 if the arguments to the variadic macro are separated by a comma, 0 otherwise.
#define PP_HAS_COMMA(...) PP_HAS_COMMA_EVAL(PP_HAS_COMMA_ARGS(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0))
#define PP_HAS_COMMA_EVAL(...) __VA_ARGS__
#define PP_HAS_COMMA_ARGS(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) _16
/// Returns 1 if the argument list to the variadic macro is empty, 0 otherwise.
#define PP_IS_EMPTY(...) \
PP_HAS_COMMA \
( \
PP_JOIN_5 \
( \
PP_IS_EMPTY_CASE_, \
PP_HAS_COMMA(__VA_ARGS__), \
PP_HAS_COMMA(PP_IS_EMPTY_BRACKET_TEST __VA_ARGS__), \
PP_HAS_COMMA(__VA_ARGS__ (~)), \
PP_HAS_COMMA(PP_IS_EMPTY_BRACKET_TEST __VA_ARGS__ (~)) \
) \
)
#define PP_IS_EMPTY_CASE_0001 ,
#define PP_IS_EMPTY_BRACKET_TEST(...) ,
// PP_VA_NUM_ARGS() is a very nifty macro to retrieve the number of arguments handed to a variable-argument macro.
// unfortunately, VS 2010 still has this preprocessor bug which treats a __VA_ARGS__ argument as being one single parameter:
// https://connect.microsoft.com/VisualStudio/feedback/details/521844/variadic-macro-treating-va-args-as-a-single-parameter-for-other-macros#details
#if _MSC_VER >= 1400
# define PP_VA_NUM_ARGS_HELPER(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, N, ...) N
# define PP_VA_NUM_ARGS_REVERSE_SEQUENCE 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
# define PP_VA_NUM_ARGS_LEFT (
# define PP_VA_NUM_ARGS_RIGHT )
# define PP_VA_NUM_ARGS(...) PP_VA_NUM_ARGS_HELPER PP_VA_NUM_ARGS_LEFT __VA_ARGS__, PP_VA_NUM_ARGS_REVERSE_SEQUENCE PP_VA_NUM_ARGS_RIGHT
#else
# define PP_VA_NUM_ARGS(args...) PP_VA_NUM_ARGS_HELPER(args, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
# define PP_VA_NUM_ARGS_HELPER(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, N, ...) N
#endif
// PP_NUM_ARGS correctly handles the case of 0 arguments
#define PP_NUM_ARGS(...) PP_IF(PP_IS_EMPTY(__VA_ARGS__), 0, PP_VA_NUM_ARGS(__VA_ARGS__))
// PP_PASS_ARGS passes __VA_ARGS__ as multiple parameters to another macro, working around the following bug:
// https://connect.microsoft.com/VisualStudio/feedback/details/521844/variadic-macro-treating-va-args-as-a-single-parameter-for-other-macros#details
#if _MSC_VER >= 1400
# define PP_PASS_ARGS_LEFT (
# define PP_PASS_ARGS_RIGHT )
# define PP_PASS_ARGS(...) PP_PASS_ARGS_LEFT __VA_ARGS__ PP_PASS_ARGS_RIGHT
#else
# define PP_PASS_ARGS(...) (__VA_ARGS__)
#endif
/// Expand any number of arguments into a list of operations called with those arguments
#define PP_EXPAND_ARGS_0(op)
#define PP_EXPAND_ARGS_1(op, a1) op(a1, 0)
#define PP_EXPAND_ARGS_2(op, a1, a2) op(a1, 0) op(a2, 1)
#define PP_EXPAND_ARGS_3(op, a1, a2, a3) op(a1, 0) op(a2, 1) op(a3, 2)
#define PP_EXPAND_ARGS_4(op, a1, a2, a3, a4) op(a1, 0) op(a2, 1) op(a3, 2) op(a4, 3)
#define PP_EXPAND_ARGS_5(op, a1, a2, a3, a4, a5) op(a1, 0) op(a2, 1) op(a3, 2) op(a4, 3) op(a5, 4)
#define PP_EXPAND_ARGS_6(op, a1, a2, a3, a4, a5, a6) op(a1, 0) op(a2, 1) op(a3, 2) op(a4, 3) op(a5, 4) op(a6, 5)
#define PP_EXPAND_ARGS_7(op, a1, a2, a3, a4, a5, a6, a7) op(a1, 0) op(a2, 1) op(a3, 2) op(a4, 3) op(a5, 4) op(a6, 5) op(a7, 6)
#define PP_EXPAND_ARGS_8(op, a1, a2, a3, a4, a5, a6, a7, a8) op(a1, 0) op(a2, 1) op(a3, 2) op(a4, 3) op(a5, 4) op(a6, 5) op(a7, 6) op(a8, 7)
#define PP_EXPAND_ARGS_9(op, a1, a2, a3, a4, a5, a6, a7, a8, a9) op(a1, 0) op(a2, 1) op(a3, 2) op(a4, 3) op(a5, 4) op(a6, 5) op(a7, 6) op(a8, 7) op(a9, 8)
#define PP_EXPAND_ARGS_10(op, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) op(a1, 0) op(a2, 1) op(a3, 2) op(a4, 3) op(a5, 4) op(a6, 5) op(a7, 6) op(a8, 7) op(a9, 8) op(a10, 9)
#define PP_EXPAND_ARGS_11(op, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) op(a1, 0) op(a2, 1) op(a3, 2) op(a4, 3) op(a5, 4) op(a6, 5) op(a7, 6) op(a8, 7) op(a9, 8) op(a10, 9) op(a11, 10)
#define PP_EXPAND_ARGS_12(op, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) op(a1, 0) op(a2, 1) op(a3, 2) op(a4, 3) op(a5, 4) op(a6, 5) op(a7, 6) op(a8, 7) op(a9, 8) op(a10, 9) op(a11, 10) op(a12, 11)
#define PP_EXPAND_ARGS_13(op, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) op(a1, 0) op(a2, 1) op(a3, 2) op(a4, 3) op(a5, 4) op(a6, 5) op(a7, 6) op(a8, 7) op(a9, 8) op(a10, 9) op(a11, 10) op(a12, 11) op(a13, 12)
#define PP_EXPAND_ARGS_14(op, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) op(a1, 0) op(a2, 1) op(a3, 2) op(a4, 3) op(a5, 4) op(a6, 5) op(a7, 6) op(a8, 7) op(a9, 8) op(a10, 9) op(a11, 10) op(a12, 11) op(a13, 12) op(a14, 13)
#define PP_EXPAND_ARGS_15(op, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) op(a1, 0) op(a2, 1) op(a3, 2) op(a4, 3) op(a5, 4) op(a6, 5) op(a7, 6) op(a8, 7) op(a9, 8) op(a10, 9) op(a11, 10) op(a12, 11) op(a13, 12) op(a14, 13) op(a15, 14)
#define PP_EXPAND_ARGS_16(op, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16) op(a1, 0) op(a2, 1) op(a3, 2) op(a4, 3) op(a5, 4) op(a6, 5) op(a7, 6) op(a8, 7) op(a9, 8) op(a10, 9) op(a11, 10) op(a12, 11) op(a13, 12) op(a14, 13) op(a15, 14) op(a16, 15)
#define PP_EXPAND_ARGS(op, ...) PP_JOIN_2(PP_EXPAND_ARGS_, PP_NUM_ARGS(__VA_ARGS__)) PP_PASS_ARGS(op, __VA_ARGS__)
/// Turn any legal C++ expression into nothing
#define UNUSED_IMPL(symExpr, n) , (void)sizeof(symExpr)
#define UNUSED(...) (void)sizeof(true) PP_EXPAND_ARGS PP_PASS_ARGS(UNUSED_IMPL, __VA_ARGS__)
/// stringizes a string, even macros
#define PP_STRINGIZE_HELPER(token) #token
#define PP_STRINGIZE(token) PP_STRINGIZE_HELPER(token)
#define TEST_CONDITION(condition, a, ...) (condition) ? UNUSED(a) : UNUSED(__VA_ARGS__)
int main(int argc, char* argv[])
{
int a = 3, b = 4, c = 5;
//UNUSED(a);
TEST_CONDITION(true, a, b, c);
}
最终宏视觉在VS 2012中运行良好,但GCC和Clang失败了。
运行clang -E macro.cpp
会创建此输出:
int main(int argc, char* argv[])
{
int a = 3, b = 4, c = 5;
(true) ? (void)sizeof(true) PP_EXPAND_ARGS (UNUSED_IMPL, a) : (void)sizeof(true) PP_EXPAND_ARGS (UNUSED_IMPL, b, c);
}
无法展开PP_EXPAND_ARGS。
正在运行clang macro.cpp
会出现以下错误:
macro.cpp:141:5: error: expected ':'
TEST_CONDITION(true, a, b, c);
^
macro.cpp:135:57: note: expanded from macro 'TEST_CONDITION'
#define TEST_CONDITION(condition, a, ...) (condition) ? UNUSED(a) : UNUSED(__VA_ARGS__)
^
macro.cpp:128:44: note: expanded from macro 'UNUSED'
#define UNUSED(args...) (void)sizeof(true) PP_EXPAND_ARGS PP_PASS_ARGS(UNUSED_IMPL, args)
^
macro.cpp:141:5: note: to match this '?'
macro.cpp:135:55: note: expanded from macro 'TEST_CONDITION'
#define TEST_CONDITION(condition, a, ...) (condition) ? UNUSED(a) : UNUSED(__VA_ARGS__)
^
macro.cpp:141:5: error: use of undeclared identifier 'UNUSED_IMPL'
TEST_CONDITION(true, a, b, c);
^
macro.cpp:135:57: note: expanded from macro 'TEST_CONDITION'
#define TEST_CONDITION(condition, a, ...) (condition) ? UNUSED(a) : UNUSED(__VA_ARGS__)
^
macro.cpp:128:72: note: expanded from macro 'UNUSED'
#define UNUSED(args...) (void)sizeof(true) PP_EXPAND_ARGS PP_PASS_ARGS(UNUSED_IMPL, args)
^
macro.cpp:101:45: note: expanded from macro 'PP_PASS_ARGS'
# define PP_PASS_ARGS(args...) (args)
^
2 errors generated.
基本上告诉我UNUSED_IMPL没有被声明,当它是。
我不明白为什么它不起作用,为什么它告诉我UNUSED_IMPL宏没有被声明。
答案 0 :(得分:1)
事实证明问题是宏PP_PASS_ARGS:
# define PP_PASS_ARGS(args...) (args)
当没有使用UNUSED和PP_EXPAND_ARGS宏时,一切正常:
#define PP_EXPAND_ARGS(op, args...) PP_JOIN_2(PP_EXPAND_ARGS_, PP_NUM_ARGS(args))(op, args)
#define UNUSED(args...) (void)sizeof(true) PP_EXPAND_ARGS(UNUSED_IMPL, args)
我不知道为什么它适用于VisualStudio的预处理器而不适用于GCC和Clang。我还没有尝试删除VisualStudio下的PP_PASS_ARGS宏,看看我是否可以完全摆脱它。
编辑:我尝试删除VisualSutio下的PP_PASS_ARGS宏,但它不起作用。