Question

J'ai une macro 'foreach' que j'utilise fréquemment en C ++ et qui fonctionne pour la plupart des conteneurs STL:

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

(Notez que 'typeof' est une extension de gcc.) Il est utilisé comme ceci:

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

Je voudrais faire quelque chose de similaire qui itère sur les valeurs d'une carte. Appelez-le "foreach_value", peut-être. Donc, au lieu d'écrire

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

j'écrirais

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

Je ne peux pas proposer de macro qui le fasse, car il faut déclarer deux variables: l'itérateur et la variable de valeur ('v', ci-dessus). Je ne sais pas comment faire cela dans l'initialiseur d'une boucle for, même en utilisant des extensions gcc. Je pourrais le déclarer juste avant l'appel de foreach_value, mais cela entrerait en conflit avec d'autres instances de la macro foreach_value de même portée. Si je pouvais suffixer le numéro de ligne actuel du nom de la variable itérateur, cela fonctionnerait, mais je ne sais pas comment faire.

Était-ce utile?

La solution

Vous pouvez le faire en utilisant deux boucles. Le premier déclare l'itérateur, avec un nom qui est une fonction de la variable conteneur (et vous pouvez le rendre plus laid si vous craignez des conflits avec votre propre code). Le second déclare la variable de valeur.

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

En utilisant la même condition de terminaison de boucle, la boucle externe ne se produit qu'une seule fois (et si vous avez de la chance, elle est optimisée). De plus, vous évitez d'appeler - & second sur l'itérateur si la carte est vide. C'est la même raison pour l'opérateur ternaire dans l'incrément de la boucle interne; à la fin, on laisse simplement var à la dernière valeur, car elle ne sera plus référencée.

Vous pouvez utiliser inline ci (conteneur), mais je pense que cela rend la macro plus lisible.

Autres conseils

Vous recherchez BOOST_FOREACH - ils ont déjà fait tout le travail pour vous!

Si vous souhaitez lancer le vôtre, vous pouvez déclarer un bloc n'importe où en C ++, ce qui résout le problème de votre portée avec votre stockage intermédiaire 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
}

La fonction STL Transformer fait également quelque chose de similaire.

Les arguments sont (dans l'ordre):

  1. Un itérateur d'entrée désignant le début d'un conteneur
  2. Un itérateur d'entrée désignant la fin du conteneur
  3. Un itérateur de sortie définissant où placer la sortie (pour une transformation sur place similaire à for-each, il suffit de passer l'itérateur d'entrée dans # 1)
  4. Une fonction unaire (objet function) à exécuter sur chaque élément

Pour un exemple très simple, vous pouvez mettre chaque caractère d'une chaîne en majuscule de:

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

Vous pouvez également utiliser le accumuler . fonction, qui permet de conserver certaines valeurs entre les appels à l’objet fonction. accumulate ne modifie pas les données du conteneur d'entrée comme c'est le cas avec transformation .

Avez-vous pensé à utiliser les bibliothèques Boost ? Ils ont une macro poureach mise en œuvre qui est probablement plus robuste que tout ce que vous allez écrire ... et il y a aussi transform_iterator qui semble pouvoir être utilisé pour effectuer la deuxième extraction de ce que vous voulez.

Malheureusement, je ne peux pas vous dire exactement comment l'utiliser, car je ne connais pas suffisamment le C ++ :) Cette recherche Google fournit des réponses prometteuses: comp.lang.c ++. modéré , Cas d'utilisation de Boost transform_iterator .

Boost :: For_each est de loin votre meilleur pari. La chose intéressante est que ce qu'ils vous donnent en réalité est la macro BOOST_FOREACH () que vous pouvez ensuite encapsuler et définir #fixez ce que vous voulez vraiment appeler dans votre code. La plupart des gens vont opter pour le bon vieux "foreach", mais d'autres magasins peuvent avoir des normes de codage différentes, donc cela correspond à cet état d'esprit. Boost a également de nombreux autres avantages pour les développeurs C ++! Vaut la peine d'être utilisé.

J'ai créé un petit utilitaire Foreach.h avec quelques variantes de foreach (), y compris les deux qui fonctionnent sur les variables locales et sur les pointeurs, ainsi qu'une version supplémentaire sécurisée contre la suppression d'éléments de la boucle. Donc, le code qui utilise mes macros a l’air sympa comme ça:

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

sortie:

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

Mon Foreach.h fournit les macros suivantes:

  • foreach () - recherche régulière pour chaque pointeur
  • foreach_ () - Foreach régulier pour les variables locales
  • foreachdel () - Version foreach avec vérification de la suppression dans la boucle, version du pointeur
  • foreachdel_ () - version foreach avec vérification de la suppression dans la boucle, version de la variable locale

Ils travaillent vraiment pour moi, j'espère qu'ils vous simplifieront également la vie :))

Cette question comporte deux parties. Vous devez en quelque sorte (1) générer un itérateur (ou plutôt une séquence itérable) sur les valeurs de votre carte (pas les clés), et (2) utiliser une macro pour effectuer l'itération sans beaucoup de passe-partout .

La solution la plus propre consiste à utiliser un Boost Plage Adaptateur pour la partie (1) et Boost Foreach pour la partie (2). Vous n'avez pas besoin d'écrire la macro ou d'implémenter l'itérateur vous-même.

#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

Vous pouvez définir une classe de modèle prenant le type de mymap en tant que paramètre de modèle et agissant comme un itérateur sur les valeurs en surchargeant * et - >.

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

Il n'y a pas de typeof en C ++ ... comment est-ce que cette compilation est faite pour vous? (ce n'est certainement pas portable)

J'ai mis en œuvre mon propre valeur_foreach_ en fonction du code 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)        

Par exemple, vous pouvez l'utiliser dans votre code comme ceci:

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

il n'y a pas de typeof (), vous pouvez donc utiliser ceci:

decltype((container).begin()) var 
decltype(container)::iterator var
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top