11
votes

Conteneur générique en C ++

Je tente de créer un type de conteneur générique pour fournir une seule interface commune, ainsi que de masquer les conteneurs internes que j'utilise, car ils sont sujets à changement.

essentiellement, j'ai des plugins qui renvoient des collections d'articles de retour et je ne souhaite pas que les plugins soient conscients du type de conteneur mon code utilise. P>

Quelqu'un peut-il me diriger dans une meilleure direction, puis le code exemple ci-dessous? P>

template<class C, typename I>
class Container
{
 public:
 //...

    void push(const I& item)
    {
        if(typeid(C) == typeid(std::priority_queue<I>))
        {
           std::priority_queue<I>* container = (std::priority_queue<I>*)&_container;
           container->push(item);
        }
        if(typeid(C) == typeid(std::list<I>))
        {
           std::list<I>* container = (std::list<I>*)&_container;
           container->push_back(item);
        }
        else
        {
           //error!
        }
     };

  private:
     C _container;
 //...
}


5 commentaires

Je peux. Ne fais pas ça. Passez votre temps à concevoir votre application afin qu'il utilise des conteneurs typés statiquement.


Je peux vous signaler dans une meilleure direction, sûr: N'essayez pas de faire cela . Sérieusement. Ça ne te gagnera pas quoi que ce soit. Voix d'expérience parlant ici.


Pourquoi le bownvote ici? C'est une question parfaitement légitime. Même si la réponse est "ne le fais pas de cette façon", c'était toujours une question valable.


Je ne comprends pas. J'ai une interface implémentée par mes plugins qui nécessitent une liste STD :: Liste à renvoyer. À une date ultérieure, certaines exigences se présentent de ces éléments commandés par exemple. En caché le type de conteneur renvoyé, je me permettrai de changer de flexibilité sans casser les plugins existants. Est-ce que j'ai râté quelque chose?


Juste besoin d'utiliser un conteneur de séquence; Ensuite, vous pouvez utiliser list , DEQUE ou vecteur (ou tout autre conteneur non standard répondant aux exigences de conteneur de séquence) sans aucune manipulation spéciale du tout . Le point entier du concepts de conteneur est que vous pouvez facilement écrire du code qui sera Travaillez avec n'importe quel conteneur qui répond aux exigences. (Oui, cela signifie que vous ne serez pas en mesure d'utiliser des non-conteneurs comme priority_queue directement, mais il est très inhabituel nécessaire pour le faire quand même.)


3 Réponses :


0
votes

Vous savez, lorsque vous avez écrit "Interface commune", j'étais sûr que vous alliez montrer quelque chose de style Java avec une classe abstraite et des sous-classes qui enveloppent des conteneurs standard. J'ai été surpris de voir un tas d'appels typeid ...

Mais alors, pourquoi voulez-vous faire cela? La plupart des conteneurs partagent une interface commune et avec la puissance de Sfinae, vous n'avez même pas à écrire de code spécial! Utilisez simplement des modèles et appelez la méthode directement.

Edit: Vous avez oublié que les conteneurs standard n'ont pas de méthodes virtuelles et, par conséquent, ne peuvent donc pas être dynamic_cast ed.


0 commentaires

7
votes

J'ai des plugins qui renvoient des collections d'articles et je ne souhaite pas que les plugins soient conscients du type de conteneur mon code utilise.

avoir vos plugins fournissez commencer et extrémité itérateurs dans leurs collections d'éléments, puis consommez la plage au fur et à mesure que vous le voyez.

Le plus grand avantage des itérateurs est qu'ils permettent un découplage complet de la manière dont les données sont stockées (le conteneur) à partir de la manière dont les données sont utilisées (l'algorithme; dans votre cas, votre code d'application qui consomme les données du plug-in).

De cette façon, vous ne devez pas vous soucier de la manière dont les plugins stockent leurs données et que les plugins ne doivent pas avoir à prendre soin de ce que vous faites avec leurs données une fois qu'ils vous le donnent.


8 commentaires

Merci pour vos réponses James. Cette approche a été examinée cependant devenue poilue lorsque des threads sont apparus sur la scène. Un plug-in enregistre par exemple peut avoir une méthode get_events (int Validator_id) pouvant être appelée par plusieurs threads avec des identifiants de validation variés.


@user: Je ne comprends pas; Une interface itératrice n'a plus de problèmes de concurrence qu'une interface de conteneur: verrouillez le conteneur pendant que vous vous y êtes itération ou effectuez une copie du conteneur à parcourir.


+1. Mais je pense qu'il serait souhaitable d'éviter la nécessité de recompiler le code qui consomme les valeurs de retour du plugin si un nouveau plugin est créé sur la route qui utilise un type de conteneur différent en interne - auquel cas? Il semble que vous devriez rendre les itérateurs renvoyés par commencer () et fin () classes de base (interfaces) à leur droite, et donnez-leur un pur virtuel opérateur ++ () et opérateur * () etc. Méthodes; Ensuite, pour chaque type de conteneur utilisé, créez une sous-classe de béton qui transmet à la méthode correspondante du type d'itérateur correct, non? Semble difficile ...


@j_Random_hacker, remarquez que les Itérateurs STL ne mettent pas en œuvre une interface publique? Ils utilisent la typing de canard, vous devriez aussi être capable de. en.wikipedia.org/wiki/duck_typing (un jour, peut-être qu'ils utiliseront des concepts Mais la typing de canard est la meilleure que nous avons pour l'instant).


@Bretkuhns: Je pense que le fait que les itérateurs STL soient dactylographiés ne sont pas le problème ici. Supposons que vous définissez vecteur x; au champ d'application de fichier et une fonction ultérieure fait référence à x.begin () ou x.end () : Cette fonction (le "consommateur") devra être recompilée si la définition de x est modifiée en une liste x; . Habituellement, ce n'est pas un gros problème - Sauf si vous construisez un système de plug-in qui doit consommer (itérer sur) des types de conteneurs non inconnus. Dans mon commentaire, je cherchais un moyen d'éviter de devoir recompiler le consommateur dans cette situation.


@j_random_hacker, ok je vois votre point maintenant. Vous avez raison, la diffamation d'exécution serait la solution pour éviter de recompiler.


@Brettekuhns: Aujourd'hui, j'ai rencontré au hasard ce traitement plus approfondi de ce que je suggère, au cas où vous êtes intéressé: Artima.com/cpppsource/type_erasure.html . Bon de savoir que je ne suis pas fou :)


@j_random_hacker qui a été une grande lecture, et étonnamment pertinente pour quelque chose que j'ai prélevé récemment. Merci pour le suivi!



0
votes

Commencez par une classe comme ci-dessus, exposez une interface minimale requise par vos plugins. Ensuite, mettez-la en termes de conteneur que vous allez utiliser. Ceci s'appelle l'adaptateur de conteneurs et c'est comment STD :: Stack est implémenté.

Si vous avez vraiment besoin d'adaptateur pour plus, un conteneur STL avec modèle, jetez un coup d'œil à STD :: Stack, il montrera comment faire cela.

Ne pas basculer sur TYPETID, pourquoi voudriez-vous cela?

BTW, allez avec ce que James suggère sauf s'il est nécessaire d'exposer le conteneur lui-même.


0 commentaires