Domanda

Sto sviluppando un programma in cui mi trovo a fare molto così:

void Model::SetCollideMode( const std::string &m )
{
  Body *body;

  std::map<std::string, Body* >::iterator iter;

  for (iter=this->bodies.begin(); iter!=this->bodies.end(); iter++)
  {
    body = iter->second;

    body->SetCollideMode( m );
  }
}

Ho diversi metodi come quello in diversi oggetti che fondamentalmente applicano una proprietà a tutti i suoi figli. Provenendo dal mondo di Ruby, muoio dalla voglia di fare qualcosa del tipo:

for_all_bodies{ body->SetCollideMode(m) }

C'è comunque modo di rendere questo codice più chiusure o in qualche modo migliorarlo?

Sono consapevole di come funziona C ++, che è basato su stack e non ci sono informazioni di contesto per creare una chiusura perfetta come la funzionalità (questo ha bisogno di una VM?) ma almeno migliorare rispetto all'attuale ripetere questo codice 100 volte programmazione.

È stato utile?

Soluzione

Esistono diversi approcci, nessuno perfetto.

Innanzitutto, il "convenzionale" l'approccio sarebbe definire un funzione per fare ciò di cui hai bisogno:

struct SetCollideModeFunc {
  SetCollideModeFunc(const std::string& m) : m(m) {}
  void operator()(std::pair<std::string, Body*>& p) {
    Body* b = p.second;
    b->SetCollideMode(m);
  }

  const std::string& m;
};

void Model::SetCollideMode( const std::string &m )
{
  std::for_each(bodies.begin(), bodies.end(), SetCollideModeFunc(m));
}

Ciò non consente di risparmiare molto codice, ma consente di separare l'iterazione dall'operazione che si desidera applicare. E se devi impostare collidemode più volte, ovviamente puoi riutilizzare il funzione.

Una versione più breve è possibile con la libreria Boost.Lambda, che ti permetterebbe di definire la funzione in linea. Non ricordo l'esatta sintassi, poiché non uso spesso Boost.Lambda, ma sarebbe qualcosa del genere:

std::for_each(bodies.begin(), bodies.end(), _1.second->SetCollideMode(m));

In C ++ 0x, ottieni il supporto linguistico per lambdas, consentendo una sintassi simile a questa senza dover estrarre librerie di terze parti.

Infine, Boost.ForEach potrebbe essere un'opzione, che consente una sintassi come questa:

void Model::SetCollideMode(const std::string &m)
{
  BOOST_FOREACH ((std::pair<std::string, Body*> p), bodies) // note the extra parentheses. BOOST_FOREACH is a macro, which means the compiler would choke on the comma in the pair if we do not wrap it in an extra ()
  {
    p.second->SetCollideMode(m);
  }
}

Altri suggerimenti

In C ++ 0x, sì. Vedi qui. Proprio come hai indovinato, sono fatti in il modo C ++ caratteristico, ovvero se si chiude accidentalmente su una variabile dello stack e si lascia che l'oggetto lambda sopravviva più a lungo dello stack, si ha un comportamento indefinito. È un modo completamente nuovo di bloccare il tuo programma! Ma ciò è ingiusto: in molti modi sono più sofisticati delle lambda in molte altre lingue, perché puoi dichiarare fino a che punto è permesso loro di mutare lo stato.

Fino ad allora, ci sono stati tentativi di emulare lo stesso cosa , ma probabilmente sono più problemi di quanti ne valga la pena.

BOOST_FOREACH (o il nuovo loop basato su range) è probabilmente la strada da percorrere, ma ecco come normalmente approccio lambda nello standard corrente, usando tr1 bind:

#include <algorithm>
#include <functional>
using namespace std;

void Model::SetCollideMode( const std::string &m )
{
  for_each(bodies.begin(),bodies.end(),
           tr1::bind(&Body::SetCollideMode,
                     tr1::bind(&pair<std::string, Body*>::second, _1), m));
}

Puoi utilizzare Boost.Foreach :

#include <boost/foreach.hpp>

void Model::SetCollideMode(const std::string &m)
{
  typedef pair<std::string, Body*> body_t;
  BOOST_FOREACH (body_t& body, bodies)
  {
    body.second->SetCollideMode(m);
  }
}

C ++ non supporta ancora lambda. A volte uso questa soluzione alternativa:

#include <boost/bind.hpp>
void Model::SetCollideMode( const std::string &m )
{    
  typedef std::map<std::string, Body* > Bodies;
  struct Helper
  {
      static SetCollideMode(const std::pair<std::string, Body*> & value,
                            const std::string & m)
      {
          value.second->SetCollideMode(m);
      }
  };

  for_each(bodies.begin(),
           bodies.end(),
           boost::bind(Helper::SetCollideMode,_1, m));
}

Solo i miei 2 centesimi ..

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top