макрос foreach values ​​в gcc и cpp - PullRequest
       19

макрос foreach values ​​в gcc и cpp

12 голосов
/ 17 сентября 2008

У меня есть макрос 'foreach', который я часто использую в 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 голосов
/ 17 сентября 2008

Вы бы искали BOOST_FOREACH - они уже сделали всю работу за вас!

Если вы хотите свернуть свой собственный, вы можете объявить блок где-нибудь в C ++, что решает вашу проблему с вашей промежуточной памятью itr-> second ...

// 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
}
4 голосов
/ 17 сентября 2008

Вы можете сделать это, используя два цикла. Первый объявляет итератор с именем, которое является функцией переменной контейнера (и вы можете сделать это более уродливым, если вас беспокоит конфликт с вашим собственным кодом). Второй объявляет значение переменной.

#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 (контейнер), но я думаю, что это делает макрос более читабельным.

3 голосов
/ 17 сентября 2008

Функция 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"
}

В качестве альтернативы есть также функция накопления , которая позволяет сохранять некоторые значения между вызовами объекта функции. накопление не изменяет данные во входном контейнере, как в случае с transform .

1 голос
/ 31 августа 2011

Этот вопрос состоит из двух частей. Вам нужно как-то (1) сгенерировать итератор (или, скорее, итеративную последовательность) над значениями вашей карты (не ключами), и (2) использовать макрос для выполнения итерации без большого количества шаблонов.

Самое чистое решение - использовать Boost Range Adapter для части (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
1 голос
/ 18 ноября 2010

Я создал небольшой помощник Foreach.h с несколькими вариантами 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

My Foreach.h предоставляет следующие макросы:

  • foreach () - обычный foreach для указателей
  • foreach_ () - обычный foreach для локальных переменных
  • foreachdel () - версия foreach с проверками на удаление в цикле, версия указателя
  • foreachdel_ () - версия foreach с проверками на удаление в цикле, версия локальной переменной

Они, конечно, работают на меня, я надеюсь, что они также сделают вашу жизнь немного проще:)

1 голос
/ 17 сентября 2008

Boost :: For_each - безусловно, ваш лучший выбор. Отличная вещь заключается в том, что они фактически дают вам макрос BOOST_FOREACH (), который вы можете затем обернуть и #define к тому, что вы действительно хотите вызвать в своем коде. Большинство всех выберет старый добрый «foreach», но в других магазинах могут быть другие стандарты кодирования, так что это соответствует этому образу мышления. У Boost также есть много других полезностей для разработчиков на C ++! Хорошо стоит использовать.

1 голос
/ 17 сентября 2008

Задумывались ли вы об использовании Boost библиотеки ? В них реализован макрос foreach , который, вероятно, более надежен, чем все, что вы напишете ... а также есть transform_iterator, который может использоваться. сделать часть извлечения того, что вы хотите.

К сожалению, я не могу точно сказать вам , как использовать его, потому что я недостаточно знаю C ++ :) Этот поиск в Google дает некоторые многообещающие ответы: comp .lang.c ++. moderated , Повышение сценария использования transform_iterator .

0 голосов
/ 02 февраля 2017
#define zforeach(var, container) for(auto var = (container).begin(); var != (container).end(); ++var)

typeof () не существует, поэтому вы можете использовать это:

decltype((container).begin()) var 
decltype(container)::iterator var
0 голосов
/ 15 августа 2012

Я реализовал свой 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";
}
0 голосов
/ 17 сентября 2008
#define foreach(var, container) for (typeof((container).begin()) var = (container).begin(); var != (container).end(); ++var)

Там нет typeof в C ++ ... как это для вас? (это конечно не портативный)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...