2009-11-23 42 views
13

Ho un oggetto che voglio viaggiare in un ciclo continuo in un gioco. Ho una serie di coordinate in un std::vector che voglio usare come waypoint.Il modo più semplice per realizzare un iteratore ciclico (circolatore)?

Esiste un modo per creare un ciclo std::vector<T>::iterator (noto anche come circolatore)?

Il meglio che posso venire è avere due iteratori e poi ogni volta che il primo iteratore è esaurito assegnargli il valore del secondo (che non sarebbe usato per fare altro) ma non sono nemmeno sicuro che sia funzionerà - l'operatore incaricato copia ciò che l'iteratore sta usando per tenere l'indice o sarà semplicemente referenziato (e quindi sarà inutile dopo il secondo turno)?

Desidero che l'oggetto percorra il waypoint per sempre (a meno che non venga distrutto ma ciò non accade in tale metodo), ma l'iteratore verrà chiamato una sola volta per ogni frame e deve essere restituito in modo da poter aggiornare l'altro oggetti nel gioco.

La soluzione deve funzionare su gcc e microsoft compiler (se non è possibile scriverlo in C++ standard).

+0

Ho scritto un iteratore del genere quindi è assolutamente possibile =) L'unica cosa che ricordo è che l'operatore di confronto

+0

D'altra parte, hai davvero bisogno di 'operatore <'? È un po 'malizioso nascondere il comportamento ciclico! –

risposta

21

Ok, ora il problema è più chiara :-)

Date un'occhiata a boost :: iterator_facade e boost :: adattatore iterator. Essi implementano l'interfaccia completa iteratore e le vostre cycle_iterator solo come implementare alcuni metodi come incremento(), decremento():

template<class IteratorBase> 
class cycle_iterator 
    : public boost::iterator_adaptor< 
      cycle_iterator,  // the derived class overriding iterator behavior 
      IteratorBase,  // the base class providing default behavior 
      boost::use_default, // iterator value type, will be IteratorBase::value_type 
      std::forward_iterator_tag, // iterator category 
      boost::use_default // iterator reference type 
     > 
{ 
    private: 
    IteratorBase m_itBegin; 
    IteratorBase m_itEnd; 

    public: 
    cycle_iterator(IteratorBase itBegin, IteratorBase itEnd) 
     : iterator_adaptor_(itBegin), m_itBegin(itBegin), m_itEnd(itEnd) 
    {} 

    void increment() { 
     /* Increment the base reference pointer. */ 
     ++base_reference(); 

     /* Check if past-the-end element is reached and bring back the base reference to the beginning. */ 
     if(base_reference() == m_itEnd) 
      base_reference() = m_itBegin; 
    } 

    // implement decrement() and advance() if necessary 
    }; 

Questo probabilmente non compila, ma dovrebbe iniziare.

Edit:

boost::iterator_adaptor implementa l'interfaccia iteratore completa in termini di poche funzioni. Fornisce implementazioni predefinite per increment(), decrement(), advance(), distance_to(), equal_to() e dereference() utilizzando l'iteratore di base trasmesso alla classe base iterator_adaptor.

Se tutto ciò che serve è un iteratore di inoltro, è necessario implementare solo il metodo increment() da avvolgere una volta raggiunto l'iteratore di fine. Un iteratore ciclico può essere bidirezionale se si implementa decrement() in modo simile. Se IteratorBase è esso stesso un iteratore di accesso casuale, il ciclo iteratore può anche essere accesso casuale e il metodo advance e distance_to deve essere implementato utilizzando le operazioni modulo.

+0

+1: 'iterator_adaptor' è il modo più semplice per scrivere iteratori, e gli esempi sono semplicemente grandiosi (in particolare come scrivere solo un adattatore e ottenere entrambe le versioni const e non-const) –

+0

C'è un modo per rendere gli operatori binari come '! =' per lavorare con argomenti di tipo 'cycle_iterator ' e 'IteratorBase'? Sto cercando di confrontare 'cycle_iterator' e" solito "iteratore e ho bisogno di fare un sovraccarico esplicito in questo caso. – Mikhail

+1

'increment()' ha bisogno di metamessaggi di lavoro - Penso che idealmente l'incremento del valore prima di 'end' dovrebbe darti il' begin'ning, non il 'end' su un iteratore ciclico. – Yakk

-5

Ricava la propria raccolta da std :: vector e fornisce la propria implementazione di iteratore che sovrascrive gli operatori di decremento di incremento &.

Ci sono molti tutorial sul web. Ad esempio, dare un'occhiata a this blog post

+3

deriva dal vettore? Hai mai provato? Non sono d'accordo. Aggregato invece! – xtofl

+0

Non ho mai visto una classe derivata da un contenitore standard che fosse migliore per questo. Aggregato. –

+1

Sono d'accordo con i commenti. Risposta inviata in fretta, pentendosi a piacimento. Tuttavia, non può cancellarlo perché è stato accettato come risposta. –

7

boost::iterator adaptor è la strada da percorrere, prendere la mia parola per esso;)

Detto questo voglio sottolineare alcune insidie. Non credo di poter modificare una risposta esistente, quindi abbi pazienza con me.

Dato che l'iteratore di base sarà un vettore, è necessario prestare attenzione a quali funzioni di interfaccia di base è necessario implementare.Se volete che il vostro cycle_iterator di essere un iteratore ad accesso casuale è necessario tutti i seguenti:

increment() 
decrement() 
advance(n) 
distance_to(j) 

Ora distance_to(j) è un concetto un po 'divertente per un cycle_iterator e la sua semantica si può ottenere in tutti i tipi di problemi. Questo può essere evitato limitando la categoria iteratore dell'iter iter adattato a avanti o bidirezionale. Come questo:

template <class BaseIterator> 
class cycle_iterator 
    : public boost::iterator_adaptor< 
     cycle_iterator     // Derived 
     , BaseIterator     // Base 
     , boost::use_default    // Value 
     , boost::forward_traversal_tag // CategoryOrTraversal 
    > 
{ ... }; 

In questo caso è sufficiente implementare minimo:

void increment() 
{ 
    if (++this->base_reference() == this->m_itEnd) 
    { 
    this->base_reference() = this->m_itBegin; 
    } 
} 

Per una bidirezionale è inoltre necessario decremento:

void decrement() 
{ 
    if (this->base_reference() == this->m_itBegin) 
    { 
    this->base_reference() = this->m_itEnd; 
    } 
    --this->base_reference(); 
} 

Disclaimer: non ha funzionato questo attraverso un compilatore, quindi sono pronto a essere imbarazzato.

+0

Penso che in generale 'distance_to (j)' può restituire un numero positivo o negativo che, aggiunto ad un iteratore, produce un iteratore che punta a 'j'. Potresti decidere di voler restituire il numero più piccolo di tale numero che non è negativo o un numero tale che abbia il valore assoluto più basso. Nella maggior parte dei casi, il secondo probabilmente ha più senso - oppure esiste un argomento forte altrimenti (un controesempio)? –

Problemi correlati