2012-02-07 2 views
21

그래서 GCC에서 잘 작동하지만 Microsoft의 C++ 컴파일러에서는 작동하지 않는 매크로가 있습니다. 나는 누군가가 해결 방법을 알기를 바랄 것입니다, 또는 아마 그것이 왜 이런 식으로 행동하는지 설명 할 수 있습니다.MSVC++ 가변 매크로 확장

이 매크로는 정확히 "표준"이 아니지만 정말로 도움이 될 것입니다. GCC는 위의 확장 방법은 다음과

struct MyStructure 
{ 
    void Foo() 
    { 
    EXPAND_THESE(Property1, Property2, Property3, Property4) 
    } 

    Base * parent; 
} 

:

여기
#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, N, ...) N 
#define VA_NARGS(...) VA_NARGS_IMPL(__VA_ARGS__, 5, 4, 3, 2, 1) 

#define FULLY_EXPANDED(count, ...) \ 
    MAC## count (__VA_ARGS__) 

#define SEMI_EXPANDED(count, ...) FULLY_EXPANDED(count, __VA_ARGS__) 

#define EXPAND_THESE(...) SEMI_EXPANDED(VA_NARGS(__VA_ARGS__), __VA_ARGS__) 

#define ACTUAL_MACRO(x) parent->GetProperty<x>(); 
#define MAC1(a) ACTUAL_MACRO(a) 
#define MAC2(a,b) MAC1(a) ACTUAL_MACRO(b) 
#define MAC3(a,b,c) MAC2(a,b) ACTUAL_MACRO(c) 
#define MAC4(a,b,c,d) MAC3(a,b,c) ACTUAL_MACRO(d) 
#define MAC5(a,b,c,d,e) MAC4(a,b,c,d) ACTUAL_MACRO(e) 

내가이 매크로를 사용할 수있는 방법입니다 : 여기

매크로의 기능 예입니다

struct MyStructure 
{ 
    void Foo() 
    { 
    parent->GetProperty<Property1>(); 
    parent->GetProperty<Property2>(); 
    parent->GetProperty<Property3>(); 
    parent->GetProperty<Property4>(); 
    } 

    Base * parent; 
} 

그러나 Microsoft는 어떤 이유로 든 내 모든 __VA_ARGS__을 하나의 인수로 확장합니다.

struct MyStructure 
{ 
    void Foo() 
    { 
    parent->GetProperty<Property1, Property2, Property3, Property4>(); 
    } 

    Base * parent; 
} 

왜이 사람인지 알 수 있습니까? Microsoft에서 GCC와 같이 확장 할 수있는 트릭이 있습니까? 여분의 두 쌍의 괄호를 던지면 어떨까요?

이와 같은 매크로는 "풀"코드를 대체하는 데 정말로 도움이되지만이 문제로 인해 내 VS 프로젝트로 옮길 수 없습니다. 어떤 도움이라도 대단히 감사하겠습니다!

감사합니다.

+5

It'sa [버그] (http://connect.microsoft.com/VisualStudio/feedback/details/380090/variadic-macro-replacement)와 내가 생각하는 돈't 그들은 곧 그것을 고칠 계획이다. –

+0

Linked Duplicate : [MSVC++ (Microsoft Visual Studio)에서 "매크로 오버로딩"으로 인한 가변 매크로 관련 문제를 해결하는 방법?] (https://stackoverflow.com/q/48710758/514235) - @JesseGood Thx 곤충. – iammilind

답변

17

필자는 우연히도 오늘이 문제에 부딪치게되었고, 충분한 노력을 기울여 내 자신의 목적을위한 해결책을 찾은 것 같습니다. 버그는 MSVC가 인수 목록에서 단일 토큰으로 __VA_ARGS__을 처리합니다. 그러나 매크로 호출 인수 목록에서 직접 사용하지 않으면이 문제를 해결할 수 있습니다.

#define VA_NARGS(...) VA_NUM_ARGS_IMPL_((__VA_ARGS__, 5,4,3,2,1)) 
#define VA_NARGS_IMPL_(tuple) VA_NUM_ARGS_IMPL tuple 
#define VA_NARGS_IMPL(_1,_2,_3,_4,_5,N,...) N 

을하지만 난 당신이 가능성이 완전히 당신이 원하는 실제 "N"으로 확장됩니다 확인하는 문제로 실행하겠습니다 의심, 그리고 VA_NARGS_IMPL (arg1, arg2, 5, 4, 3, 2, 1)에 : This comment이 문제에 대한 답변의 시작을 제안한다 말하라. 내 코드 (당신처럼 보였습니다)가 MAC##code을 하나의 단위로 확장해야한다는 것을 알았습니다. 그런 다음 인수 목록과 개별적으로 결합해야했습니다. 여기에 내가 나를 위해 일한 발견 코드입니다 :

#define ASSERT_HELPER1(expr) singleArgumentExpansion(expr) 
#define ASSERT_HELPER2(expr, explain) \ 
    twoArgumentExpansion(expr, explain) 

/* 
* Count the number of arguments passed to ASSERT, very carefully 
* tiptoeing around an MSVC bug where it improperly expands __VA_ARGS__ as a 
* single token in argument lists. See these URLs for details: 
* 
* http://connect.microsoft.com/VisualStudio/feedback/details/380090/variadic-macro-replacement 
* http://cplusplus.co.il/2010/07/17/variadic-macro-to-count-number-of-arguments/#comment-644 
*/ 
#define COUNT_ASSERT_ARGS_IMPL2(_1, _2, count, ...) \ 
    count 
#define COUNT_ASSERT_ARGS_IMPL(args) \ 
    COUNT_ASSERT_ARGS_IMPL2 args 
#define COUNT_ASSERT_ARGS(...) \ 
    COUNT_ASSERT_ARGS_IMPL((__VA_ARGS__, 2, 1, 0)) 
/* Pick the right helper macro to invoke. */ 
#define ASSERT_CHOOSE_HELPER2(count) ASSERT_HELPER##count 
#define ASSERT_CHOOSE_HELPER1(count) ASSERT_CHOOSE_HELPER2(count) 
#define ASSERT_CHOOSE_HELPER(count) ASSERT_CHOOSE_HELPER1(count) 
/* The actual macro. */ 
#define ASSERT_GLUE(x, y) x y 
#define ASSERT(...) \ 
    ASSERT_GLUE(ASSERT_CHOOSE_HELPER(COUNT_ASSERT_ARGS(__VA_ARGS__)), \ 
       (__VA_ARGS__)) 

int foo() 
{ 
    ASSERT(one); // singleArgumentExpansion(one) 
    ASSERT(two, "foopy"); // twoArgumentExpansion(two, "foopy") 
} 

내 마음은 몇 시간 후 당신을 가서 완전히 해결하기 위해 내 자신의 문제를 해결 한 후 너무 많은 박쥐 우산을, 나는 말을 죄송합니다. :-) 그러나 나는 이것이 작은 일로 당신을 일하는 무언가로 인도하기에 충분하다고 생각합니다.

16

나는이 질문이 2 년이 넘었다는 것을 알고있다. 그러나 나는 내가했던 것처럼 아직도 이것에 비틀 거린 사람들에게 더 세련된 대답을하려고 노력할 것이라고 생각했다.

Jeff Walden의 대답은 모두 작동하지만 가변 인자가 필요한 각 FOO 매크로에 대해 FOO_CHOOSE_HELPER/1/2를 선언해야합니다. 이 문제를 해결하기 위해 추상화 계층을 개발했습니다.

#define ERROR1(title) printf("Error: %s\n", title) 
#define ERROR2(title, message)\ 
    ERROR1(title);\ 
    printf("Message: %s\n", message) 

#define ERROR_CHOOSE_HELPER2(count) ERROR##count 
#define ERROR_CHOOSE_HELPER1(count) ERROR_CHOOSE_HELPER2(count) 
#define ERROR_CHOOSE_HELPER(count) ERROR_CHOOSE_HELPER1(count) 

#define ERROR(...) GLUE(ERROR_CHOOSE_HELPER(COUNT_ARGS_MAX5(__VA_ARGS__)),\ 
    (__VA_ARGS__)) 

#define ASSERT1(expr) singleArgumentExpansion(expr) 
#define ASSERT2(expr, explain) twoArgumentExpansion(expr, explain) 

#define ASSERT_CHOOSE_HELPER2(count) ASSERT##count 
#define ASSERT_CHOOSE_HELPER1(count) ASSERT_CHOOSE_HELPER2(count) 
#define ASSERT_CHOOSE_HELPER(count) ASSERT_CHOOSE_HELPER1(count) 

#define ASSERT(...) GLUE(ASSERT_CHOOSE_HELPER(COUNT_ARGS_MAX5(__VA_ARGS__)),\ 
    (__VA_ARGS__)) 

그것은 것 : 다음과 같이 매크로를 정의해야

#define ERROR1(title) printf("Error: %s\n", title) 
#define ERROR2(title, message)\ 
    ERROR1(title);\ 
    printf("Message: %s\n", message) 
#define ERROR(...) CALL_OVERLOAD(ERROR, __VA_ARGS__) 

#define ASSERT1(expr) singleArgumentExpansion(expr) 
#define ASSERT2(expr, explain) twoArgumentExpansion(expr, explain) 
#define ASSERT(...) CALL_OVERLOAD(ASSERT, __VA_ARGS__) 

을 제프의 대답 : 당신은 같은 가변 인자 매크로를 정의 할 수 있습니다이 아키텍처를

#define GLUE(x, y) x y 

#define RETURN_ARG_COUNT(_1_, _2_, _3_, _4_, _5_, count, ...) count 
#define EXPAND_ARGS(args) RETURN_ARG_COUNT args 
#define COUNT_ARGS_MAX5(...) EXPAND_ARGS((__VA_ARGS__, 5, 4, 3, 2, 1, 0)) 

#define OVERLOAD_MACRO2(name, count) name##count 
#define OVERLOAD_MACRO1(name, count) OVERLOAD_MACRO2(name, count) 
#define OVERLOAD_MACRO(name, count) OVERLOAD_MACRO1(name, count) 

#define CALL_OVERLOAD(name, ...) GLUE(OVERLOAD_MACRO(name, COUNT_ARGS_MAX5(__VA_ARGS__)), (__VA_ARGS__)) 

: 다음 사항을 고려 큰 문제는 아니지만 가능한 한 간결하게 코드를 작성하는 것이 좋습니다. 또한 여러 가변 매크로를 사용하는 경우 코드 중복 및 발생할 수있는 복잡성을 줄이기 위해 기하 급수적으로 도움이됩니다. 내가 아는 한이 방법은 이식성이 뛰어납니다. 가장 일반적인 컴파일러에서 테스트 해본 결과 동일한 결과가 나타났습니다.

사용 예 :

이 에게
int foo() 
{ 
    ASSERT(one); // singleArgumentExpansion(one) 
    ASSERT(two, "foopy"); // twoArgumentExpansion(two, "foopy") 

    ERROR("Only print a title"); 
    ERROR("Error title", "Extended error description"); 
} 
+0

참고 : ';'을 제거해야했습니다. '#define CALL_OVERLOAD'의 끝에서 "error : expected"오류가 발생합니다) 'before'; ' token' with gcc4.9 – ideasman42

+0

이것에 기반하여, var-args 기반 ELEM 매크로를 구현하기 위해 최대 16 개의 arg를 사용하는 예제가 있습니다. http://stackoverflow.com/a/24837037/432509 (관심 대상 일 수 있습니다) – ideasman42

+0

@ ideasman42 지적 해 주셔서 감사합니다. –