代码之家  ›  专栏  ›  技术社区  ›  sfink

GCC和CPP中的“foreach values”宏

  •  12
  • sfink  · 技术社区  · 17 年前

    我有一个“Frach”宏,我经常在C++中使用,它适用于大多数STL容器:

    #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宏的其他实例冲突。如果我可以在当前行号后面加上迭代器变量名的后缀,它会起作用,但我不知道该如何做。

    11 回复  |  直到 8 年前
        1
  •  4
  •   archbishop    17 年前

    您可以使用两个循环来完成此操作。第一个声明迭代器,其名称是容器变量的函数(如果担心与自己的代码冲突,可以使其更丑)。第二个声明值变量。

    #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(容器),但我认为它使宏更具可读性。

        2
  •  8
  •   Thomas Eding    13 年前

    你会找的 BOOST_FOREACH -他们已经为你做了所有的工作!

    如果你确实想自己滚动,你可以在C++中的任何地方声明一个块,它解决了你的IRT-GT;第二个中间存储的范围问题。 …

    // 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
    }
    
        3
  •  3
  •   Zachary Garrett    17 年前

    STL transform 函数也做类似的事情。

    参数是(按顺序):

    1. 指定容器开头的输入迭代器。
    2. 指定容器结尾的输入迭代器。
    3. 定义输出放在哪里的输出迭代器(对于就地转换,类似于对每个转换,只需在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 函数,它允许在对函数对象的调用之间保留一些值。 积累 不修改输入容器中的数据 转型 .

        4
  •  1
  •   porges    17 年前

    你有没有想过用 Boost libraries ?他们有一个 foreach macro implemented 这可能比你要写的任何东西都强大…还有 transform_iterator 它似乎可以用来做你想要的第二个提取部分。

    不幸的是,我不能准确地告诉你 怎样 使用它,因为我不知道足够的C++: This Google search 找到了一些有希望的答案: comp.lang.c++.moderated , Boost transform_iterator use case .

        5
  •  1
  •   Mark Kegel    17 年前

    对于你来说,每个人都是你最好的选择。漂亮的是,它们实际上提供给您的是宏boost foreach(),然后您可以包装它,并定义为您在代码中真正想要调用的任何东西。大多数人都会选择好的“foreach”,但其他商店可能有不同的编码标准,因此这符合这种心态。Boost还为C++开发人员提供了很多其他的东西!很值得使用。

        6
  •  1
  •   Artur Sowiński    14 年前

    我用foreach()的一些变体创建了一个小foreach.h助手,其中包括在局部变量和指针上操作的两个变量,另外还有一个额外的版本可以防止从循环中删除元素。因此,使用我的宏的代码看起来很好也很舒适,如下所示:

    #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版本,局部变量版本

    他们确实为我工作,我希望他们也能让你的生活更轻松一点。)

        7
  •  1
  •   Stuart Berg    14 年前

    这个问题有两部分。您需要以某种方式(1)在映射的上生成一个迭代器(或者更确切地说,一个可迭代的序列)。 价值观 (不是键),和(2)使用宏进行迭代,而不需要大量样板文件。

    最干净的解决方案是使用 Boost Range Adaptor 对于第(1)部分和 Boost Foreach 部分(2)。您不需要编写宏或自己实现迭代器。

    #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
    
        8
  •  0
  •   Tyler    17 年前

    您可以定义一个模板类,该类将mymap类型作为模板参数,并通过重载*和->对值执行迭代器的操作。

        9
  •  0
  •   Assaf Lavie    17 年前
    #define foreach(var, container) for (typeof((container).begin()) var = (container).begin(); var != (container).end(); ++var)
    

    C++中没有类型…这是如何为您编译的?(当然不是便携式的)

        10
  •  0
  •   Sergey K.    13 年前

    我实现了我自己的 foreach_value 基于 Boost foreach 代码:

    #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";
    }
    
        11
  •  0
  •   Zoltán Horváth    8 年前
    #define zforeach(var, container) for(auto var = (container).begin(); var != (container).end(); ++var)
    

    没有typeof(),因此可以使用:

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