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.

Foi útil?

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

  1. Um iterador de entrada que designa o início de um contêiner
  2. Um iterador de entrada que designa o final do contêiner
  3. 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)
  4. 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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top