2012-07-31 34 views
3

爲了引導這個問題:我一直在實現各種C++實用程序函數和(當我必須)爲更大的工具包使用自己的宏。最近我一直在基於BOOST_FOREACH創建各種循環宏以及可迭代的有意識的函數。基於Boost foreach實現enumerate_foreach

長話短說,我遇到了困難,編寫了一個枚舉循環,它使用了BOOST_FOREACH,但是傳遞了一個附加參數,每循環迭代一次就增加一個參數。這與Python中的枚舉命令非常相似,在循環遍歷任意迭代器時無需添加大量的鍋爐代碼,我發現它非常有用。宏需要像BOOST_FOREACH形式:

ENUMERATE_FOREACH(COUNT, VAR, COL) 

,並會有效地做到這一點,但不依賴於放置不平衡「}」關閉(或其他循環結束宏):

COUNT = 0; BOOST_FOREACH(VAR, COL) { COUNT++; ...Rest of loop... 

現在擺在我得到了試圖在C++中創建一個非C++運算符的指責,我完全意識到這是將一個外部概念困在C++中,而我並沒有嘗試Pythonize C++代碼。我只是很好奇,是否可以用已知的工具集/ boost來實現這樣一個循環,而且沒有極端的依賴關係。有了這樣一個宏定義將消除某些循環類型的錯誤的可能來源,因爲我需要重複計算循環,並消除了COUNT變量的用途。

我想過在COL變量進入BOOST_FOREACH之前做一個模板包裝器,但COL的可能性數量使得可迭代變量的一些組合難以/不可能實現,而無需製作不同版本的ENUMERATE_FOREACH並重新實現很多BOOST_FOREACH - 沒有大量測試/時間的艱鉅和愚蠢的任務。

做一個單獨的內聯函數可以工作,但隨後循環的語法變得壞了,我更多的上,每個款式運算符函數傳遞的情況(我已經實現)。

這讓我從boost庫中獲取foreach.hpp的最後一行,並在附加參數中插入我自己的增量運算符。然後我變得依賴boost版本,並擔心新的更新(任何語法更改),以促進破壞我的hacky自定義宏。

我想最後一個選項的是做一個ENUMERATE_BEGIN和ENUMERATE_END隱藏我的迭代增量操作。這種方法比單個宏更容易出錯,因爲用戶必須放置兩個宏而不是一個 - 儘管這可能是唯一的簡單解決方案。

我試着環顧SO和其他來源,看看有人曾經嘗試過,沒有太多的運氣。希望有人已經成功地實現了這樣一個實現概念,或者有一個改變我的方法的想法。如果沒有一個乾淨的方法來做這件事,我可以繼續開始我的循環計數++,當我想數。再次,這是更多的是好奇,有人可能會提出的我狡辯約一個想法是完全合理的做法還是好,因爲它會得到。

回答

1

在閱讀Joachim的回覆後,我大部分都很滿意,但我試圖操作BOOST_FOREACH,直到我至少可以用非常冒險的方式來完成它,然後我發現我可以在不重寫整個宏的情況下以一種良性的方式實現計數器, BOOST_FOREACH_NEXT上的一些#undef #define語句。我使用BOOST_FOREACH有一個(VAR = derefence(...); ...)語句並在VAR和=之間放置一個結構的事實,以便我得到(VAR = IncrementCountAndPassRHS = derefence(...) ); ...)。

我還沒有測試太多(但)的宏觀擴展問題,但我認爲它是安全的forloop內。

編輯添加更新以修復變量作用域名稱重疊問題與同一行中的多個循環。

namespace loopnamespace { 
template<typename T> 
void incrementT(T *t) { 
    (*t)++; 
} 

struct IncrementCounterPassthrough { 
    bool checker; 
    boost::function<void(void)> incrementer; 

    template<typename Count> 
    IncrementCounterPassthrough(Count& t) { 
     t = -1; 
     checker = true; 
     incrementer = boost::bind(&incrementT<Count>, &t); 
    } 

    template<typename T> 
    T& operator=(T& rhs) { 
     incrementer(); 
     return rhs; 
    } 
}; 
} 

#define ENUMERATE_FOREACH(COUNT, VAR, COL)                 \ 
    for(::loopnamespace::IncrementCounterPassthrough BOOST_FOREACH_ID(_foreach_count_pass)(COUNT);   \ 
     BOOST_FOREACH_ID(_foreach_count_pass).checker; BOOST_FOREACH_ID(_foreach_count_pass).checker = false)\ 
    BOOST_FOREACH(VAR = BOOST_FOREACH_ID(_foreach_count_pass), COL) 

允許我做:

std::string hello("Hello, boost world!"); 
unsigned int value; 
ENUMERATE_FOREACH(value, char ch, hello) { 
    std::cout << ch << " => " << value << "\n"; 
} 

輸出:

H => 0 
e => 1 
l => 2 
l => 3 
o => 4 
, => 5 
    => 6 
b => 7 
o => 8 
o => 9 
s => 10 
t => 11 
    => 12 
w => 13 
o => 14 
r => 15 
l => 16 
d => 17 
! => 18 
4

你可以通過具有ENUMERATE_FOREACH,其中最後一個參數是一個謂詞調用四個參數版本解決這個問題。

喜歡的東西:

ENUMERATE_FOREACH(COUNT, VAR, COL, PRED) 

,它會擴展到像

{ 
    COUNT = 0; 
    BOOST_FOREACH(VAR, COL) 
    { 
     PRED(COUNT); 
     COUNT++; 
    } 
} 

有關上述的好處是,謂詞可以是一個C++ 11 lambda表達式:

ENUMERATE_FOREACH(COUNT, VAR, COL, 
        [](int count){ cout << "count = " << count << '\n'; }); 

編輯:另一種方法,就是仍然用af第四個參數,但讓該參數爲實際代碼。例如:

ENUMERATE_FOREACH(COUNT, VAR, COL, do { std::cout << COUNT << '\n'; } while (0)) 

將擴大到

{ 
    COUNT = 0; 
    BOOST_FOREACH(VAR, COL) 
    { 
     do { std::cout << COUNT << '\n'; } while (0); 
     COUNT++; 
    } 
} 

這可能是一個有點比使用例如比較凌亂C++ 11 lambda。

編輯2:如果你有C++ 11,並且可以使用lambda表達式,那麼你可能有新的range-based for loop爲好,這意味着你可以創建一個適當的函數來代替,一樣的東西:

template<typename SeqT, typename PredT> 
void for_each_count(SeqT seq, PredT pred) 
{ 
    int count = 0; 
    for (auto val : seq) 
    { 
     pred(val, count); 
     count++; 
    } 
} 

用法:

std::vector<int> v = {1, 2, 3, 4}; 
for_each_count(v, 
    [](int v, int c){ 
     std::cout << "value = " << v << ", count = " << c << '\n'; 
    }); 

上面將打印

 
value = 1, count = 0 
value = 2, count = 1 
value = 3, count = 2 
value = 4, count = 3 
+0

有趣的想法與第四個參數。它最終比我想要的更接近每個操作符 - 但是使用C++ 11 lambda函數,它比標準函數指針/ boost :: function版本更具可讀性。 – Pyrce 2012-07-31 05:49:13

+0

@Pyrce增加了另外兩種選擇。 :) – 2012-07-31 06:22:19

+0

感謝您的想法/編輯!他們讓我試圖在Count參數上做功能包裝,直到我提出我剛剛發佈的答案。如果我能夠提高更多的話,但是我認爲我提出了一些語法上的改進,以幫助我對最初的建議進行嘗試。再次感謝關於C++ 11 lambda的建議和提醒。如果我無法讓宏變得更簡單,我的後備就是使用lambda或do while循環。 – Pyrce 2012-07-31 21:20:50