2012-02-07 160 views
21

所以我有一個在GCC中很好地工作的宏,但不是在微軟的C++編譯器中。我希望有人知道解決方法,或者可以向我解釋爲什麼這樣做。MSVC++ variadic宏擴展

我確定這個宏並不完全是「標準」,但它確實會幫助我。

這裏是宏的功能例如:

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

這裏是我會如何使用這個宏:GCC是如何擴展上述

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

    Base * parent; 
} 

這裏是:

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

有人知道這是爲什麼嗎?是否有一些技巧可以讓微軟像GCC一樣擴展?也許折騰一對額外的括號?

像這樣的宏可以真正幫我取代一堆「膠水」代碼,但由於這個問題,我無法將它移到我的VS項目中。任何幫助將不勝感激!

謝謝。

+5

It'sa [錯誤](http://connect.microsoft.com/VisualStudio/feedback/details/380090/variadic-macro-replacement)和I不'噸認爲他們計劃在不久的將來修復它。 – 2012-02-07 21:45:02

+0

鏈接重複:[如何解決可變宏與MSVC++(Microsoft Visual Studio)中的「宏重載」相關的問題?](https://stackoverflow.com/q/48710758/514235) - @JesseGood Thx用於指出錯誤。 – iammilind 2018-02-10 04:43:38

回答

17

巧合的是,我恰好在今天遇到了這個問題,經過足夠的努力,我認爲我已經找到了一個解決方案來實現我自己的目的。該錯誤是MSVC將__VA_ARGS__視爲參數列表中的單個標記。但是你可以通過不在宏調用參數列表中直接使用它來解決這個問題。 This comment暗示答案的開始你的問題:

#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)說。我發現我的代碼(看起來像你的代碼)必須改爲將所有作爲一個單元擴展爲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

我知道這個問題已經過了兩歲了,但我想我會試着給那些仍然偶然發現的人提供一個更加完美的答案,就像我一樣。

Jeff Walden的答案很有用,但是您必須爲每個想要具有可變參數的FOO宏聲明FOO_CHOOSE_HELPER/1/2。我開發了一個抽象層來解決這個問題。考慮以下幾點:

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

在此架構下,你可以定義複雜的宏這樣:

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

它不是什麼大不了的,但是我喜歡我的代碼儘可能簡潔。如果您使用多個可變宏,它也會成倍地幫助您減少代碼重複以及可能導致的複雜情況。據我所知,這種方法也是可移植的。我已經在許多最常用的編譯器上進行了測試,並且它們產生了相同的結果。

實施例使用:

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')';'令牌「與gcc4.9 – ideasman42 2014-07-19 03:57:52

+0

在此基礎上,繼承人使用多達16個參數來實現基於var-args的ELEM宏的示例,http://stackoverflow.com/a/24837037/432509(可能感興趣) – ideasman42 2014-07-19 05:22:55

+0

@ ideasman42謝謝你指出,更新它。 – 2014-07-20 12:18:00