2010-01-15 14 views
3

Je cherche un moyen de déterminer à l'exécution, quel type d'objet doit être alloué (basé sur un nom de classe donné, qui est de type const char*). Eh bien la façon la plus simple est bien sûr d'utiliser des charges de if s/else if s, mais cela ne semble pas applicable, parce que j'ai plus de 100 classes différentes (enfin, elles dérivent toutes d'une classe de base), et j'ai ajouter régulièrement de nouvelles classes.Comment obtenir un objet d'une classe inconnue avec le nom de classe donné

Je suis déjà venu avec un premier projet, mais malheureusement, il ne marche pas compilez encore (MinGW & g ++ 4,4)

template<typename TBase, typename TDerived, typename... TArgs> 
Base* get_classobject(const char* classname) 
{ 
    if(strcmp(classname,typeid(TDerived).name())==0) 
     return new TDerived; // 
    else if(sizeof...(TArgs)>0) 
     return get_classobject<TBase,TArgs...>(classname); 
    else 
     return 0; 
} 


int main() 
{ 
    Base* obj = get_classobject<Base,A,Foo,B,C>("Foo"); 
    // ^- Types A B C and Foo are all derived from Base 
    delete obj; //of course we got an virtual dtor ;) 
    return 0; 
} 

mais que gcc d'arrêt sizeof...(TArgs)>0 ne marche pas essayer de générer du code pour get_classobject<TBase,const char*>(const char*) qui ne

Avez-vous une idée, comment résoudre ce problème ou une autre idée? Merci.

EDIT: Je l'ai résolu:

template<typename TBase, typename TDerived> 
Base* get_classobject(const char* classname) 
{ 
    if(strcmp(classname,typeid(TDerived).name())==0) 
     return new TDerived; 
    return 0; 
} 

template<typename TBase, typename TDerived, typename TArg, typename... TArgs> 
Base* get_classobject(const char* classname) 
{ 
    if(strcmp(classname,typeid(TDerived).name())==0) 
     return new TDerived; 
    return get_classobject<TBase,TArg,TArgs...>(classname); 
} 

EDIT Pour les lecteurs intéressés:
Vous devriez maintenant que la mise en œuvre est compilateur ne indépendant au-dessus du tout. La sortie de typeif(sometype).name() est spécifique au compilateur/implémentation. L'utilisation d'une variable ou d'une fonction static const char* name dans toutes les classes dérivées corrige cela, mais ajoute beaucoup de travail (bien sûr, vous pouvez utiliser une macro, mais si vous utilisez déjà des macros, vous pouvez également utiliser une autre méthode d'objet)

+0

Vous avez besoin d'implémenter une méthode factory et 'clone'. En utilisant une interface 'clone()', vous pouvez créer des objets d'une famille et ne pas avoir besoin de connaître le nom de leur classe. –

+0

Je ne veux pas paraître critique, mais vraiment ... ça sent mauvais. Tout d'abord l'utilisation de 'typeid' est assez controversée, mais je voudrais aussi souligner l'inefficacité d'une telle méthode> vous effectuez une recherche avec une complexité linéaire et vous devez réellement préciser TOUS les types possibles qui pourraient être générés. .. et je ne veux même pas penser au coût d'entretien cauchemardesque. –

+0

typeid peut être facilement substitué avec une fonction static :: name, la recherche linéaire n'est pas une issure puisque je n'appelle cette méthode qu'une seule fois (à part que toute autre usine d'objet a aussi une recherche linéaire ou abuse des macros) – smerlin

Répondre

3

tu ne peux pas simplement déclarer

template<typename TBase, typename TDerived, typename TArg, typename... TArgs> 

?

Ensuite, vous pouvez vous spécialiser dans le cas de

typename TBase, typename TDerived, typename TArg 
+0

wouldnt it échouer une étape de récurrence plus tôt si je fais ça? – smerlin

+0

Vous essayez d'obtenir 1+ paramètres de modèle, au lieu de 0+ droit? Vous pouvez spécialiser le modèle pour le cas 0. –

+0

a travaillé comme un charme une fois que je vous ai compris. – smerlin

2

Lire les réponses sur here, vous avez besoin probablement d'une usine.

0

Cela ressemble à vous recherchez le modèle d'usine d'objets classique. Jetez un oeil à ce stackoverflow question. Personnellement j'aime ça method

+0

lorsque vous utilisez ce modèle, vous devez écrire le code vous-même, en utilisant le compilateur pour moi;) – smerlin

+0

@smerlin Si je comprends * votre * manière, vous devrez chaîner/écrire toutes les classes dérivées chaque fois que vous devez appeler 'get_classobject()'. Donc, lorsque vous ajoutez une classe ou en supprimez une, c'est un processus administratif/manuel. La façon dont je décris est un peu plus * automatique *. Fondamentalement, vous avez seulement besoin d'ajouter deux lignes. METADECL dans la déclaration de classe et METAIMPL dans une unité de compilation (fichier .cpp), de préférence proche d'un constructeur. Cette fonctionnalité automatisée de cette solution est ce qui me plaît vraiment, on peut même ajouter des classes à l'exécution en chargeant des objets partagés (fichiers .so/.dll) Fait que ... – epatel

+0

je dois seulement appeler cette fonction une fois de toute façon, et si je devais les appeler plus d'une fois, j'ajouterais une fonction wrapper (inline), de sorte que je n'ai à écrire les classes qu'une seule fois – smerlin

1

Que diriez-vous de faire un get_classobject spécialisé() sans temlates variadiques? Cela arrêterait la récursivité.

Vous auriez alors une définition avec un modèle variadique, et une autre de seulement template<typename TBase, typename TDerived>. Une autre idée consiste à effectuer une surcharge hors modèle qui n'accepte que const char *, et renvoie 0.

+0

Déjà essayé, toujours obtenu 'error: aucune fonction correspondante pour l'appel à 'get_classobject (const char * &) ''' – smerlin