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.
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):
- Ein InputIterator den Beginn eines Container Bezeichnung
- Ein InputIterator das Ende des Behälters Bezeichnen
- 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)
- 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