2010-08-18 10 views
3

J'ai une structure de liste chaînée:Utiliser boost :: iterator_facade <>

struct SomeLinkedList 
{ 
    const char* bar; 
    int lots_of_interesting_stuff_in_here; 
    DWORD foo; 
    SomeLinkedList* pNext; 
}; 

Il fait partie d'une API existante et je ne peux pas le changer.

Je voudrais ajouter le support de l'itérateur. La bibliothèque boost::iterator_facade<> semblait idéale à cet effet.

class SomeIterator 
    : public boost::iterator_facade< SomeIterator, 
            const SomeLinkedList, 
            boost::forward_traversal_tag > 
{ 
public: 
    SomeIterator() : node_(NULL) {}; 

    explicit SomeIterator(const SomeLinkedList* p) : node_(p) {}; 

private: 
    friend class boost::iterator_core_access; 

    void increment() { node_ = node_->pNext; }; 

    bool equal(SomeIterator const& other) const { /*some comparison*/; }; 

    SomeLinkedList const& dereference() const { return *node_; }; 

    SomeLinkedList const* node_; 
}; // class SomeIterator 

L'objectif est de pouvoir l'utiliser dans les fonctions de la bibliothèque standard comme std::for_each

void DoSomething(const SomeLinkedList* node); 

SomeLinkedList* my_list = CreateLinkedList(); 
std::for_each(SomeIterator(my_list), SomeIterator(), DoSomething); 

Malheureusement, je reçois une erreur disant qu'il essaie de transmettre la liste par valeur plutôt que par pointeur .

error C2664: 'void (const SomeLinkedList *)' : cannot convert parameter 1 from 'const SomeLinkedList' to 'const SomeLinkedList *' 

Comment puis-je changer SomeIterator faire pour obtenir ce travail correctement?

Merci, paulh


Edit: J'ai essayé ceci:

class SomeIterator 
    : public boost::iterator_facade< SomeIterator, 
            SomeLinkedList, 
            boost::forward_traversal_tag, 
            SomeLinkedList* > 
{ 
    // ... 

mais je reçois cette erreur de complier:

error C2664: 'boost::implicit_cast' : cannot convert parameter 1 from 'SomeLinkedList **' to 'boost::detail::operator_arrow_proxy<T> 

Ed il 2:

J'ai essayé de modifier le type de déréférencement:

class SomeIterator 
    : public boost::iterator_facade< SomeIterator, 
            const SomeLinkedList, 
            boost::forward_traversal_tag > 
{ 
    // ... 

    const SomeLinkedList* dereference() const { return node_; }; 

mais, je reçois l'erreur d'origine:

error C2664: 'void (const SomeLinkedList *)' : cannot convert parameter 1 from 'const SomeLinkedList' to 'const SomeLinkedList *' 
+0

Re: l'édition. Vous dites toujours que 'value_type' va être' SomeLinkedList'. Vous ne devriez probablement pas avoir besoin du dernier paramètre, puisque la valeur par défaut devrait fonctionner pour cela. Aussi, avez-vous changé le type de retour du membre 'dereference'? - Mais encore, puisque votre base de code ne semble pas être particulièrement stdlib-style, ne serait-il pas plus simple d'écrire votre propre foreach pour cette liste liée particulière (puisque je doute que tout autre algorithme stdlib va ​​bien fonctionner avec un tel un itérateur bizarre de toute façon sans travail supplémentaire sérieux)? – UncleBens

+0

@UncleBens - Voir édition 2. En outre, je ne pense pas que ce serait beaucoup de travail supplémentaire. Il suffit de surcharger un opérateur de comparaison ou un prédicat binaire. – PaulH

+1

Il semble que vous ayez besoin de 'const SomeLinkedList *' pour les paramètres 'value_type' et' reference'. Probablement un peu bizarre quand il s'agit de prendre des références à des pointeurs. - Quant au travail supplémentaire: vous convient. Pour moi, l'utilisation de chaînes de style C avec stdlib est trop de travail, car il n'a jamais été conçu pour supporter ceux qui sortent de la boîte. – UncleBens

Répondre

1

Lorsque votre iterator est déréférencé, il retournerait un const SomeLinkedList& cependant votre fonction DoSomething attend un const SomeLinkedList*. Modifiez l'itérateur pour renvoyer les pointeurs en cas de déréférencement ou modifiez votre fonction DoSomething.


EDIT en réponse à une discussion plus:

Je n'ai pas réellement utilisé boost :: me iterator_facade, mais en regardant le code supplémentaire que vous posté il semble que vous pourriez ne pas avoir changé toutes les pièces nécessaires en même temps.

Avez-vous déjà essayé

class SomeIterator 
    : public boost::iterator_facade< SomeIterator, 
            SomeLinkedList, 
            boost::forward_traversal_tag, 
            SomeLinkedList* > 
{ 

et

const SomeLinkedList* dereference() const { return node_; }; 

ensemble?

Ou si cela ne fonctionne pas, alors que diriez-vous:

class SomeIterator 
    : public boost::iterator_facade< SomeIterator, 
            SomeLinkedList*, 
            boost::forward_traversal_tag> 
{ 

const SomeLinkedList* dereference() const { return node_; }; 

Sinon, comme davka suggéré dans un commentaire, que de résoudre le problème de pointeur vs référence en faisant une enveloppe autour DoSomething? Tels que:

void DoSomethingWrapper(const SomeLinkedList& node) 
{ 
    DoSomething(&node); 
} 

En fait, vous pourriez probablement même garder l'emballage le même nom que la fonction qu'elle enveloppe et laisser les règles de surcharge prennent en charge lorsque la version du pointeur ou de référence est appelée.

+0

'DoSomething' est également corrigé et ne peut pas être modifié. Je voudrais savoir comment modifier l'objet 'boost :: iterator_facade <>' pour qu'il fonctionne. Idéalement, il se comporterait exactement comme 'std :: vector :: const_iterator' (mais traversée vers l'avant seulement) – PaulH

+0

@PaulH: Donc, avez-vous au moins essayé de changer ce que l'itérateur déréférences (aussi n'oubliez pas le paramètres de modèle)? OMI, cette chose ne ressemblera pas beaucoup aux itérateurs standards de stdlib, sauf si elle déréférence au membre DWORD de toute façon. Par exemple, si je veux utiliser 'std :: find' pour trouver un noeud avec une valeur spécifique, pourquoi cela ne fonctionnerait-il pas? Pourquoi les détails de l'itération (que c'est un noeud dans une liste liée intrusive) gênent-ils? – UncleBens

+0

@UncleBens - L'idée ici est que je ** pourrais ** utiliser des algorithmes comme 'std :: find'. Oui, j'ai essayé plusieurs choses pour changer ce que l'itérateur déréférences à. Pour le moment, je n'ai pas réussi à le faire dans le cadre de 'boost :: iterator_facade <>'. C'est essentiellement ce que je demande dans cette question. – PaulH

1

J'ai essayé de comprendre boost :: iterator_facade. En cherchant un exemple simple, j'ai trouvé cette (ancienne) question et la seule réponse acceptée. Je pensais publier le code dont j'avais besoin pour que cet exemple fonctionne, car la question et la réponse existantes ne règlent jamais vraiment le problème. En premier lieu, notez que le second paramètre de for_each() est un itérateur de fin. J'ai trouvé qu'un itérateur NULL (je ne sais pas si c'est la bonne terminologie) comme utilisé dans le code de la question originale fonctionne bien, mais seulement si vous finissez la définition incomplète de .equal() comme suit;

bool equal(SomeIterator const& other) const { return node_ == other.node_; } 

En dehors de cela changeant simplement la définition du paramètre de DoSomething() de référence à ptr, comme mentionné dans la réponse acceptée, est la clé pour obtenir ce pour compiler et exécuter. J'ai mis un code de test primitif ci-dessous pour illustrer.

void DoSomething(const SomeLinkedList& node) 
{ 
    std::cout << "DoSomething " << node.foo << "\n"; 
} 

int main() 
{ 
    SomeLinkedList temp[5]; 
    memset(temp,0,sizeof(temp)); 
    temp[0].pNext = &temp[1]; 
    temp[1].pNext = &temp[2]; 
    temp[2].pNext = &temp[3]; 
    temp[3].pNext = &temp[4]; 
    temp[4].pNext = 0; 
    temp[0].foo = 0; 
    temp[1].foo = 1; 
    temp[2].foo = 2; 
    temp[3].foo = 3; 
    temp[4].foo = 4; 
    SomeLinkedList* my_list = &temp[0]; 
    std::for_each(SomeIterator(my_list), SomeIterator(), DoSomething); 
    return 0; 
}