2008-09-17 62 views
11

我有,對於大多數STL容器一起工作了 '的foreach' 宏我用C經常使用++:「的foreach值」 宏GCC和CPP

#define foreach(var, container) \ 
    for(typeof((container).begin()) var = (container).begin(); \ 
     var != (container).end(); \ 
     ++var) 

(請注意, 'typeof運算' 是gcc的一個擴展。)它是這樣使用的:

std::vector<Blorgus> blorgi = ...; 
foreach(blorgus, blorgi) { 
    blorgus->draw(); 
} 

我想做類似的東西,迭代地圖的值。或許,稱它爲「foreach_value」。因此,而不是寫的

foreach(pair, mymap) { 
    pair->second->foo(); 
} 

我會寫

foreach_value(v, mymap) { 
    v.foo(); 
} 

我不能拿出一個宏,將做到這一點,因爲它需要聲明兩個變量:迭代器和變量的值(」 v',上面)。我不知道如何在for循環的初始化器中這樣做,即使使用gcc擴展。我可以在foreach_value調用之前聲明它,但是它會在相同範圍內與foreach_value宏的其他實例發生衝突。如果我可以將當​​前行號加後綴到迭代器變量名稱,它會起作用,但我不知道該怎麼做。

回答

4

您可以使用兩個循環來完成此操作。第一個聲明迭代器,它的名字是容器變量的函數(如果你擔心與你自己的代碼有衝突,你可以使這個醜陋的)。第二個聲明值變量。

#define ci(container) container ## iter 
#define foreach_value(var, container) \ 
    for (typeof((container).begin()) ci(container) = container.begin(); \ 
     ci(container) != container.end();) \ 
     for (typeof(ci(container)->second)* var = &ci(container)->second; \ 
      ci(container) != container.end(); \ 
      (++ci(container) != container.end()) ? \ 
       (var = &ci(container)->second) : var) 

通過使用相同的循環終止條件,外循環只發生一次(如果你幸運的話,得到優化)。此外,如果地圖爲空,則避免在迭代器上調用 - > second。這與內循環增量中的三元運算符的原因相同;最後,我們將var留在最後一個值,因爲它不會再被引用。

你可以內聯ci(容器),但我認爲它使宏更具可讀性。

+0

完美!但我不明白製作額外物體的一點意思。 var是一個指針。你有沒有使用過價值的早期版本?並且var可以作爲另一個(最內層)循環的引用:for(typeof(...)var = * ptr; ptr; ptr = NULL)。謹慎更新? – sfink 2008-09-17 17:46:27

0

您可以定義一個模板類,它將mymap的類型作爲模板參數,並通過重載*和 - >的方式充當對值的迭代器。

8

你會找BOOST_FOREACH - 他們已經爲你做了所有的工作!

如果你想推出自己的,你可以聲明在C++中的任何地方的塊,這與itr-你的中間存儲解決您的作用域的問題>第二 ...

// Valid C++ code (which does nothing useful) 
{ 
    int a = 21; // Which could be storage of your value type 
} 
// a out of scope here 
{ 
    int a = 32; // Does not conflict with a above 
} 
+0

感謝您的鏈接;這可能是原來的一個很好的替代品。主要區別在於它需要聲明循環變量的類型,這有時可以,但對於多毛類型有問題。 您更新的答案不起作用 - 它聲明瞭兩個變量。它不會編譯。 – sfink 2008-09-17 01:09:04

+0

真的! 您總是可以使用std :: pair來存儲兩個循環變量或在宏中使用範圍。我會從我的文章 移除惡意程式碼( 爲(itr_type ITR =開始,VALUE_TYPE VAL = itr->第二;! ITR =結束; ++ ITR,VAL = itr->第二) ) – 2008-09-17 02:46:39

+0

的對不起作用,因爲我仍然需要聲明的普通變量。範圍不起作用,因爲在宏中創建的任何範圍對宏而言都是本地的而不是for()的主體,或者它需要匹配的foreach_end宏來關閉範圍。 (或者是一個純粹的'}',但是yuck!) – sfink 2008-09-17 17:20:06

1

你有沒有想過使用Boost libraries?他們有一個foreach macro implemented這可能比你寫的任何東西都更強大......還有transform_iterator,它似乎可以用來做你想要的第二部分。

可惜我不能確切地告訴你如何使用它,因爲我不知道夠不夠C++ :) This Google search變成了一些有希望的答案:comp.lang.c++.moderatedBoost transform_iterator use case

+0

我認爲你對transform_iterator可能是正確的,但我必須一定要玩。 (是的,我已經使用了各種增強功能,雖然我只熟悉它提供的一小部分。) – sfink 2008-09-17 17:49:00

3

STL transform函數也做類似的事情。

的參數是(按順序):

  1. 一個輸入迭代指定容器
  2. 一個輸入迭代指定所述容器
  3. 的輸出迭代的端部限定在何處放置的開頭輸出(對於就地變換,類似於for-each,只是通過#1中的輸入迭代器)
  4. 對每個元素執行的一元函數(函數對象)

對於一個非常簡單的示例,可以通過利用每一個字符在字符串:

#include <iostream> 
#include <string> 
#include <algorithm> 
#include <cctype> 

int main(int argc, char* argv[]) { 
    std::string s("my lowercase string"); 
    std::transform(s.begin(), s.end(), s.begin(), toupper); 
    std::cout << s << std::endl; // "MY LOWERCASE STRING" 
} 

可選地還有所述accumulate函數,它允許將呼叫功能對象之間要保留一些值。 累積不會像在變換的情況那樣修改輸入容器中的數據。

1

Boost :: For_each是你迄今爲止最好的選擇。最好的事情是,他們實際給你的是宏BOOST_FOREACH(),然後你可以將它包裝起來,並#define任何你想在你的代碼中調用它的任何東西。大多數人都會選擇舊的「foreach」,但其他商店可能有不同的編碼標準,所以這符合這種思維。 Boost還爲C++開發人員提供了許多其他好東西!值得使用。

0
#define foreach(var, container) for (typeof((container).begin()) var = (container).begin(); var != (container).end(); ++var) 

C++中沒有typeof ...這是如何爲你編譯的? (它肯定不是便攜式的)

1

我創建了一個foreach.h helper,它帶有幾個foreach()函數,包括對局部變量和指針進行操作的函數,還有一個額外版本,用於防止從循環中刪除元素。因此,使用我的宏代碼看起來美觀舒適的是這樣的:

#include <cstdio> 
#include <vector> 
#include "foreach.h" 

int main() 
{ 
    // make int vector and fill it 
    vector<int> k; 
    for (int i=0; i<10; ++i) k.push_back(i); 

    // show what the upper loop filled 
    foreach_ (it, k) printf("%i ",(*it)); 
    printf("\n"); 

    // show all of the data, but get rid of 4 
    // http://en.wikipedia.org/wiki/Tetraphobia :) 
    foreachdel_ (it, k) 
    { 
     if (*it == 4) it=k.erase(it); 
     printf("%i ",(*it)); 
    } 
    printf("\n"); 

    return 0; 
} 

輸出:

0 1 2 3 4 5 6 7 8 9 
0 1 2 3 5 6 7 8 9 

Foreach.h提供以下宏:

  • 的foreach() - 普通的foreach爲指針
  • foreach_() - 針對局部變量的常規foreach
  • foreachdel() - 的foreach版本檢查循環中刪除,指針版本
  • foreachdel_() - 的foreach版本循環中勾選要刪除,局部變量版本

他們肯定不適合我的工作,我希望他們也會讓你的生活變得更輕鬆:)

1

這個問題有兩個部分。你需要以某種方式(1)在你的地圖的(不是鍵)上生成一個迭代器(或更確切地說,一個迭代序列),並且(2)使用宏來進行迭代,而不需要大量的樣板。(2)部分(1)和Boost Foreach最乾淨的解決方案是使用Boost RangeAdaptor(0124)。您不需要編寫宏或自己實現迭代器。

#include <map> 
#include <string> 
#include <boost/range/adaptor/map.hpp> 
#include <boost/foreach.hpp> 

int main() 
{ 
    // Sample data 
    std::map<int, std::string> myMap ; 
    myMap[0] = "Zero" ; 
    myMap[10] = "Ten" ; 
    myMap[20] = "Twenty" ; 

    // Loop over map values 
    BOOST_FOREACH(std::string text, myMap | boost::adaptors::map_values) 
    { 
     std::cout << text << " " ; 
    } 
} 
// Output: 
// Zero Ten Twenty 
0

我實現我自己的foreach_value基礎上,Boostforeach代碼:

#include <boost/preprocessor/cat.hpp> 
#define MUNZEKONZA_FOREACH_IN_MAP_ID(x) BOOST_PP_CAT(x, __LINE__) 

namespace munzekonza { 
namespace foreach_in_map_private { 
inline bool set_false(bool& b) { 
    b = false; 
    return false; 
} 

} 
} 

#define MUNZEKONZA_FOREACH_VALUE(value, map)         \ 
for(auto MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) = map.begin();  \ 
     MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) != map.end();)  \ 
for(bool MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) = true;  \ 
     MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) &&    \ 
     MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) != map.end();   \ 
     (MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue)) ?    \ 
     ((void)++MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it)) :   \ 
     (void)0)                \ 
    if(munzekonza::foreach_in_map_private::set_false(       \ 
      MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue))) {} else \ 
    for(value = MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it)->second;  \ 
     !MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue);    \ 
     MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) = true)   

例如,你可以用它在你的代碼是這樣的:

#define MUNZEKONZA_FOREACH_VALUE foreach_value 

std::map<int, std::string> mymap; 
// populate the map ... 

foreach_value(const std::string& value, mymap) { 
    // do something with value 
} 

// change value 
foreach_value(std::string& value, mymap) { 
    value = "hey"; 
} 
0
#define zforeach(var, container) for(auto var = (container).begin(); var != (container).end(); ++var) 

有是沒有typeof(),所以你可以使用這個:

decltype((container).begin()) var 
decltype(container)::iterator var