8
votes

Comment puis-je garder une trace de (énumérer) toutes les classes qui implémentent une interface

J'ai une situation où j'ai une interface qui définit la façon dont une certaine se comporte de classe afin de remplir un certain rôle dans mon programme, mais à ce moment je ne suis pas sûr à 100% le nombre de classes que je vais écrire remplir ce rôle. Cependant, en même temps, je sais que je veux que l'utilisateur soit en mesure de sélectionner, à partir d'une zone de liste déroulante GUI / liste, qui classe concrète qui implémente l'interface qu'ils veulent utiliser pour remplir un certain rôle. Je veux l'interface graphique pour être en mesure d'énumérer toutes les classes disponibles, mais je préférerais ne pas avoir à revenir en arrière et changer l'ancien code chaque fois que je décide de mettre en œuvre une nouvelle classe pour remplir ce rôle (qui peut être mois à partir de maintenant)

Certaines choses que j'ai pris en compte:

  1. en utilisant une énumération
    • Plus:
      1. Je sais comment le faire
      2. Moins
        1. je vais devoir mettre à jour à jour l'énumération quand j'ajoute une nouvelle classe
        2. laid à itérer
        3. en utilisant une sorte de statique de liste dans l'interface et l'ajout d'un nouvel élément à partir du fichier de définition de la classe implémentant
          • Plus:
            1. Wont doivent changer l'ancien code
            2. Inconvénients:
              1. Même pas sûr si cela est possible
              2. Je ne sais pas ce genre d'information pour stocker de sorte qu'une méthode de fabrication peut choisir le constructeur approprié (peut-être une carte entre une chaîne et un pointeur de fonction qui renvoie un pointeur vers un objet de l'interface)

                Je suppose que cela est un problème (ou similaire à un problème) que les programmeurs plus expérimentés ont probablement rencontré avant (et souvent), et il y a probablement une solution commune à ce genre de problème, ce qui est presque certainement mieux que tout ce que je suis capable de venir avec. Alors, comment puis-je faire?

                (PS je cherchais, mais tout ce que je trouvais était-ce, et ce n'est pas le même: Comment puis-je énumérer tous les éléments qui mettent en œuvre une interface générique? . Il semble qu'il sait déjà comment résoudre le problème que j'essaie de comprendre.)

                Edit: Je renommé le titre « Comment puis-je garder une trace de ... » plutôt que « Comment puis-je énumérer ... » parce que la question initiale sonnait comme si j'étais plus intéressé à examiner l'environnement d'exécution, où comme ce que je suis vraiment intéressé est la compilation tenue des livres.


2 commentaires

Qu'entendez-vous par interface? Une classe d'interface réelle ou une méthode spécifique pour la classe?


Une classe d'interface réelle: une collection de méthodes diverses définissant des interactions avec un objet de cette interface


6 Réponses :


1
votes

Si vous êtes sous Windows, et utilisez C ++ / CLI, cela devient assez facile. Le .NET Framework fournit cette fonctionnalité via la réflexion et fonctionne très proprement du code géré.

Dans Native C ++, cela obtient un peu plus délicat, car il n'y a pas de moyen simple d'interroger la bibliothèque ou la demande d'informations d'exécution. Il existe de nombreux Frameworks qui fournissent ce (Juste cherche CIO, DI ou Frameworks Plugin), mais le moyen le plus simple de le faire est de faire une certaine forme de configuration qu'une méthode d'usine peut utiliser pour s'inscrire et renvoyer une mise en œuvre de votre classe de base spécifique. Vous devez simplement mettre en œuvre la chargement d'une DLL et l'enregistrement de la méthode d'usine - une fois que vous aurez cela, c'est assez facile.


1 commentaires

Je suis sous Windows, mais, hélas, le programme doit également fonctionner sous Linux (donc j'utilise GNU Compiler). Je n'ai pas besoin d'aller aussi loin que d'interroger le temps d'exécution. En fait, je préférerais une solution de compilation (ou plutôt une tenue de liaison).



0
votes

Il n'y a aucun moyen d'interroger les sous-classes d'une classe dans (natif) C ++. Comment créez-vous les instances? Pensez à utiliser une méthode d'usine vous permettant d'iTER à toutes les sous-classes que vous travaillez. Lorsque vous créez une instance comme celle-ci, il ne sera pas possible d'oublier d'ajouter une nouvelle sous-classe plus tard.


1 commentaires

C'est exactement ce que je veux faire, mais je veux un moyen propre de faire la garde du livre, je n'ai donc pas à mettre à jour le code de cette méthode d'usine chaque fois que j'écris une nouvelle sous-classe.



3
votes

Je me souviens vaguement de faire quelque chose de similaire à cela il y a de nombreuses années. Votre option (2) est à peu près ce que j'ai fait. Dans ce cas, c'était un std :: map de std :: chaîne to std :: typeinfo . Dans chacun, fichier .cpps j'ai enregistré la classe comme ceci: xxx

registreClass prend un type_info objet et renvoie simplement vrai . Vous devez initialiser une variable pour vous assurer que registreClass est appelé lors de l'heure de démarrage. Il suffit d'appeler registreClass dans l'espace de noms global est une erreur. Et la fabrication DMMY statique vous permet de réutiliser le nom entre les unités de compilation sans une collision de noms.


3 commentaires

Ça a l'air bien. Je ne suis pas confiant à 100% avec mes connaissances sur l'initialisation statique, mais si c'est un plan réalisable, je vais commencer à essayer. L'opérateur Typeid sera utile. Je reviendrai et faire cela la réponse si je l'obtiens au travail.


Républis de réponses, je pense que cette solution est la meilleure (pour l'instant?). Pas besoin d'encombrer le code avec des méthodes d'usine, cela fonctionne avec le code existant et il est plus direct que ce que j'ai proposé. Comme C ++ n'a pas beaucoup de mécanismes RTTI, je ne pense pas que vous puissiez faire mieux ... +1 homme.


Et comment créez-vous une classe enregistrée?



8
votes

Créez un singleton où vous pouvez enregistrer vos classes avec un pointeur sur une fonction de créateur. Dans les fichiers du CPP des classes de béton, vous enregistrez chaque classe. de
Quelque chose comme ceci: xxx


4 commentaires

Et vous pouvez envelopper l'enregistrement dans une macro pour faciliter l'utilisation: register_class (chic)


Merci, ce code de code est très utile. Existe-t-il une raison particulière d'utiliser cette classe supplémentaire plutôt que de stocker la carte en tant que membre statique de l'interface. Pour autant que je sache, il fournit une méthode «Créer ()» sensible, mais toute autre raison. Cela peut être particulièrement utile en tant que générique singleton, puis je peux instancer une pour chacune des interfaces que je sais que je vais aller à l'avenir.


@cheshirekow principe de responsabilité unique


@Timw, merci une tonne. Je pense que c'est une très bonne solution. Je l'ai mis en œuvre et ça fonctionne assez bien. J'ai posté mon code pour partager: Cheshirekow.com/blog/?p=40



1
votes

Quelque chose que vous pouvez considérer est un compteur d'objet. De cette façon, vous n'avez pas besoin de changer tous les endroits que vous allouez, mais simplement une définition de mise en œuvre. C'est une alternative à la solution d'usine. Considérons des avantages / inc.

Un moyen élégant de faire est d'utiliser le CRTP: modèle de modèle curieusement récurrent . L'exemple principal est un tel compteur :) p>

De cette façon, vous devez simplement ajouter votre implémentation de classe de béton: P>

class X; // your interface

class MyConcreteX : public counter<X>
{
    // whatever
};


2 commentaires

C'est un moyen très intelligent d'aborder le problème. Je suppose que le compteur s'étend x et contient un compte d'objet statique, incrémenté de la construction et décrémenté sur la destruction. Je pense que c'est un peu indirect cependant et ce dont j'ai besoin n'est pas en réalité d'un objet, mais un moyen d'énumérer les sous-classes disponibles.


Pas exactement, qui utilisent le modèle CRTP qui mérite un coup d'œil. Voir le lien dans ma réponse car le code d'un tel compteur est donné. L'idée de ma réponse était d'utiliser un compteur d'objet qui ne compte que la première instance. Je sais qu'il est comme la nécessité d'instancier au moins un objet (peut être un mannequin statique cependant), mais semble être meilleur que la solution de méthode d'usine.



2
votes

J'ai mentionné cet article pour mettre en œuvre une usine de classe auto-enregistreuse similaire à celle décrite dans la réponse de Timw, mais elle a le bon truc d'utilisation d'une classe de proxy d'usine modélisée pour gérer l'enregistrement de l'objet. Vaut bien un look :)

Objets auto-enregistrants en C ++ -> http://www.ddj.com / 184410633

EDIT

Voici l'application de test que j'ai faite (rangée un peu;):

objet_factory .h xxx

objets.h xxx

objets.cpp xxx

main.cpp xxx

sortie: xxx


2 commentaires

Mais ensuite, l'enregistrement est implicite et vous devez créer une instance de chaque classe de béton. Je préfère l'enregistrement explicite sans création d'objet.


Vous créez un proxy d'usine pour chaque classe de béton, mais les classes elles-mêmes ne sont créées que lorsque vous avez besoin, via l'usine. Je vais éditer la réponse avec du code quand je rentrerai à la maison ce soir :)