Frage

Ich habe eine 'foreach' Makro ich häufig in C ++ verwenden, die für die meisten STL-Containern funktioniert:

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

(Beachten Sie, dass 'typeof' ist eine gcc-Erweiterung.) Es wird wie folgt verwendet:

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

Ich möchte etwas ähnliches, dass iteriert über eine Karte Werte machen. Nennen Sie es „foreach_value“ vielleicht. Anstatt also das Schreiben

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

Ich würde schreiben

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

Ich kann nicht mit einem Makro kommen, die dies tun wird, weil es zwei Variablen erfordert erklärt: Iterator und der Wert-Variable ( ‚v‘, oben). Ich weiß nicht, wie das für die Schleife eines im Initialisierer zu tun, auch gcc-Erweiterungen verwenden. Ich konnte es erklärt kurz vor dem foreach_value Anruf, aber dann wird es mit anderen Instanzen des foreach_value Makro im gleichen Umfang in Konflikt geraten. Wenn ich die aktuelle Zeilennummer an der Iteratorvariable Namensuffix könnte, würde es funktionieren, aber ich weiß nicht, wie das zu tun.

War es hilfreich?

Lösung

Sie können dies mit zwei Schleifen tun. Der erste erklärt den Iterator, mit einem Namen, der eine Funktion des Behälters variabel ist (und Sie können diese hässlicher machen, wenn Sie über Konflikte mit Ihrem eigenen Code Sorgen machen). Die zweite erklärt den Wert variabel ist.

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

die gleiche Schleifenabbruchbedingung Durch die Verwendung der äußeren Schleife geschieht nur einmal (und wenn man Glück hat, bekommt wegoptimiert). Auch vermeiden Sie rufen -> Sekunde auf dem Iterator, wenn die Karte leer ist. Das ist der gleiche Grund für den ternären Operator in der Erhöhung der inneren Schleife; am Ende verlassen wir nur var auf dem letzten Wert, da es nicht wieder verwiesen wird.

Sie können Inline-ci (Container), aber ich denke, es ist das Makro besser lesbar macht.

Andere Tipps

Sie haben Interesse BOOST_FOREACH - sie haben schon für Sie die ganze Arbeit getan!

Wenn Sie möchten, dass Ihre eigene Rolle, können Sie einen Block überall in C ++ deklarieren, die mit Zwischenlagerung von itr- Ihr Anwendungsbereich Problem behebt> 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
}

Die STL verwandeln Funktion auch tut sowas ähnliches.

Die Argumente sind (in der Reihenfolge):

  1. Ein InputIterator den Beginn eines Container Bezeichnung
  2. Ein InputIterator das Ende des Behälters Bezeichnen
  3. Ein Ausgang Iterator definieren, wo die Ausgabe setzen (für eine in-Place-Transformation, ähnlich wie für die-jeweils nur die in die Eingangs Iterator # 1 pass)
  4. A einstellige Funktion (Funktionsobjekt) auf jedes Element auszuführen

Für ein sehr einfaches Beispiel, jedes Zeichen in einer Zeichenkette nutzen könnte durch:

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

Alternativ gibt es auch die accumulate Funktion, die einige Werte beibehalten werden zwischen Anrufen an die Funktion Objekt ermöglicht. accumulate die Daten nicht in den Eingangsbehälter modifizieren, wie es der Fall mit Transformation .

Haben Sie daran gedacht, die Bibliotheken Auftrieb? Sie haben eine foreach Makro implementiert die wahrscheinlich mehr robuster als alles werde schreiben Sie ... und es gibt auch Das Google-Suche einige vielversprechende Antworten auftaucht: comp.lang.c ++. moderierte , Boost-transform_iterator Use Case .

boost :: for_each ist bei weitem die beste Wahl. Die nette Sache ist, dass das, was sie tatsächlich geben, ist das Makro BOOST_FOREACH (), die Sie dann zu, was auch immer wickeln und #define kann man wirklich nennen es in Ihrem Code möchten. Fast jeder wird für das gute alte „foreach“ entscheiden, aber auch andere Geschäfte verschiedene Codierungsstandards haben können, so das paßt mit dieser Einstellung. Boost hat auch viele andere Leckereien für C ++ Entwickler! Nun lohnt sich der Einsatz.

habe ich ein wenig Foreach.h Helfer mit einigen Varianten von foreach () einschließlich der beiden Einsen auf die lokalen Variablen und Zeiger, mit auch eine extra Version gesichert gegen Bedienelemente aus Schleife zu löschen. So ist der Code, der meine Makros verwendet sieht schön und gemütlich wie folgt aus:

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

Ausgabe:

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

Meine Foreach.h bietet folgende Makros:

  • foreach () - regelmäßige foreach für Zeiger
  • foreach_ () - regelmäßige foreach für lokale Variablen
  • foreachdel () - foreach Version mit Kontrollen für das Löschen innerhalb der Schleife, Zeiger-Version
  • foreachdel_ () - foreach Version mit Kontrollen für das Löschen innerhalb der Schleife, lokale Variable Version

Sie sicher für mich arbeiten, ich hoffe, sie wird auch Ihr Leben ein bisschen leichter machen:)

Es gibt zwei Teile auf diese Frage. Sie müssen irgendwie (1) einen Iterator erzeugen (oder besser gesagt, eine iterable Sequenz) über Sie die Wert Karte (keine Schlüssel), und (2) einen Makro verwenden, ohne viel vorformulierten die Iteration zu tun .

Die sauberste Lösung ist ein Boost zu verwenden Bereich Adapter für einen Teil (1) und Erhöhung Foreach für einen Teil (2). Sie brauchen nicht das Makro oder implementieren den Iterator selbst zu schreiben.

#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

Sie können eine Template-Klasse definieren, die die Art der mymap als Template-Parameter nimmt, und verhält sich wie ein Iterator über die Werte, die durch Überlastung * und ->.

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

Es gibt keinen typeof in C ++ ... wie ist das Compilieren für Sie? (Es ist sicherlich nicht tragbar)

I umgesetzt meine eigene foreach_value basierend auf dem Boost foreach Code:

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

Zum Beispiel, können Sie es in Ihrem Code verwenden:

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

gibt es keine typeof (), so können Sie diese verwenden:

decltype((container).begin()) var 
decltype(container)::iterator var
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top