2008-09-17 13 views
11

que tienen una macro 'foreach' utilizo con frecuencia en C++ que funciona para la mayoría de los contenedores STL:"valores foreach" macro en gcc y CPP

#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. Así que en lugar de escribir

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

me gustaría escribir

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

no puedo llegar a una macro que hacer esto, ya que 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.

Respuesta

4

Puede hacerlo utilizando 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) 

Mediante el uso de la misma condición de terminación del bucle, el bucle externo sólo ocurre una vez (y si tienes suerte, se ha optimizado de distancia). 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.

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

+0

¡Perfecto! Pero no entiendo lo de hacer un objeto extra. var es un puntero. ¿Tuviste una versión anterior que usó un valor? Y var podría convertirse en una referencia con otro ciclo más (más interno): for (typeof (...) var = * ptr; ptr; ptr = NULL). ¿Te importa actualizar con eso? – sfink

0

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

8

Usted estaría buscando BOOST_FOREACH - ya han hecho todo el trabajo por usted!

Si desea rodar su propia, se puede declarar un bloque en cualquier lugar en C++, que resuelve su problema de alcance con su almacenamiento temporal de los itr-> segunda ...

// 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

Gracias por el enlace; ese es probablemente un buen reemplazo para el original. La principal diferencia es que requiere declarar el tipo de la variable de bucle, lo que está bien a veces pero es problemático para los tipos peludos. Su respuesta actualizada no funciona; declara dos variables. No compilará – sfink

+0

¡Es cierto! Siempre puede usar un std :: pair para almacenar los dos valores de bucle o usar el alcance en su macro. Voy a quitar el código erróneo de mi puesto ( para (ITR = itr_type empezar, value_type val = itr-> segundos;! ITR = final; ++ ITR, val = itr-> segundos) ) El –

+0

el par no funciona porque todavía necesito la variable plain declarada también. El ámbito no funciona porque cualquier ámbito creado en la macro es local para la macro, no el cuerpo de for(), o requiere una macro foreach_end coincidente para cerrar un ámbito. (O un simple '}', pero ¡puaj!) – sfink

1

¿Ha pensado de usar el Boost libraries? Tienen un foreach macro implemented que es probablemente más robusto que cualquier cosa que escribas ... y también hay transform_iterator que parecen ser capaces de usarse para hacer la segunda extracción de lo que deseas.

Lamentablemente no puedo decir exactamente cómo de usar porque no sé lo suficiente C++ :) This Google search vueltas hasta algunas respuestas prometedoras: comp.lang.c++.moderated, Boost transform_iterator use case.

+0

Creo que puede tener razón sobre el transform_iterator, pero tendré que jugar con él para estar seguro. (Y sí, ya estoy usando varios bits de impulso, aunque estoy familiarizado con solo una fracción de lo que ofrece). – sfink

3

La función STL transform 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 extremo del recipiente
  3. Un iterador de salida que define dónde colocar el de salida (para una en el lugar de transformación, similar a la para-cada uno, sólo tiene que pasar el iterador la entrada en # 1)
  4. una función unaria (función de objeto) para realizar en cada elemento

Para un ejemplo muy sencillo, que pudo capitalizar cada carácter en una cadena por:

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

Como alternativa existe también la función accumulate, que permite a algunos valores que deben conservarse entre las llamadas al objeto función. acumular no modifica los datos en el contenedor de entrada como es el caso con la transformada .

1

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

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

No hay ningún tipo de en C++ ... ¿cómo es esto compilación para usted? (Ciertamente no es portátil)

+0

Es por eso que mencioné "gcc" en el título, es una extensión de gcc. Una muy útil. El estándar C++ eventualmente adquirirá la palabra clave 'auto' (o mejor dicho, un nuevo significado para esa palabra clave) que asumirá la mayor parte de lo que uso typeof. Pero actualizaré la pregunta para señalar mejor el gcc-ismo. – sfink

1

Creé un pequeño asistente 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 . Por lo que el código que utiliza mis macros parece agradable y acogedor como esto:

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

de salida:

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

Mis Foreach.h proporciona macros siguientes:

  • foreach() - foreach regular para los punteros
  • foreach_() - foreach regular para las variables locales
  • foreachdel() - versión foreach con cheques por deleción dentro del bucle, la versión puntero
  • foreachdel_() - versión foreach con los cheques para su eliminación dentro del bucle, la versión local variables

Seguro que funcionan para mí, espero también harán que su vida sea un poco más fácil :)

+0

Gracias por el foreach, pero el foreachdel no compiló para mí: error: no se pudo convertir 'it_bup' en 'bool' – stephenmm

1

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

La solución más limpia es usar un Boost RangeAdaptor 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 
0

implementé mi propia foreach_value basado en el código 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)   

Por ejemplo, puede utilizarlo en su código como este:

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

existe no es typeof() para que pueda usar esto:

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