Un adaptateur d'itérateur C ++ qui enveloppe et cache un itérateur interne et convertit le type itéré
Avoir joué avec cette je soupçonne que ce n'est pas à distance possible, mais j'ai pensé que je demanderais aux experts. J'ai le code C++ suivant:
classe IInterface { virtual void SomeMethod() = 0; }; Objet de classe { IInterface* GetInterface() { ... } }; classe de Conteneur { privé: struct Point { Objet* pObject; [... autres membres ...] }; std::list<Item> m_items; };
Je tiens à ajouter que ces méthodes Conteneur:
MagicIterator<IInterface*> Begin(); MagicIterator<IInterface*> ();
Afin que les appelants peut écrire:
Container c = [...] pour (MagicIterator<IInterface*> i = c.Begin(); i != c.End(); i++) { IInterface* pItf = *i; [...] }
Donc, essentiellement, je veux fournir une classe qui semble être une itération sur certains de la collection (qui à l'appelant de Begin() et End() n'est pas autorisé à voir) de IInterface pointeurs, mais qui est en fait une itération sur une collection de pointeurs vers d'autres objets (privé de la classe Container) qui peuvent être convertis en IInterface pointeurs.
Quelques points clés:
MagicIterator
doit être défini en dehors deContainer
.Container::Item
doit rester privé.
MagicIterator
a pour itérer surIInterface
pointeurs, malgré le fait queContainer
est titulaire d'unstd::list<Container::Item>
.Container::Item
contient unObject*
etObject
peut être utilisé pour récupérerIInterface*
.
MagicIterator
doit être réutilisable plusieurs classes qui ressemblent à des Conteneurs, mais pourrait en interne ont différents pour les implémentations de la tenue de différents objets (std::vector<SomeOtherItem>
mylist<YetAnotherItem>
) et avecIInterface*
obtenus d'une manière différente à chaque fois.
MagicIterator
ne doit pas contenir de conteneur de code spécifiques, mais il peut déléguer à des classes qui ne sont, à condition que cette délégation n'est pas codé en dur pour notamment les conteneurs à l'intérieur deMagicIterator
(donc en quelque sorte résolu automatiquement par le compilateur, par exemple).
- La solution doit compilation sous Visual C++ sans utiliser d'autres bibliothèques (comme boost) qui aurait besoin d'un accord de licence à partir de leurs auteurs.
- Aussi, l'itération ne peut allouer de la mémoire de masse (donc pas de
new()
oumalloc()
à toute étape), et pas dememcpy()
.
Merci pour votre temps, même si vous êtes juste en train de lire; cela a vraiment été m'énerve!
mise à Jour: Alors que j'ai eu des réponses très intéressantes, aucun ne répondait à toutes les exigences ci-dessus encore. Notamment les zones difficiles d'accès sont: i) le découplage MagicIterator de Conteneur en quelque sorte (modèle par défaut arguments ne coupe pas), et ii) d'éviter d'allocation de tas; mais je suis vraiment après une solution qui couvre l'ensemble de la ci-dessus balles.
source d'informationauteur
Vous devez vous connecter pour publier un commentaire.
Je pense que vous avez deux questions se posent ici:
Tout d'abord, créer un itérateur qui sera de retour le
IInterface*
de votrelist<Container::Item>
. Ceci se fait facilement avecboost::iterator_adaptor
:Vous pouvez créer ce type, comme interne dans
Container
et le retour de sonbegin()
etend()
méthodes.Deuxièmement, vous voulez le runtime-polymorphe
MagicIterator
. C'est exactement ce queany_iterator
. leMagicIterator<IInterface*>
est justeany_iterator<IInterface*, boost::forward_traversal_tag, IInterface*>
etcont_iter
peut être assigné à elle.N'a pas l'air trop compliqué. Vous pouvez définir l'itérateur à l'extérieur. Vous pouvez également utiliser des typedefs. Quelque chose comme ce serait bon, je pense. Notez qu'il serait beaucoup plus propre si que MagicIterator serait pas gratuitement un modèle, mais un membre de l'Élément, typedefed dans le Conteneur peut-être. Comme il est maintenant, il est cyclique de référence, qui en font necassary à écrire moches, code solution de contournement.
Maintenant, commencer à l'utiliser:
Vous pouvez également utiliser un itérateur mixin fournis par boost, qui fonctionne comme la version d'entrée de boost::function_output_iterator. Il appelle votre itérateur est
operator()
qui renvoie alors la valeur appropriée, de faire ce que nous faisons ci-dessus dans notreoperator*
en principe. Vous le trouverez dansrandom/detail/iterator_mixin.hpp
. Ce serait sans doute de moins en moins de code. Mais elle exige aussi de wrack jusqu'à notre cou pour assurer l'ami-stuff car l'Article est privé et l'itérateur n'est pas définie à l'intérieur de l'Élément. De toute façon, bonne chance 🙂Créer un résumé
IteratorImplementation
classe:Et un
Iterator
classe de s'enrouler autour d'elle:(Vous pouvez faire
IteratorImplementation
une classe "à l'intérieur" deIterator
de garder les choses en ordre.)Dans votre
Container
classe, retourner une instance deIterator
avec une sous-classe personnalisée deIteratorImplementation
dans lector
:Cela dépend vraiment de la
Container
parce que les valeurs de retour dec.Begin()
etc.End()
sont définis par l'implémentation.Si une liste de
Container
s est connu pourMagicIterator
une classe wrapper peut être utilisé.La facile moyen serait d'utiliser un modèle qui accepte
Container
's type:Plus d'informations ici.
Je ne vois aucune raison pourquoi vous ne pouvez pas mettre en œuvre la présente exactement comme vous l'avez jeté dehors... j'ai loupé quelque chose?
Pour clarifier, vous aurez besoin de mettre une sorte de méthodes d'accès à votre Conteneur de classe. Ils peuvent être privés et vous pouvez déclarer MagicIterator comme un ami, si vous pensez que c'est la meilleure façon de l'encapsuler, mais je voudrais exposer directement. Ces méthodes accesseur serait normal STL itérateur à l'intérieur du Contenant et effectuer la conversion de IInterface. Ainsi, l'itération serait réellement être fait avec le Conteneur des méthodes accesseurs et MagicIterator serait juste une sorte de proxy de l'objet pour le rendre plus facile. Pour le rendre réentrant, vous pourriez avoir la MagicIterator passer un IDENTIFIANT pour regarder le TSL itérateur à l'intérieur du Contenant, ou vous avez passer dans la STL itérateur comme un
void *
.J'ai maintenant trouvé une solution qui est mieux pour mon premier but. Je ne l'aime toujours pas bien 🙂
La solution consiste à MagicIterator être modélisé sur IInterface* et d'être construit à la fois avec un void* pour un itérateur, la taille en octets de dit itérateur, et un tableau de pointeurs de fonctions qui effectuent standard itération des fonctions en dit void* comme incrémenter, décrémenter, déréférencement, etc. MagicIterator suppose qu'il est sécuritaire de le memcpy le itérateur dans une mémoire tampon interne, et met en œuvre ses propres membres par le passage de sa propre mémoire tampon comme un vide,* les fonctions comme si c'était l'original de l'itérateur.
Récipient de a à mettre en œuvre statique itération des fonctions qui jettent de retour fourni de void* vers une std::list::iterator. Conteneur::begin() et Conteneur::end (), il suffit de construire une std::list::iterator, passer un pointeur dans un MagicIterator avec un tableau de ses fonctions itératives, et le retour de la MagicIterator.
C'est un peu dégoûtant, et me brise d'origine de la règle concernant les "pas de memcpy()", et fait des hypothèses sur le fonctionnement interne de la itérateurs en question. Mais il évite d'allocation de tas, garde de la Collection internes (y compris la Poste) privé, rend MagicIterator entièrement indépendant de la collection en question et de IInterface*, et, en théorie, permet MagicIterators de travailler avec toute la collection (à condition que son itérateurs peuvent être en toute sécurité memcopy()'d).
Un visiteur peut être plus simple (et donc plus facile à entretenir) solution.