Utilisation des typelist en c++

Soyez le premier à donner votre avis sur cette source.

Snippet vu 4 526 fois - Téléchargée 19 fois

Contenu du snippet

==========================
Note admin codes-sources:
==========================

ATTENTION:
Ce code est un exemple de ce qu'il est possible de faire avec des templates. Néanmoins, ce code NE doit PAS être utilisé. En effet, tous les effets que ce code émule sont disponibles dans le langage. "dynamic_cast" et "typeid" se charge déjà de le faire, et le font bien mieux.

En C++ propre, tout ce code tiendrait en 3 lignes (voir même en un mot "dynamic_cast"):

#include <iostream>

template <typename CastType, typename ObjType>
bool isInstanceOf(ObjType obj)
{
return dynamic_cast<CastType>(obj);
}

struct A {};
struct B : public A {};
struct C {};

int main()
{
A a;
B b;
C c;
C* null = 0;

std::cout << std::boolalpha
<< isInstanceOf<A*>(&a) << std::endl // true
<< isInstanceOf<A*>(&b) << std::endl // true
<< isInstanceOf<B*>(&b) << std::endl // true
<< isInstanceOf<C*>(&c) << std::endl // true
<< isInstanceOf<C*>(null) << std::endl; // false

isInstanceOf<A*>(&c); // Ne compilera pas ! Et heureusement :)

return 0;
}

==========================

Ce code permet de créer une mécanique qui imite le dynamic_cast. Le seul but est de montrer ce que l'on peut faire avec des templates.

Exemple : 'MyObject' est un objet générique, Les autres objets que l'on créera hériteront de cet objet.

MyClass1 heritera de MyObject
MyClass2 heritera de MyObject

etc, etc

Ainsi on peu générer automatiquement l'arborescence d'un objet, verifier s'il est bien une instance d'un objet parent etc etc.

Par exempl :

class MyClass1 : public MyObject
{
/* ... */
};

class MyClass2 : public MyObject
{
/* ... */
};

MyObject* obj1 = new MyClass1();
MyObject* obj2 = new MyClass2();

cout << (obj1.instanceOf<MyClass1>() ? "true" : "false") << endl; //true
cout << (obj1.instanceOf<MyClass2>() ? "true" : "false") << endl; //false
cout << (obj2.instanceOf<MyClass1>() ? "true" : "false") << endl; //true
cout << (obj2.instanceOf<MyClass2>() ? "true" : "false") << endl; //false

On peut imaginer encore plus complex

class MyClass3 : public MyClass1
{
/* ... */
};

class Myclass4 : public MyClass2
{
/* ... */
};

MyObject* obj3 = new MyClass3();
MyObject* obj4 = new MyClass4();

cout << (obj3.instanceOf<MyClass1>() ? "true" : "false") << endl; //true
cout << (obj3.instanceOf<MyClass2>() ? "true" : "false") << endl; //false
cout << (obj3.instanceOf<MyClass3>() ? "true" : "false") << endl; //true
cout << (obj3.instanceOf<MyClass4>() ? "true" : "false") << endl; //false

cout << (obj4.instanceOf<MyClass1>() ? "true" : "false") << endl; //false
cout << (obj4.instanceOf<MyClass2>() ? "true" : "false") << endl; //true
cout << (obj4.instanceOf<MyClass3>() ? "true" : "false") << endl; //false
cout << (obj4.instanceOf<MyClass4>() ? "true" : "false") << endl; //true

Voila, voir le code pour comprendre comment cela fonctionne.

Source / Exemple :


#include <string>
#include <iostream>

using namespace std;

template<class __type, class __parent>
struct TypeList
{
    typedef __type type;
    typedef __parent parent;
};

#define INTERFACE(CLASSNAME) public:\
    typedef TypeList<CLASSNAME, CLASSNAME::type> type;\
    static inline std::string ClassName()\
    {\
        return type::parent::type::ClassName()+"."#CLASSNAME;\
    }\
    virtual inline std::string className()\
    {\
        return type::parent::type::ClassName()+"."#CLASSNAME;\
    }\
    static CLASSNAME* cast(void* __object)\
    {\
        CLASSNAME* object = (CLASSNAME*)__object;\
        if(object->instanceOf<CLASSNAME>())return object;\
        else return 0;\
    }\
    private:

#define DEFINE_INTERFACE(CLASSNAME) public:\
    typedef TypeList<CLASSNAME, CLASSNAME> type;\
    static inline std::string ClassName()\
    {\
        return #CLASSNAME;\
    }\
    virtual inline std::string className()\
    {\
        return ClassName();\
    }\
    template<class __type>\
    bool instanceOf()\
    {\
        int res = className().find(__type::ClassName());\
        return res >= 0;\
    }\
    static CLASSNAME* cast(void* __object)\
    {\
        CLASSNAME* object = (CLASSNAME*)__object;\
        if(object->instanceOf<CLASSNAME>())return object;\
        else return 0;\
    }\
    private:

class MyObject
{
    DEFINE_INTERFACE(MyObject)
};

class MyClass1 : public MyObject
{
    INTERFACE(MyClass1)
};

class MyClass2 : public MyObject
{
    INTERFACE(MyClass2)
};

#define AFFICHER(X) #X" = " << X

int main(int argc, char** argv)
{
    MyObject object;
    MyClass1 class1;
    MyClass2 class2;

    cout << object.className() << endl;
    cout << class1.className() << endl;
    cout << class2.className() << endl;

    cout << endl;

    cout << AFFICHER(object.instanceOf<MyObject>()) << endl;
    cout << AFFICHER(class1.instanceOf<MyObject>()) << endl;
    cout << AFFICHER(class2.instanceOf<MyObject>()) << endl;

    cout << endl;

    cout << AFFICHER(object.instanceOf<MyClass1>()) << endl;
    cout << AFFICHER(class1.instanceOf<MyClass1>()) << endl;
    cout << AFFICHER(class2.instanceOf<MyClass1>()) << endl;

    cout << endl;

    cout << AFFICHER(object.instanceOf<MyClass2>()) << endl;
    cout << AFFICHER(class1.instanceOf<MyClass2>()) << endl;
    cout << AFFICHER(class2.instanceOf<MyClass2>()) << endl;

    cout << endl;

    cout << AFFICHER(MyObject::cast(&object)) << endl;
    cout << AFFICHER(MyClass1::cast(&object)) << endl;
    cout << AFFICHER(MyClass2::cast(&object)) << endl;

    cout << endl;

    cout << AFFICHER(MyObject::cast(&class1)) << endl;
    cout << AFFICHER(MyClass1::cast(&class1)) << endl;
    cout << AFFICHER(MyClass2::cast(&class1)) << endl;

    cout << endl;

    cout << AFFICHER(MyObject::cast(&class2)) << endl;
    cout << AFFICHER(MyClass1::cast(&class2)) << endl;
    cout << AFFICHER(MyClass2::cast(&class2)) << endl;

    return 0;
}

Conclusion :


Je pense que cela peut vous servir.

A voir également

Ajouter un commentaire

Commentaires

Messages postés
1
Date d'inscription
jeudi 6 mars 2008
Statut
Membre
Dernière intervention
21 août 2011

Bien toutes tes questions on une simple réponse.

Le développement ce n'est pas facile, les nouveaux utilisent des libs, des systemes qui existe deja sans avoir une idé global de comment coder soit meme un mécanisme du meme type que ce qu'ils utilisent.

Ce que j'ai mis la ce n'est pas à utiliser dans un projets, c'est une manière d'ajouter des idées dans la tête des nouveaux développeurs.

Mais merci pour tes critiques, je t'assure que ca aidera les autres.

Bonne continuation.
Messages postés
3813
Date d'inscription
dimanche 12 décembre 2004
Statut
Modérateur
Dernière intervention
12 juin 2020
107
Bonjour.

Je ne sais pas trop par quoi commencer. Il y a plein de choses qui me gênent dans ce code.

Je ne vois pas l'utilité d'avoir recodé un système déjà existant en moins bien. Quel intérêt quand on a un dynamic_cast et des typeid ? (http://en.wikipedia.org/wiki/Typeid). On peut déjà faire tout ce que tu fais sans ajouter d'instructions intrusives dans son code.

De plus, au niveau architecture logiciel, dès le moment où toute classe hérite d'une super classe, c'est qu'il y a 99% de chance que la conception soit mauvaise.
J'ai aussi du mal à comprendre pourquoi il y a autant de macro, alors qu'une bonne partie du code aurait pu être écrite sous la forme de template.
Enfin, avoir des méthodes au nom identique mais à la casse différente, c'est très très risqué, et source d'ambiguïtés. D'ailleurs pourquoi avoir deux fois la même méthode dans ta première classe ?

Au niveau du code lui même, j'ai quelque remarques:
- Évite les using namespace, voir: http://0217021.free.fr/portfolio/axel.berardino/articles/bon-usage-using-namespace
- En C++, quand une méthode prend en argument un void*, c'est très très louche. On utilise généralement une méthode template pour éviter cela.
- Utilise des cast C++ et non des casts C. Le cast C est un fourre-tout qui ne te permet pas d'avoir un contrôle sur le type de cast que tu fais (renseigne toi sur: reinterpret_cast, static_cast, et dynamic_cast). Ici j'aurais plutôt fait un static_cast.
- Pour savoir de manière portable si la recherche dans un std::string a réussi, on ne regarde pas si res >= 0 mais si res != std::string::npos. D'ailleurs la méthode find renvoie normalement dans la plupart des implémentations de la STL, un size_t qui est non signé (donc récupérer cela avec un int signé c'est moyen).
- Pas besoin de else après un return (le return est débranchant)
- Au lieu de faire des MyObject::cast(&class1) ? "true" : "false" utilise directement des: std::boolalpha << MyObject::cast(&class1)
- Enfin, au lieu de faire des milliards de std::cout, n'en fait qu'un seul et chaîne tes actions...
Ex:
cout << object.className() << endl;
cout << class1.className() << endl;
cout << class2.className() << endl;

Devient:
std::cout << object.className() << std::endl
<< class1.className() << std::endl
<< class2.className() << std::endl;

Pour conclure, si ton but est uniquement de montrer une technique à travers un exemple, précise que cet exemple ne doit pas être utilisé en production, mais qu'il faut privilégier les mécanismes déjà existant du langage.
Ce sont ici des débutants qui lisent ces codes, et ça serait dommage qu'ils pensent que c'est la bonne méthode pour vérifier si une classe héritent d'une autre. (Soit dit en passant, si le programme est bien conçu, on n'a jamais besoin de savoir si une classe hérite d'une autre, le polymorphisme fait son travail naturellement).

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.