Pregunta

Tengo una macro 'foreach' que uso frecuentemente en C++ y 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 repita los valores de un mapa.Llámelo "foreach_value", quizás.Entonces en lugar de escribir

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

Yo escribiría

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

No se me ocurre 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 bucle 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 agregar el número de línea actual al nombre de la variable del iterador, funcionaría, pero no sé cómo hacerlo.

¿Fue útil?

Solución

Puedes hacer esto usando dos bucles.El primero declara el iterador, con un nombre que es una función de la variable contenedora (y puedes hacerlo 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 utilizar la misma condición de terminación del bucle, el bucle externo solo ocurre una vez (y, si tiene suerte, se optimiza).Además, evita llamar a ->segundo en el iterador si el mapa está vacío.Esa es la misma razón para el operador ternario en el incremento del bucle interno;al final, simplemente dejamos var en el último valor, ya que no se hará referencia a él nuevamente.

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

Otros consejos

Estarías buscando BOOST_FOREACH - ¡Ya han hecho todo el trabajo por ti!

Si desea rodar el suyo, puede declarar un bloque en cualquier lugar de C ++, que resuelve su problema de alcance con su almacenamiento intermedio de ITR-> Segundo ...

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

El STL transformar La función 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 in situ, similar a for-each, simplemente pase el iterador de entrada en el n.° 1)
  4. Una función unaria (objeto de función) para realizar en cada elemento

Para un ejemplo muy simple, podrías poner en mayúscula 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 el acumular función, que permite conservar algunos valores entre llamadas al objeto de función. acumular no modifica los datos en el contenedor de entrada como es el caso con transformar.

¿Has pensado en utilizar el Impulsar bibliotecas?Ellos tienen un foreach macro implementada que probablemente sea más sólido que cualquier cosa que escribas...y también hay transform_iterator que parecería poder usarse para hacer la segunda parte de extracción de lo que desea.

Lamentablemente no puedo decirte exactamente cómo usarlo porque no sé suficiente C++ :) Esta búsqueda de Google presenta algunas respuestas prometedoras: comp.lang.c++.moderado, Impulsar el caso de uso de transform_iterator.

Boost::For_each es, con diferencia, tu mejor opción.Lo interesante es que lo que realmente te dan es la macro BOOST_FOREACH() que luego puedes ajustar y #definir como realmente quieras llamarla en tu código.Casi todo el mundo optará por el viejo "foreach", pero otras tiendas pueden tener diferentes estándares de codificación, por lo que esto encaja con esa mentalidad.¡Boost también tiene muchas otras ventajas para los desarrolladores de C++!Vale la pena usarlo.

Creé un pequeño asistente Foreach.h con algunas variantes de foreach(), incluidas las que operan en las variables locales y en los punteros, con también una versión adicional protegida contra la eliminación de elementos dentro del bucle.Entonces, el código que usa mis macros se ve bonito 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;
}

producción:

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

Mi Foreach.h proporciona las siguientes macros:

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

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

Esta pregunta tiene dos partes.Necesita de alguna manera (1) generar un iterador (o más bien, una secuencia iterable) sobre su mapa. valores (no claves) y (2) usar una macro para realizar la iteración sin muchos textos repetitivos.

La solución más limpia es utilizar un Rango de impulso Adaptador para la parte (1) y Impulsar Foreach para la parte (2).No es necesario escribir la macro ni 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

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

#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 te está compilando esto?(ciertamente no es portátil)

Implementé el mío propio foreach_value basado en el Boost foreach código:

#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, puedes usarlo en tu 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 existe typeof() así que puedes usar esto:

decltype((container).begin()) var 
decltype(container)::iterator var
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top