Domanda

Ho un 'foreach' macro che uso frequentemente in C++ che funziona per la maggior parte dei contenitori STL:

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

(Si noti che la "typeof" è un gcc estensione.) Esso è utilizzato come questa:

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

Vorrei fare qualcosa di simile che scorre su una mappa di valori.La chiamano "foreach_value", forse.Quindi invece di scrivere

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

Mi permetto di scrivere

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

Io non posso venire con una macro che consente di fare questo, perché non è necessario dichiarare due variabili:l'iteratore e il valore della variabile ('v', qui sopra).Non so come farlo in inizializzatore di un ciclo for, anche utilizzando le estensioni del gcc.Potrei dichiarare subito prima del foreach_value chiamata, ma poi non è in conflitto con le altre istanze della foreach_value macro nello stesso ambito.Se potessi suffisso il numero di riga corrente per la variabile di iterazione nome, che avrebbe funzionato, ma non so come fare.

È stato utile?

Soluzione

È possibile farlo utilizzando due cicli.Il primo dichiara che l'iteratore, con un nome che è una funzione della variabile del contenitore (e questo si può fare di più brutto se siete preoccupati per i conflitti con il proprio codice).Il secondo dichiara che il valore della variabile.

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

Utilizzando lo stesso circuito, a condizione risolutiva, il ciclo esterno si verifica solo una volta (e se siete fortunati, viene ottimizzato di distanza).Inoltre, evitare di chiamare ->secondo l'iteratore se la mappa è vuota.Che è la stessa ragione per l'operatore ternario in incremento del ciclo interno;alla fine, dobbiamo solo lasciare var all'ultimo valore, in quanto non sarà fatto riferimento nuovamente.

Si potrebbe inline ci(contenitore), ma penso che rende la macro più leggibile.

Altri suggerimenti

Si potrebbe essere alla ricerca di BOOST_FOREACH - hanno fatto tutto il lavoro per voi già!

Se volete rotolare il vostro proprio, è possibile dichiarare un blocco ovunque in C++, che risolve il problema di ambito con il deposito intermedio di itr->secondo ...

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

Il STL trasformare la funzione, inoltre, fa qualcosa di simile.

Gli argomenti sono (in ordine):

  1. Un iteratore di input che designa l'inizio di un contenitore
  2. Un iteratore di input che designa la fine del contenitore
  3. Un iteratore uscita definire dove mettere l'uscita (per un posto di trasformazione, simili a for-each, basta passare l'input iteratore in #1)
  4. Una funzione unario (funzione di oggetto) per eseguire su ogni elemento

Per un esempio molto semplice, si poteva capitalizzare ogni carattere in una stringa:

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

In alternativa c'è anche il accumulare la funzione, che permette ad alcuni valori per essere mantenuto tra le chiamate alla funzione oggetto. accumulare non modificare i dati di input, di contenitore, come è il caso con trasformare.

Hai pensato di usare il Librerie Boost?Hanno un foreach macro implementato che è probabilmente il più robusto di quello che scriverò...e c'è anche transform_iterator che sembrerebbe essere in grado di essere utilizzato per fare il secondo di estrazione di una parte di ciò che si desidera.

Purtroppo non posso dirvi esattamente come usarlo perché non conosco abbastanza il C++ :) Questa ricerca di Google si trasforma in alcuni promettenti risposte: comp.lang.c++.moderatore, Boost transform_iterator caso di utilizzo.

Boost::For_each è la vostra scommessa migliore.La bella cosa è che essi effettivamente dare è la macro BOOST_FOREACH() che si può avvolgere e #define a tutto ciò che si vorrebbe chiamare in codice.La maggior parte tutti possono optare per il buon vecchio "foreach", ma altri negozi possono avere diversi standard di codifica, in modo che si adatta con la mentalità che.Boost ha anche un sacco di altre chicche per gli sviluppatori C++!Vale la pena utilizzare.

Ho creato un po ' Foreach.h helper con un paio di varianti di foreach() di cui sia quelli che operano su variabili locali e sui puntatori, con anche un extra versione protetta contro l'eliminazione di elementi all'interno del loop.Quindi il codice che usa il mio macro sembra piacevole e accogliente come questo:

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

output:

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

Il mio Foreach.h fornisce la seguente macro:

  • foreach() - regolare foreach per i puntatori
  • foreach_() - regolare foreach per le variabili locali
  • foreachdel() - foreach versione con controlli per l'eliminazione all'interno del loop, puntatore versione
  • foreachdel_() - foreach versione con controlli per l'eliminazione entro ciclo, la variabile locale versione

Sono sicuro di fare un lavoro per me, spero che lo sarà anche rendere la vostra vita un po ' più facile :)

Ci sono due parti di questa domanda.In qualche modo bisogna (1) generare un iteratore (o meglio, un iterable sequenza) su di voi mappa valori (non keys), e (2) utilizzare una macro per eseguire l'iterazione senza un sacco di boilerplate.

La soluzione più pulita è quello di utilizzare un Aumentare La Gamma Di Adattatore per la parte (1) e Boost Foreach per la parte (2).Non c'è bisogno di scrivere la macro o implementare l'iteratore di te.

#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

Si potrebbe definire un modello di classe che ha il tipo di mymap come un parametro di modello, e agisce come un iteratore su valori da sovraccarico * e ->.

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

Non c'è typeof in C++...come è questa la compilazione per te?(certamente non portatile)

Ho implementato la mia foreach_value basato sul Boost foreach codice:

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

Ad esempio, è possibile utilizzare nel vostro codice come questo:

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

non c'è typeof() in modo che è possibile utilizzare questo:

decltype((container).begin()) var 
decltype(container)::iterator var
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top