2011-03-17 59 views
2

我懷疑我無法做到這一點,但我想先問明智的社區。C++可變宏指定多個條件

我想檢查一些變量(比如說10個,但可能只是兩個或三個)是否等於相同的特定值。例如

if (X == 3 || Y == 3 || Z == 3 || W == 3) ... 

在Python中,我習慣於簡單地做if 3 in (X, Y, Z, W):,但無論如何。

無論如何,我想知道是否有可能將其抽象爲可變宏,如EQU_ANY(3, X, Y, Z, W)而不是編寫一堆EQU_ANYX宏,其中X是參數的數量。

+0

不,宏是錯誤的工具 - 無法遍歷宏中的參數。 –

+0

謝謝吉姆,那就是我想知道的。 (啊,如果只有我有Lisp宏..) – Kevin

+0

嗯,事實證明,我錯了 - boost :: preprocessor包有真正迂迴的方式來實現這個多達256個參數。請看喬納森和詹姆斯的答案(你真的應該接受後者)。 –

回答

4

[使用宏的解決方案是在年底,因爲它是相當可怕的,令人討厭的。]


如果你不介意的副本製成,它可能是清潔劑使用std::find

std::array<int, 4> values = { X, Y, Z, W }; 
if (std::find(values.begin(), values.end(), 3) != values.end()) { } 

這往往是不錯的這條線包裝成一個功能:

template <typename Container, typename Value> 
bool contains(const Container& c, const Value& v) 
{ 
    return std::find(c.begin(), c.end(), v) != c.end(); 
} 

使用:

std::array<int, 4> values = { X, Y, Z, W }; 
if (contains(values, 3)) { } 

在C++ 0X,可以使用,而不是創建臨時數組的初始化列表:

if (contains({ X, Y, Z, W }, 3)) { } 

(這適用於GCC 4.5+;我不知道任何其他支持此C++ 0x功能的編譯器。)


如果您確實想避免複製對象(例如,,如果他們是大或昂貴的複製),您可以使用相同的功能的間接版本:

#include <boost/iterator/indirect_iterator.hpp> 

template <typename Container, typename Value> 
bool indirect_contains(const Container& c, const Value& v) 
{ 
    return std::find(boost::make_indirect_iterator(c.begin()), 
        boost::make_indirect_iterator(c.end()), 
        v) 
     != boost::make_indirect_iterator(c.end()); 
} 

用作:

std::array<int*, 4> values = { &X, &Y, &Z, &W }; 
if (indirect_contains(values, 3)) { } 

或者,用的C++ 0x初始化列表:

if (indirect_contains({ &X, &Y, &Z, &W }, 3)) { } 

由於喬納森·萊弗勒提到Boost.Preprocessor,這裏是該解決方案將是什麼樣子:

#include <boost/preprocessor.hpp> 

#define SEQUENCE_CONTAINS_IMPL(r, data, i, elem)       \ 
    BOOST_PP_IIF(BOOST_PP_EQUAL(i, 0), BOOST_PP_EMPTY(), ||)    \ 
    ((elem) == (data)) 


#define SEQUENCE_CONTAINS(elements, value)        \ 
    (BOOST_PP_SEQ_FOR_EACH_I(SEQUENCE_CONTAINS_IMPL, value, elements)) 

用作:

if (SEQUENCE_CONTAINS((X)(Y)(Z)(W), 3)) { } 

它展開:

if ((((X) == (3)) || 
    ((Y) == (3)) || 
    ((Z) == (3)) || 
    ((W) == (3)))) { } 

(這是醜陋和可怕的;我不會在我的代碼中使用它,但是如果您真的擔心正在創建的兩個或三個值的副本,您可能不希望有機會進行函數調用。)

+0

副本是邪惡的。 = P – Kevin

+0

如果你這麼說... –

+0

Re:你的編輯,謝謝!我可能不會使用它,我同意它非常難看,但仍然很棒Boost可以做到這一點。 – Kevin

1

嘗試使用switch語句。您可以指定多個條件相同的行爲,像這樣:

switch (n) { 
    case 1: 
    case 2: 
    case 3: 
     // Do something 
     break; 
} 

這就是

if (x == 1 || x == 2 || x == 3) 
    // Do something 

相當於這就是要做到這一點最徹底的方法。你也許可以編寫一個可變開關宏(或其他可以實現這種功能的東西),但請爲你身邊的每個人......不要:P

+0

這個工作,直到我想使用除了整數或字符之外的東西。我仍然想要一個宏,因爲使用開關會導致輸入(甚至是條件!),而不是更少,並且我敢打賭,編譯器足夠聰明,可以優化開關/ gotos的那些微不足道的條件。 **編輯**等待,你的方式讓我更加困惑...... – Kevin

+0

@Kevin在一些條件下,條件是可以的,但是當你得到5或更多時,寫開關要好得多。使用== ==測試相等性不適用於字符串或浮點數IIRC,因此您的條件對於字符串或浮點數不起作用。不要擔心你輸入了多少,差別是微不足道的。擔心可讀性。 –

+0

沒有辦法編寫可變開關宏,因爲沒有辦法引用個別可變參數。預處理器的設施非常有限。 –

0

最有可能使用Boost's boost::preprocessor包(可以與C以及C++一起使用)。

+0

預處理器只允許將可變宏參數作爲一個組引用('__VA_ARGS__'),並且沒有任何提升::預處理器或任何其他軟件包可以更改該參數 - 如果要迭代參數,則必須執行它在C/C++代碼中。 –

+0

@Jim:我不同意。我發佈了一個利用預處理器的例子。做這種事情非常簡單,特別是在使用Boost.Preprocessor完成所有艱苦工作時。 –

+0

@James是的,我收回了!儘管對於任意*可變參數列表來說,boost通過爲每個參數數量定義一個單獨的宏來處理最多256個參數,並且如果有n個參數則返回true,否則返回false。我仍然試圖理解代碼,但是它非常迂迴(和巨大)。 –

1

既然您也用C標記你的問題,這是一個只適用於C99和可變宏的答案,C++沒有這個答案。使用P99做聲明展開

#define TESTIT(WHAT, X, I) X == WHAT 
#define TEST_MORE(WHAT, ...) P99_FOR(WHAT, P99_NARG(__VA_ARGS__), P00_OR, TESTIT, __VA_ARGS__) 

在這裏,這

TEST_MORE(A, b, c, d, e, f) 
TEST_MORE(3, b, c, d, e, f) 

變得那麼

((((((((b == A) || (c == A))) || (d == A))) || (e == A))) || (f == A)) 
((((((((b == 3) || (c == 3))) || (d == 3))) || (e == 3))) || (f == 3)) 

相比,以提高其優點,爲TEST_MORE最後調用簡單,可讀。

+0

+1:一個非常好的解決方案! –