Макрос "foreach values" в gcc & cpp
У меня есть макрос "foreach", который я часто использую в С++, который работает для большинства контейнеров 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 в той же области. Если бы я мог суффикс текущего номера строки для имени переменной итератора, это сработало бы, но я не знаю, как это сделать.
Ответы
Ответ 1
Вы можете сделать это, используя две петли. Первый объявляет итератор с именем, которое является функцией переменной контейнера (и вы можете сделать это более уродливым, если беспокоитесь о конфликтах с вашим собственным кодом). Второй объявляет переменную значения.
#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)
Используя одно и то же условие завершения цикла, внешний цикл выполняется только один раз (и если вам повезет, его оптимизируют). Кроме того, вы избегаете вызова → секунды на итераторе, если карта пуста. Это та же причина для тройного оператора в приращении внутреннего цикла; в конце мы просто оставляем var при последнем значении, так как он не будет ссылаться снова.
Вы можете встроить ci (контейнер), но я думаю, что это делает макрос более удобочитаемым.
Ответ 2
Вы бы искали BOOST_FOREACH - они уже сделали для вас всю работу!
Если вы хотите сворачивать свои собственные, вы можете объявить блок в любом месте на С++, что устраняет проблему с вашей промежуточной памятью 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
}
Ответ 3
Функция STL transform также делает что-то подобное.
Аргументы (в порядке):
- Итератор ввода, обозначающий начало контейнера
- Итератор ввода, обозначающий конец контейнера
- Итератор вывода, определяющий, куда помещать вывод (для преобразования на месте, похожего на for-each, просто передайте входной итератор в # 1)
- Унарная функция (функциональный объект) для каждого элемента
Для очень простого примера вы можете загладить каждый символ в строке:
#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, которая позволяет сохранять некоторые значения между вызовами объекта функции. accumulate не изменяет данные во входном контейнере, как в случае с преобразованием.
Ответ 4
Думали ли вы об использовании Boost libraries? У них есть макрос foreach
, который, вероятно, более устойчив, чем все, что вы напишете... и есть также transform_iterator
, который, как представляется, может быть использован для выполнения второй части извлечения того, что вы хотите.
К сожалению, я не могу точно сказать вам, как его использовать, потому что я недостаточно знаю С++:) Этот поиск Google появляется некоторые многообещающие ответы: comp.lang.c++.moderated, Пример использования преобразователя-преобразователя.
Ответ 5
Boost:: For_each - это, безусловно, лучший выбор. Самое замечательное в том, что то, что они на самом деле дают вам, это макрос BOOST_FOREACH(), который вы можете затем обернуть и #define, чтобы вы действительно хотели его называть в своем коде. Большинство из них предпочтут добрый старый "foreach", но в других магазинах могут быть разные стандарты кодирования, поэтому это соответствует такому мышлению. Boost также имеет множество других полезных свойств для разработчиков на С++! Хорошо стоит использовать.
Ответ 6
Я создал небольшой помощник 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 с проверками на удаление в цикле, локальная версия переменной
Они наверняка работают для меня, я надеюсь, что они также облегчат вашу жизнь:)
Ответ 7
В этом вопросе есть две части. Вам нужно как-то (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
Вы можете определить класс шаблона, который принимает тип mymap в качестве параметра шаблона и действует как итератор над значениями путем перегрузки * и → .
Ответ 9
#define foreach(var, container) for (typeof((container).begin()) var = (container).begin(); var != (container).end(); ++var)
В С++ нет типаof... как это компилируется для вас? (это, конечно, не переносимо)
Ответ 10
Я внедрил свой собственный 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
#define zforeach(var, container) for(auto var = (container).begin(); var != (container).end(); ++var)
нет typeof(), поэтому вы можете использовать это:
decltype((container).begin()) var
decltype(container)::iterator var