ultima compiler compilador c++ gcc

c++ - compiler - Macro "foreach values" en gcc y cpp



gcc ultima version (11)

¿Has pensado en usar las bibliotecas de Boost ? Tienen una macro foreach implementada que es probablemente más robusta que cualquier cosa que escribas ... y también hay transform_iterator que parece ser capaz de usarse para hacer la segunda extracción de lo que deseas.

Lamentablemente, no puedo decirle exactamente cómo usarlo porque no conozco bastante C ++ :) Esta búsqueda de Google encuentra algunas respuestas prometedoras: comp.lang.c ++ moderado , Boost transform_iterator caso de uso .

Tengo una macro ''foreach'' que uso frecuentemente en C ++ que funciona para la mayoría de los contenedores STL:

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

(Tenga en cuenta que ''typeof'' es una extensión de gcc.) Se usa así:

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

Me gustaría hacer algo similar que itere sobre los valores de un mapa. Llámalo "foreach_value", tal vez. Entonces, en lugar de escribir

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

Yo escribiría

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

No puedo encontrar una macro que haga esto, porque requiere declarar dos variables: el iterador y la variable de valor (''v'', arriba). No sé cómo hacer eso en el inicializador de un ciclo for, incluso usando extensiones gcc. Podría declararlo justo antes de la llamada foreach_value, pero luego entrará en conflicto con otras instancias de la macro foreach_value en el mismo ámbito. Si pudiera ponerle un sufijo al número de línea actual al nombre de la variable del iterador, funcionaría, pero no sé cómo hacerlo.


Boost :: For_each es su mejor apuesta. Lo ingenioso es que lo que realmente le dan es la macro BOOST_FOREACH () que luego puede envolver y definir # a lo que realmente le gustaría llamar en su código. La mayoría optará por el "viejo foreach", pero otras tiendas pueden tener diferentes estándares de codificación, por lo que encaja con esa mentalidad. ¡Boost también tiene muchas otras ventajas para los desarrolladores de C ++! Bien vale la pena usar


La función de transformación STL también hace algo similar.

Los argumentos son (en orden):

  1. Un iterador de entrada que designa el comienzo de un contenedor
  2. Un iterador de entrada que designa el final del contenedor
  3. Un iterador de salida que define dónde colocar la salida (para una transformación local, similar a for-each, simplemente pase el iterador de entrada en # 1)
  4. Una función única (objeto de función) para realizar en cada elemento

Para un ejemplo muy simple, puedes capitalizar cada carácter en una cadena de la siguiente manera:

#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" }

Alternativamente, también existe la función de acumulación , que permite retener algunos valores entre llamadas al objeto de función. acumular no modifica los datos en el contenedor de entrada como es el caso con la transformación .


Podría definir una clase de plantilla que tome el tipo de mymap como un parámetro de plantilla, y actúe como un iterador sobre los valores al sobrecargar * y ->.


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

No hay typeof en C ++ ... ¿Cómo está compilando esto para ti? (ciertamente no es portátil)


Puedes hacer esto usando dos bucles. El primero declara el iterador, con un nombre que es una función de la variable del contenedor (y puedes hacer que esto sea más feo si te preocupan los conflictos con tu propio código). El segundo declara la variable de valor.

#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)

Al usar la misma condición de terminación de bucle, el bucle externo solo ocurre una vez (y si tiene suerte, se optimiza). Además, evitas llamar -> segundo en el iterador si el mapa está vacío. Esa es la misma razón para el operador ternario en el incremento del ciclo interno; al final, simplemente dejamos var en el último valor, ya que no se volverá a referenciar.

Podría alinear ci (contenedor), pero creo que hace que la macro sea más legible.


Creé un pequeño ayudante Foreach.h con algunas variantes de foreach () que incluyen tanto los que operan en las variables locales como en los punteros, con también una versión extra asegurada contra la eliminación de elementos dentro del ciclo. Entonces el código que usa mis macros se ve bien y acogedor así:

#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; }

salida:

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

My Foreach.h proporciona las siguientes macros:

  • foreach () - foreach regular para los indicadores
  • foreach_ () - foreach regular para variables locales
  • foreachdel () - versión de foreach con comprobaciones para su eliminación dentro del ciclo, versión del puntero
  • foreachdel_ () - versión de foreach con comprobaciones para su eliminación dentro del ciclo, versión de variable local

Seguro que funcionan para mí, espero que también te hagan la vida un poco más fácil :)


Hay dos partes en esta pregunta. Necesitas de alguna manera (1) generar un iterador (o más bien, una secuencia iterable) sobre los valores de tu mapa (no las claves), y (2) usar una macro para hacer la iteración sin una gran cantidad de texto repetitivo.

La solución más limpia es usar un adaptador Boost Range para la parte (1) y Boost Foreach para la parte (2). No necesita escribir la macro o implementar el iterador usted mismo.

#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


Estarías buscando BOOST_FOREACH : ya han hecho todo el trabajo por usted.

Si desea ejecutar el suyo propio, puede declarar un bloque en cualquier lugar en C ++, lo que resuelve el problema de alcance con su almacenamiento intermedio de 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 }


Implementé mi propio foreach_value basado en el código de 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)

Por ejemplo, puede usarlo en su código de esta manera:

#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"; }


#define zforeach(var, container) for(auto var = (container).begin(); var != (container).end(); ++var)

no hay typeof () para que pueda usar esto:

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