Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Nom de la classe dans une variable

8 réponses
Avatar
NDLCDUV
Bonjour,

Dans une application que je développe, je lis dans une table d'une base
de données le nom de la classe que je dois instancier.

En clair, le nom de la classe est contenu dans une variable. Comment
puis-je faire alors pour créer un objet de ce type ?

Exemple :

myvar = "MaClasse";

MaClasse *mc = new ??? <== Que dois je mettre ici pour réaliser
l'instanciation ?

Merci de votre aide.

8 réponses

Avatar
espie
In article ,
NDLCDUV wrote:
Bonjour,

Dans une application que je développe, je lis dans une table d'une base
de données le nom de la classe que je dois instancier.

En clair, le nom de la classe est contenu dans une variable. Comment
puis-je faire alors pour créer un objet de ce type ?

Exemple :

myvar = "MaClasse";

MaClasse *mc = new ??? <== Que dois je mettre ici pour réaliser
l'instanciation ?

Merci de votre aide.



Globalement, tu ne peux pas.

C++ n'a pas de proprietes de reflexivite, a l'instar d'autres langages...

Il y a des tonnes de solutions (le pattern souvent appele "constructeur
virtuel") mais tu vas avoir du boulot.

Tu peux toujours aller fouiller dans Boost, il y a certainement moult
outils pour te simplifier tes problemes de serialisation.
Avatar
Falk Tannhäuser
NDLCDUV schrieb:
Dans une application que je développe, je lis dans une table d'une base
de données le nom de la classe que je dois instancier.

En clair, le nom de la classe est contenu dans une variable. Comment
puis-je faire alors pour créer un objet de ce type ?

Exemple :

myvar = "MaClasse";

MaClasse *mc = new ??? <== Que dois je mettre ici pour réaliser
l'instanciation ?



Si toutes ces classes dérivent d'une classe de base commune (disons BaseClass), tu peux faire tout simplement :


BaseClass* Create(std::string const& name)
{
if (name == "BaseClass") return new BaseClass;
else if(name == "MaPremiereClasse") return new MaPremiereClasse;
else if(name == "MaSecondeClasse" ) return new MaSecondeClasse;
// etc ...
else return 0; // Si le nom est inconnu (éventuellement traitement d'erreur ici ?)
}


Si le nombre de classes est un peu plus important, des schémas plus sophistiqués peuvent être envisagés :
_________________________________________________________________________________

class BaseClass
{
public:
virtual ~BaseClass() {}
};

class MyFirstClass : public BaseClass {};
class MySecondClass : public BaseClass {};

typedef std::map<std::string, BaseClass* (*)()> FactoryMap;
FactoryMap factory_map;

template<typename DerivedClass> BaseClass* CallNew() { return new DerivedClass; }

BaseClass* Create(std::string const& name)
{
FactoryMap::const_iterator it = factory_map.find(name);
return it == factory_map.end() ? 0 : it->second(); // Éventuellement traitement d'erreur ?
}

#define ADDCLASS(DerivedClass) factory_map[#DerivedClass] = CallNew<DerivedClass>

...
// Initialisation : Ajouter ici toutes les classes souhaitées
ADDCLASS(BaseClass);
ADDCLASS(MyFirstClass);
ADDCLASS(MySecondClass);
...
// Utilisation :
BaseClass* pb = Create("MyFirstClass");
_________________________________________________________________________________

Idéalement le factory_map, le CallNew(), le Create() et l'initialisation seront encapsulés dans une classe Factory instanciée en
singleton, éventuellement "templatisée" avec BaseClass comme paramètre template...

Falk
Avatar
NDLCDUV
NDLCDUV a pensé très fort :
Bonjour,

Dans une application que je développe, je lis dans une table d'une base de
données le nom de la classe que je dois instancier.

En clair, le nom de la classe est contenu dans une variable. Comment puis-je
faire alors pour créer un objet de ce type ?



Je pensais éventuellement utiliser une simple fabrique, qui
contiendrait la méthode statique suivante :

MaClasseMere *FabriqueDeDocuments::omr = NULL; // Variable statique

MaClasseMere *FabriqueDeDocuments::CreateDocument(CString nomClasse)
// Méthode statique
{
omr = NULL;

if (nomClasse == "ClasseDerivee1")
omr = new ClasseDerivee1();

if (nomClasse == "ClasseDerivee2")
omr = new ClasseDerivee2();

return (MaClasseMere *)omr;
}

mais cette solution me parait très (trop ?) simple par rapport à celles
que vous me proposez. N'est ce pas pourtant une solution viable ?

Merci.
Avatar
Michael DOUBEZ
NDLCDUV wrote:
NDLCDUV a pensé très fort :
Bonjour,

Dans une application que je développe, je lis dans une table d'une
base de données le nom de la classe que je dois instancier.

En clair, le nom de la classe est contenu dans une variable. Comment
puis-je faire alors pour créer un objet de ce type ?



Je pensais éventuellement utiliser une simple fabrique, qui contiendrait
la méthode statique suivante :

MaClasseMere *FabriqueDeDocuments::omr = NULL; // Variable statique




Je ne vois pas l'intérêt de cette variable

MaClasseMere *FabriqueDeDocuments::CreateDocument(CString nomClasse) //
Méthode statique



Pas besoin de dire en commentaire qu'il s'agit d'une fonction de classe.
Il suffit de mettre static devant.

{
omr = NULL;

if (nomClasse == "ClasseDerivee1")
omr = new ClasseDerivee1();



Ou simplement
if (nomClasse == ... ) return new ClasseDerivee1();


if (nomClasse == "ClasseDerivee2")
omr = new ClasseDerivee2();

return (MaClasseMere *)omr;



Pas besoin de caster et surtout pas avec un cast C.

}

mais cette solution me parait très (trop ?) simple par rapport à celles
que vous me proposez. N'est ce pas pourtant une solution viable ?



Tout dépend de la quantité de couplage que tu peux supporter.
Typiquement, avec ta solution, tu devra recompiler ce fichier dès qu'une
des classes sera modifiée et il te faudra maintenir ta liste de classe.

Avec un simple système d'enregistrement, tu t'évites ce genre de
désagrément. Et le cout est relativement faible (sauf si tu as besoin
d'appeler ta factory avant d'entrer dans le main()).

--
Michael
Avatar
David Fleury
Marc Espie a écrit :

Globalement, tu ne peux pas.

C++ n'a pas de proprietes de reflexivite, a l'instar d'autres langages...



J'ai l'impression que tu exprimes un regret ici.
Du coup "à l'instar" (comme) fait bizarre ici
Mais si c'était pas l'idée alors désolé. Je me rendors :)

Mais ce n'est pas vraiment du C++ ;)

David
Avatar
espie
In article <49d3b7ca$0$5262$,
David Fleury wrote:
Marc Espie a écrit :

Globalement, tu ne peux pas.

C++ n'a pas de proprietes de reflexivite, a l'instar d'autres langages...



J'ai l'impression que tu exprimes un regret ici.
Du coup "à l'instar" (comme) fait bizarre ici
Mais si c'était pas l'idée alors désolé. Je me rendors :)



Non, pas vraiment de regret. Tu ne peux pas non plus faire de prolog
en C++, et ca n'est pas vraiment grave. Ca pousse juste les gens a developper
d'autres solutions, qui souvent fonctionnent "mieux" dans le contexte.

Mais ce n'est pas vraiment du C++ ;)



J'ai l'impression que le posteur initial voulait une technique equivalente
a ce qui est pratique en java.
Avatar
Sylvain SF
Marc Espie a écrit :

J'ai l'impression que le posteur initial voulait une technique equivalente
a ce qui est pratique en java.



dans sa question initiale oui, à tout le moins cela pouvait
faire "regretter" la réflexivité Java; dans son exemple posté
plus tard, beaucoup moins.

btw, Objective-C permettrait cela non ?

SF.
Avatar
pjb
NDLCDUV writes:

Bonjour,

Dans une application que je développe, je lis dans une table d'une
base de données le nom de la classe que je dois instancier.

En clair, le nom de la classe est contenu dans une variable. Comment
puis-je faire alors pour créer un objet de ce type ?

Exemple :

myvar = "MaClasse";

MaClasse *mc = new ??? <== Que dois je mettre ici pour réaliser
l'instanciation ?



Il faut que tu implémente la reflexivité toi même.



// Pseudo-code!
class Object {
};

class Class:public Object {
public:
typedef Object*(*Factory)();
protected:
std::string name;
Factory factory;
static std::map<std::string,Class*> classes;
public:
Class(std::string aName,Factory aFactory):name(aName),factory(aFactory){
classes[name]=this; }
static Class* classNamed(std::string aName){return(classes[aName]);}
Object* make(){ return(factory()); }
};

#define REGISTER(CLASSNAME) static Class CLASSNAME##_class(#CLASSNAME,&CLASSNAME::CLASSNAME)

Et alors, à chaque fois que tu déclare une classe, tu appelles REGISTER:

class MaClasse {
// ...
};
REGISTER(MaClasse);



std::string myvar = "MaClasse";

MaClasse *mc = dynamic_cast<MaClasse*>(Class::classNamed(myvar)->make());


Une meilleure solution serait d'utiliser Objective-C au lieu de C++.


En fait, dans tous les cas, une meilleure solution serait d'utiliser
autre chose que C++...

--
__Pascal Bourguignon__