2013-03-13 2 views
1

저는 Windows 7 + VisualStudio 2012 환경에서 "많이 사용되는"매크로를 성공적으로 사용했습니다. 지난 주에 프로젝트를 Linux로 포팅하고 싶었습니다 (플랫폼 종속 코드, 작은 코드베이스 없음). 빌드 할 C++ 코드를 작성하는 것은 쉽지만, 매크로에 대해서는 동일하지 않습니다.매크로 대체가 GCC 4.7/Clang 3.2에서 실패합니다.

/// 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); 
} 

결국 매크로 substition는 VS 2012 년 잘 작동하지만 GCC와 연타 실패 :

내가 함께 간단한 테스트를 넣었습니다.

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 -E macro.cpp 실행

이 출력을 생성합니다. 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 매크로가 선언되지 않았는지 알 수 없습니다.

사용하지 않은 및 PP_EXPAND_ARGS 매크로 모두에서 제외
# define PP_PASS_ARGS(args...)    (args) 

잘 작동 :으로 VisualStudio의 작동이 왜

#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) 

모르겠어요

+0

UNUSED_IMPL 매크로가 선언되지 않았다는 것을 말하는 것이 아니라 매크로 확장 후에도 여전히 UNUSED_IMPL 어딘가에 있으며 그 이름을 가진 변수 나 함수가 선언되지 않았다는 것을 알려줍니다. 비주얼 스튜디오는 상당히 비표준적인 전처리 기가있는 것으로 알려져 있습니다. 매크로에 더 많은 회귀선을 추가해야 할 수도 있습니다. –

+0

UNUSED_IMPL은 인수 목록 뒤에 인수 목록이 오는 경우 정의되지만, 인수 목록이없는 경우에는 정의되지 않습니다. 최종 사전 처리 된 소스는 어떤 모습입니까? 어쩌면 매크로의 다른 레이어가 있다면 더 잘 처리 할 수 ​​있을까요? – ams

+0

PP_EXPAND_ARGS는 UNUSED_IMPL에 인수 목록을 전달해야합니다. 더 많은 방향을 추가하고 어떤 일이 일어날지를 볼 수 있습니다 ... –

답변

1

그것은 문제가 매크로 PP_PASS_ARGS이었다 밝혀 전처리 기가 아니라 GCC와 Clang이 필요합니다. 그리고 Visual Studio에서 PP_PASS_ARGS 매크로를 제거하려고 시도하지 않았습니다. 아직 제거하지 못했는지 확인해야합니다.

편집 : VisualSutio에서 PP_PASS_ARGS 매크로를 제거해 보았지만 작동하지 않았습니다.

+1

Boost.PP와 관련된 Boost 메일 링리스트의 토론에서 Visual Studio C++ 컴파일러와 그 전처리 기가 완전히 부합하지 않고 매크로를 잘못 확장했다는 것을 상기합니다. Clang과 GCC 모두 코드를 거부하는 것이 맞을 수 있습니다. – wilx

관련 문제