1

我已经在 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 宏。

4

1 回答 1

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 宏,但没有成功。

于 2013-03-14T09:39:01.547 回答