Instancier une classe de nom?
imaginer, j'ai un tas de C++ classes associées (toute l'extension de la même classe de base et de fournir le même constructeur) que j'ai déclaré dans une commune de fichier d'en-tête (que je comprend), et leur mise en œuvre dans certains autres fichiers (que j'ai compiler et lier de manière statique dans le cadre de la compilation de mon programme).
Je voudrais être en mesure d'instancier l'un d'eux en passant le nom, qui est un paramètre qui doit être passé à mon programme (soit en ligne de commande ou comme une compilation de macro).
La seule solution que je vois est d'utiliser une macro:
#ifndef CLASS_NAME
#define CLASS_NAME MyDefaultClassToUse
#endif
BaseClass* o = new CLASS_NAME(param1, param2, ..);
Est-il la seule approche intéressante?
Vous devez vous connecter pour publier un commentaire.
Ce problème est généralement résolu en utilisant la Modèle De Registre:
Vous de mettre en œuvre un registre normalement à l'aide d'un objet singleton, l'objet singleton est informé au moment de la compilation ou au moment du démarrage, les noms des objets, et la manière de les construire. Ensuite, vous pouvez l'utiliser pour créer l'objet de la demande.
Par exemple:
Vous vous inscrivez les noms des objets et les fonctions de création de la sorte:
On peut donc simplifier avec des macros pour lui permettre d'être fait au moment de la compilation. ATL utilise le Registre de Modèle pour les CoClasses qui peut être créé au moment de l'exécution par le nom de l'enregistrement est aussi simple que d'utiliser quelque chose comme le code suivant:
Cette macro est placé dans votre fichier d'en-tête quelque part, de la magie, des causes qu'il puisse être enregistré avec le singleton au moment de la COM serveur est démarré.
Une façon de mettre en œuvre ce qui est de coder en dur une cartographie à partir de la classe de noms " à l'usine de la fonction. Les modèles peuvent rendre le code plus court. Le TSL peut rendre le codage plus facile.
EDIT -- à la demande générale 🙂 un peu de retravailler afin de rendre les choses plus lisse (crédits de Stone Free qui n'a pas voudrez probablement ajouter une réponse lui-même)
struct mapping { const char *classname; tConstructor constructor; operator map<string,constructor>::value_type() const { return map<string,constructor>::value_type(classname, constructor); } } mapping {[] = { { "class1", &fCreate<Class1> } , { "class2", &fCreate<Class2> } // , ... }; map< string, constructor > constructors(mapping,mapping+_countof(mapping));
Alors à chaque fois que la carte constructeur déréférence l'itérateur, il va appeler value_type à cooerce dans le type correct!.Pourquoi ne pas utiliser un objet de l'usine?
Dans sa forme la plus simple:
En C++, cette décision doit être prise au moment de la compilation.
Au cours de la compilation, vous pouvez utiliser une définition de type plutôt qu'un macor:
c'est l'équivalent et évite les macros (macros mal ;-)).
Si la décision doit être prise lors de l'exécution, vous devez écrire votre propre code à l'appui. Les simples solution est une fonction qui teste la chaîne et instancie la classe.
Une version étendue de ce (indépendante des sections de code à inscrire leurs classes) serait un
map<name, factory function pointer>
.Vous parler de deux possibilités de ligne de Commande et la compilation de macro mais la solution de chacun est très différent.
Si le choix est fait par une compilation de macro que c'est un simple problème qui peut être résolu avec #définit et #ifdefs et la comme. La solution que vous proposez est aussi bon que tout.
Mais si le choix est fait au moment de l'exécution à l'aide d'un argument de ligne de commande, alors vous devez avoir une Usine cadre qui est en mesure de recevoir une chaîne de caractères et de créer l'objet approprié. Cela peut être fait en utilisant un simple, statique
if().. else if()... else if()...
de la chaîne qui a toutes les possibilités ou peut-être un cadre dynamique où les objets s'inscrire et sont clonés pour fournir de nouvelles instances d'elles-mêmes.Bien que la question existe maintenant depuis plus de quatre ans, il est encore utile. Parce que l'appel pour le nouveau code inconnu au moment de la compilation et la liaison des principaux fichiers de code est dans ces jours-ci un scénario très commun. Une solution à cette question n'est pas mentionné du tout. Donc, je tiens à souligner le public à un autre type de solution ne se construit pas en C++. C++ lui-même n'a pas la capacité de se comporter comme
Class.forName()
connu de Java ou commeActivator.CreateInstance(type)
connu de .NET. En raison de raisons, qu'il n'y a pas de surveillance par une machine virtuelle pour JIT code à la volée. Mais de toute façon, LLVM, le faible niveau de la machine virtuelle, vous donne les outils nécessaires et les bibliothèques de lecture en mode compilé lib. Essentiellement, vous devez exécuter les deux étapes:clang -emit-llvm -o foo.bc -c foo.c
ParseIRFile()
méthode dellvm/IRReader/IRReader.h
pour analyser lesfoo.bc
fichier afin d'obtenir les fonctions de LLVM (lui-même ne connaît que des fonctions comme la bitcode est une conséquence directe de l'abstraction de CPU opcodes et assez unsimiliar de plus haut-niveau intermédiaire de représentations comme le bytecode Java). Se référer par exemple à ce l'article pour un code plus complet description.Après la mise en place de ces mesures esquissées ci-dessus, vous pouvez appeler dynamiquement également à partir de C++ avant inconnu de fonctions et de méthodes.
Dans le passé, j'ai mis en place le modèle de Fabrique de telle façon que les classes peuvent s'inscrire eux-mêmes à l'exécution sans l'usine elle-même avoir à connaître précisément à leur sujet. La clé est d'utiliser un non-standard compilateur fonction appelée (IIRC), "saisie par l'initialisation", dans lequel vous déclarez une dummy variable statique dans le fichier d'implémentation pour chaque classe (par exemple, un bool), et l'initialiser avec un appel à l'enregistrement de routine.
Dans ce schéma, chaque classe a à #inclure l'en-tête contenant de l'usine, mais l'usine connaît rien à l'exception de l'interface de la classe. Vous pouvez littéralement ajouter ou supprimer la mise en œuvre des classes à partir de votre construction, et de recompiler sans modification de code.
Le hic, c'est que seuls certains compilateurs support de fixation par l'initialisation - IIRC autres initialiser le fichier-portée des variables lors de la première utilisation (de la même façon la fonction locale de la statique de travail), qui n'est d'aucune aide ici, car la variable muette est jamais accès et l'usine de la carte sera toujours trouvé vide.
Les compilateurs je suis intéressé (MSVC et GCC) ceci, cependant, il n'est donc pas un problème pour moi. Vous aurez à décider pour vous-même si cette solution vous convient.