Pergunta
Eu tenho uma macro 'foreach' que uso frequentemente em C++ que funciona para a maioria dos contêineres STL:
#define foreach(var, container) \
for(typeof((container).begin()) var = (container).begin(); \
var != (container).end(); \
++var)
(Observe que 'typeof' é uma extensão do gcc.) É usado assim:
std::vector< Blorgus > blorgi = ...;
foreach(blorgus, blorgi) {
blorgus->draw();
}
Eu gostaria de fazer algo semelhante que itere sobre os valores de um mapa.Chame-o de "foreach_value", talvez.Então, em vez de escrever
foreach(pair, mymap) {
pair->second->foo();
}
eu escreveria
foreach_value(v, mymap) {
v.foo();
}
Não consigo criar uma macro que faça isso, porque requer a declaração de duas variáveis:o iterador e a variável de valor ('v', acima).Não sei como fazer isso no inicializador de um loop for, mesmo usando extensões gcc.Eu poderia declará-lo logo antes da chamada foreach_value, mas isso entrará em conflito com outras instâncias da macro foreach_value no mesmo escopo.Se eu pudesse adicionar o sufixo do número da linha atual ao nome da variável do iterador, funcionaria, mas não sei como fazer isso.
Solução
Você pode fazer isso usando dois loops.O primeiro declara o iterador, com um nome que é uma função da variável contêiner (e você pode tornar isso ainda mais feio se estiver preocupado com conflitos com seu próprio código).O segundo declara a variável 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)
Ao usar a mesma condição de encerramento do loop, o loop externo acontece apenas uma vez (e se você tiver sorte, será otimizado).Além disso, você evita chamar ->second no iterador se o mapa estiver vazio.Essa é a mesma razão para o operador ternário no incremento do loop interno;no final, apenas deixamos var no último valor, pois não será referenciado novamente.
Você poderia embutir ci(container), mas acho que isso torna a macro mais legível.
Outras dicas
Você estaria procurando BOOST_FOREACH - eles já fizeram todo o trabalho para você!
Se você deseja rolar o seu próprio, poderá declarar um bloco em qualquer lugar do C ++, que resolve seu problema de escopo com o seu armazenamento intermediário 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
}
O STL transformar function também faz algo semelhante.
Os argumentos são (em ordem):
- Um iterador de entrada que designa o início de um contêiner
- Um iterador de entrada que designa o final do contêiner
- Um iterador de saída que define onde colocar a saída (para uma transformação no local, semelhante a for-each, basta passar o iterador de entrada em #1)
- Uma função unária (objeto de função) para executar em cada elemento
Para um exemplo muito simples, você poderia colocar cada caractere em maiúscula em uma string:
#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, há também o acumular função, que permite que alguns valores sejam retidos entre chamadas ao objeto de função. acumular não modifica os dados no contêiner de entrada como é o caso de transformar.
Você já pensou em usar o Impulsione bibliotecas?Eles TEM um foreach
macro implementada o que provavelmente é mais robusto do que qualquer coisa que você escreverá ...e também há transform_iterator
que parece poder ser usado para fazer a segunda parte da extração do que você deseja.
Infelizmente não posso te dizer exatamente como usá-lo porque não conheço C++ suficiente :) Esta pesquisa no Google apresenta algumas respostas promissoras: comp.lang.c++.moderado, Caso de uso Boost transform_iterator.
Boost::For_each é de longe sua melhor aposta.O interessante é que o que eles realmente fornecem é a macro BOOST_FOREACH() que você pode agrupar e #define como você realmente gostaria de chamá-la em seu código.Quase todo mundo optará pelo bom e velho "foreach", mas outras lojas podem ter padrões de codificação diferentes, então isso se encaixa nessa mentalidade.Boost também tem muitas outras vantagens para desenvolvedores C++!Vale a pena usar.
Criei um pequeno auxiliar Foreach.h com algumas variantes de foreach() incluindo ambas operando em variáveis locais e em ponteiros, com também uma versão extra protegida contra exclusão de elementos de dentro do loop.Portanto, o código que usa minhas macros fica bonito e aconchegante assim:
#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;
}
saída:
0 1 2 3 4 5 6 7 8 9
0 1 2 3 5 6 7 8 9
Meu Foreach.h fornece as seguintes macros:
- foreach() - foreach regular para ponteiros
- foreach_() - foreach regular para variáveis locais
- foreachdel() - versão foreach com verificações de exclusão dentro do loop, versão do ponteiro
- foreachdel_() - versão foreach com verificações de exclusão dentro do loop, versão da variável local
Eles com certeza funcionam para mim, espero que também tornem sua vida um pouco mais fácil :)
Existem duas partes nesta questão.Você precisa de alguma forma (1) gerar um iterador (ou melhor, uma sequência iterável) sobre o seu mapa valores (não chaves) e (2) usar uma macro para fazer a iteração sem muitos clichês.
A solução mais limpa é usar um Alcance de reforço Adaptador para a parte (1) e Impulsionar Foreach para a parte (2).Você não precisa escrever a macro ou implementar o iterador sozinho.
#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
Você poderia definir uma classe de modelo que usa o tipo mymap como parâmetro de modelo e atua como um iterador sobre os valores sobrecarregando * e ->.
#define foreach(var, container) for (typeof((container).begin()) var = (container).begin(); var != (container).end(); ++var)
Não há typeof em C++ ...como isso é compilado para você?(certamente não é portátil)
Eu implementei o meu próprio foreach_value
com base no 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 exemplo, você pode usá-lo em seu código assim:
#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)
não há typeof() então você pode usar isto:
decltype((container).begin()) var
decltype(container)::iterator var