IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

  1. #1
    Membre éclairé Avatar de cs_ntd
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Décembre 2006
    Messages
    598
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2006
    Messages : 598
    Par défaut Récupérer le type d'un param... => EDIT : Stocker des variables d'un type différents dans un même conteneneur
    [EDIT]
    Ancien titre : Récupérer le type d'un parametre et retourner un pointeur sur une valeur de ce type

    Je l'ai modifié car il ne correspondait plus au problème...
    [/EDIT]


    Bon dimanche à tous !

    Bon derrière ce titre mal tourné ce cache le fait que j'ai un petit souci de conception que je ne sais résoudre...

    En fait la problématique est la suivante :

    J'ai une classe que je vais appeler "Object". Le problème de cette classe est qu'elle est très générale : je ne sais absolument pas "quoi", et surtout que devra faire un objet de type Object...
    Et le principale problème était de pouvoir stocker des paramètres "d'évolution" de manière transparente, cependant les paramètres pouvait être de n'importe quel type.
    C'est pour ça que dans ma class "Objetc", j'ai un attribut membre "paramètre" de type "std::vector<long>", dans lequel je stock :

    les valeurs des pointeurs associés aux paramètres "réels"
    (Edit : d'ailleurs j'y pense : ne vaudrait il pas mieux stocker des pointeurs "void *" plutôt que des long ???)

    Pas de souci pour rentrer les adresses dans mon vector, par contre pour les sortir ...

    Bon pour les faires rentrer, j'ai une fonction
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Object::param_push_back(void *param);
    et roule ma poule.
    Après au niveau de l'appel, ça reste assez user-friendly :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    Object object1(...);
    A_type param1;
    object1.param_push_back(&param1);
    Et si après je veux le récupérer, je n'ai pour l'instant pas trouver mieux que de faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    std::vector<long>::iterator it;
    ...
    A_type param2 = *((A_type*)(*it));
    Ce qui est très moyen au niveau de l'"use-friendly".
    L'ideal serait une fonction par exemple "get_param()", pour que l'on puissee faire "param2 = get_param();" voir "param2 = *get_param();".

    J'ai bien penser à faire un truc du genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    void* get_param(void *param, vector<long>::iterator jt)
    {
        return ((typeof(*param)*)(*jt)
    }
    
    ...
    
    A_type param2(...);
    param2 = *get_param(&param2,it);
    Mais bien sur ça marche pas, j'ai plein d'erreurs :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void* is not a pointer-to-object type
    Et en plus j'utilise "typeof()" ce qui d'après ce que j'ai lu semblerai être une fonction propre à gcc, donc ça me plait pas vraiment.

    Est_ce que par miracle quelqu'un aurait une idée de comment je pourrait, en passant un paramètre dans ma fonction get_param, comment récupérer, soit un pointeur sur la valeur, soit la valeur directement ?

    Je vous remercie par avance de toute l'aide que vous voudrait bien m'apporter

  2. #2
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Salut,
    Je te conseille de t'intéresser vivement à Boost.Any (ou au minimum au type erasure). tout ce que tu décris ressemble plus à du code présentant beaucoup de défaut, d'approximation, limite des erreurs là où il existe des solutions adéquates avec cette approche.

  3. #3
    Membre éclairé Avatar de cs_ntd
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Décembre 2006
    Messages
    598
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2006
    Messages : 598
    Par défaut
    Wow merci de ta réponse

    Je ne connaissai pas trop l'un, et pas du tout les "type erasure" !

    Le seul problème est que c'est dans un cadre scolaire : je ne peut donc pas utiliser boost (pas standard et c'est pas moi qui l'ai fait).
    Après pour le type erasure, pourquoi mais bon :

    1) J'ai pas tout compris c'est un peu au dessus de mes compétences (surtout sur la fin le truc avec les politiques et tout, j'ai du louper un truc ^^).
    2) Je ne suis pas sur que ça corresponde bien à ce que je veux faire, C'est à dire :

    Je souhaite, que lorsqu'un futur utilisateur utilisera ma librairie, qu'il ne se préocupe pas de ce qu'il y a derrière, c'est à dire, qu'il ne s'occupe pas de faire dériver ses int ou ses double d'une classe base...
    Après, j'ai certains objets, dont je ne connais pas du tout l'utilisation qui en sera fait, mais je sais qu'il y aura besoin de sauvegarder certaines informations sur cet objet bien précis (et dans mon idée, ces informations devaient devenir le plus proche possible d'attributs de l'objet).

    Dans l'absolue, je ne voudrait pas que cela soit plus compliquer que de faire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    int a = 5;
    obj.insert(&a);
     
    //Puis plus tard pour récupérer
     
    int b = *get_info(position);
    A l'utilisateur par contre de se débrouiller pour se rappeler quel est le type de quoi, et à quelles données correspond quelle position...

    Est-ce qu'on peut vraiment faire aussi simple avec le type erasure ??? Est-ce quon peut vraiment cacher la partie héritage à l'utilisateur ? Si vraiment on peut faire aussi simple pourquoi pas...

    Sinon en répondant je crois que j'ai trouvé la solution à mon problème en fait
    Voici ce à quoi j'ai pensé (et qui fonctionne d'après mes tests) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
     
    typedef std::vector<void *> void_vect; //Ou vector<long> ? C'était ma première hypothèse à la base, mais ça n'a pas l'air de changer grand chose
    typedef void_vect::const_iterator position;
    class Object
    {
        void_vect  all_type_vect;
        position it;
    public:
        Object();
        ~Object();
     
     
        position push_back(void *);
     
        template<class T>
        T* get_pointed(position);
    }
     
    position Object::push_back(void *param)
    {
        all_type_vect.push_back(param);
        return all_type_vect.end()--;
    }
     
    template<class T>
    T* get_pointed(position jt)
    {
        return ((T*)(*jt));
    }
    Ce qui pourrait s'utiliser avec ce code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
    struct A{...}
     
    int main()
    {
        Object obj;
        int i = 0;
        A a(...);
     
        position pos = obj.push_back(&i);
        obj.push_back(&a);
        obj.push_back(new int(5));
        obj.push_back(new A(...));
     
        std::cout << "int i = " << *get_pointed<int>(pos) << endl; //Récupération de la véritable valeur.
        int b = *get_pointed<A>(pos + 1); //Copie de la valeur pointée dans un nouvel objet
        A *b = get_pointed<int>(pos + 2); //Passage de l'adresse à b
        return 0;
    }
    Mais j'avoue que je n'ai pas encore pensé à la destruction de l'objet... Mis à part cela vous en pensez quoi franchement ?
    Ca marche ? marche pas ?
    En comparaison du type erasure ça parait plus simple/moins simple ?
    Plus casse gueule/moins casse gueule ?
    Y a un truc auquel je n'ai pas pensé ?

    Merci encore de toutes vos réponses

  4. #4
    Membre chevronné Avatar de Lavock
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    560
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 560
    Par défaut
    Dans la mesure ou c'est de l'OO, je te conseil de faire une interface qui possède les méthode getSave et setRestore. Mais j'avoue que j'ai pas bien compris.

    Je te proposerai aussi le Factory (notamment abstract factory), juste pour voir si ça correspond pas à ton problème...

  5. #5
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    @cs_ntd : le type erasure te permet de ne pas passer par un void* pour stocker tes objets mais par un type défini et donc bénéficier du typage statique du C++. Conjugué avec une fonction comme any_cast<T>, tu peux utiliser une 'union générique' comme tu le souhaites tout en bénéficiant des avantages du typage.
    Ta solution avec des void* (pas des long qui seront dépendant du compilo/plateforme) nécessite des reinterpret_cast à tout va et détruisent la sécurité du typage.
    Reprends le tuto sur le type erasure à tête reposée et lit bien la partie Réécrivons boost::any. Elle se propose de redéfinir Boost.Any ... donc tu peux t'en inspirer sans avoir à utiliser Boost. J'ai quand même l'impression que cela correspond à ton besoin : avoir un conteneur qui puisse contenir n'importe quel objet et le retrouver ensuite avec le bon type.

  6. #6
    Membre éclairé Avatar de cs_ntd
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Décembre 2006
    Messages
    598
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2006
    Messages : 598
    Par défaut
    Re tout le monde,

    Soit dit en passant, ça doit ête le post que j'ai mis le plus long temps à écrire, tellement j'ai marqué de truc, puis tout effacé, puis tout réécris

    @3DArchi :

    Oué ça correspond en effet, il me semble... Le seul problème, c'est que y a un endroit ou j'ai pas compris comment la magie opère mais le résultat reste là.

    Ce que je ne comprend pas, c'est ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class any
    {
    	value_base* v;
    	
    	public:
    	any() : v(0) { }
    	
    	template <class value_type>
    	any(const value_type& v_) : v(new value<value_type>(v_)) { }
    	
    	~any() { delete v; }
    };
    [/COLOR]
    1) Dans la partie any():v(0) on fait vraiment appel au constructeur d'un pointeur ?? ça veut dire qu'on initialise un pointeur avec l'adresse 0 ?

    2) Ensuite, c'est dans le code, par exemple "any a = 4"... Coment est-ce que ça marche ? Je ne saisi pas du tout le mécanisme... J'aurais eu tendance à écrire (c'est sans doute faux, mais bon) quelque chose du genre :
    any<int> a(4) ou encore
    any a<int>(4)
    vu que le constructeur est un constructeur "template"... Ca veut dire que le constructeur récupère tout seul le bon type ??? La pour moi c'est très très mystérieux :o

    Sinon pour le reste... Presque tout compris !

    Ou pas ! Je crois que c'est encore trop subtil pour moi, parcequ'au final, je ne vois pas tout le temps la différence avec mon code, ni la raison de certains choix (dans le re-codage de any).

    Par exemple, il est dit (partie Limites et conclusion) que :
    on ne peut pas récupérer l'information que l'on a perdu sur le type précis de départ
    Donc au final, un objet "any" contient bien un pointeur sur un type "value_base", qui est certe un type, mais qui ne correspond à rien de "concret". Donc au final on a une adresse sur un objet qui peut être n'importe quoi (c'est le but nan ?)
    Mais moi de mon coté, je stock aussi des pointeurs ayant perdu le type sur lequel ils pointaient au départ... Alors si en plus, dans ma fonction "get_pointed", si à la place de faire une conversion (T*), je fait une conversion "dynamic_cast<T>", je ne vois plus de différence du tout...

    Tellement de choses à apprendre et comprendre encore ... ...

    @Lavock :
    Question noms, j'ai pris les premiers trucs qui me sont passés par la tête ^^.
    Sinon l'abstract factory baaa je crois que je comprend encore moins bien le mécanisme que pour le type erasure , mais pour ce que j'en ai compris, il me semble que ça ne me serve pas trop dans le cas présent :

    pouvoir stocker un objet de type T1 dans un conteneur
    puis stocker un autre objet, de type T2 dans le même conteneur
    etc...
    puis récupérer ces valeurs
    Le tout d'une manière transparente à l'utilisateur...
    (càd qu'il n'ai pas à se soucier de savoir si c'est un vector ou autre que j'utilise, si j'ai défini un type any ou pas, et sans qu'il est besoin de faire hériter ses types de quelque chose...)


    Je ne saurais vous remercier tous assez pour votre aide

  7. #7
    Membre Expert
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 34
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Par défaut
    v(0) ça revient à v = 0 ni plus ni moins. donc oui t'initialises ton pointeur à null.

    Pour le ctor template, comme toute fonction template il est capable d'inférer le type des arguments, et donc à l'appel tu n'as pas a spécifier le type.
    Ah et c'est le ctor qui est appelé car :
    Type a = anotherType; appel un constructeur et non l'op= comme on pourrait le croire.

  8. #8
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Salut,
    1/Comme le dit Goten v(0) initialise le pointeur à nul.
    2/Type p = v; correspond à Type p(v);
    L'appel du constructeur template, comme toutes les fonctions templates, peut ne pas préciser le type générique si celui ci peut être déduit des arguments.

    Sinon, tout l'intérêt d'any est bien de stocker une valeur de n'importe quel type et de le récupérer de façon sûr. Quand le tuto écrit :
    on ne peut pas récupérer l'information que l'on a perdu sur le type précis de départ
    Cela veut dire que pour récupérer la valeur tu dois savoir de quel type il est.

    value<T> dérive de value_base, donc un dynamic_cast de v vers value<int> réussi si v est bien un value<int> et échoue s'il s'agit d'un value<std::string>. C'est en ce sens qu'on continue d'avoir un typage fort.

    void*p efface complètement le type. dynamic_cast<T*>(p), ben, c'est pas possible. Ce qui est autorisé c'est dynamic_cast<derivée*>(base*) (ou avec des références, + const). Donc il te faut passer par un cast forcé. Et cast (T*)p réussi toujours même si au départ p n'était pas de type T. D'où une moindre sécurité.

    N'hésites pas à relire l'article tant que tu ne comprends pas trop car c'est vraiment un concept intéressant.

  9. #9
    Rédacteur

    Avatar de Davidbrcz
    Homme Profil pro
    Ing Supaéro - Doctorant ONERA
    Inscrit en
    Juin 2006
    Messages
    2 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ing Supaéro - Doctorant ONERA

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 307
    Par défaut
    Ouais, enfin dans tous les cas, une classe Object racine, c'est signe de problèmes.

    On est en C++,pas en Java. Je te laisse retrouver le post de Koala qui en parle.
    "Never use brute force in fighting an exponential." (Andrei Alexandrescu)

    Mes articles dont Conseils divers sur le C++
    Une très bonne doc sur le C++ (en) Why linux is better (fr)

  10. #10
    Membre chevronné Avatar de Lavock
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    560
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 560
    Par défaut
    Déjà, je suis pas sur d'avoir compris ce que tu voulais (mais c'est mieux que la dernière fois).

    Le truc qui est moche, c'est que tu as besoin de connaitre la classe dérivé pour dynamic-caster... Un simple forward ne suffit pas. Cela rejoint aussi la remarque précédente d'arch : tu dois connaitre le type que tu veux pouvoir pour le récupérer.

    Bon, y a peut-être moyen de s'en sortir avec les déclaration template extern et les forwards aussi.

  11. #11
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Citation Envoyé par Davidbrcz Voir le message
    Ouais, enfin dans tous les cas, une classe Object racine, c'est signe de problèmes.
    Tout à fait d'accord. Boost.Any (ou un équivalent) permet justement de s'en passer.

  12. #12
    Membre éclairé Avatar de cs_ntd
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Décembre 2006
    Messages
    598
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2006
    Messages : 598
    Par défaut
    OOOK !!

    Ca y est, je crois que j'ai saisi le truc !

    Goten et 3DArchi :
    1) pour v(0) au final c'est logique, je sais pas pourquoi ça m'a tant surpris...
    2) pour les fonctions templates, je n'avais jamais vu avant qu'on pouvait se passer du type si il peut être déduis ! Pratique du coup !

    Sinon pour le problème des dynamic_cast et tout, je pense que j'ai compris le mécanisme enfin ^^
    un dynamic_cast de v vers value<int> réussi si v est bien un value<int> et échoue s'il s'agit d'un value<std::string>
    Donc en quelque sorte, on pourrait dire que l'on ne peut pas déduire le type "réel", mais que si on l'indique, on est capable de voir si il correspond bien au pointeur mis en mémoire ?
    Je ne me trompe pas dans ma reformulation ?

    Et cast (T*)p réussi toujours même si au départ p n'était pas de type T. D'où une moindre sécurité
    Certe ! je me rappelle quelques jolis crash pendant mes tests, alors que je n'indiquais pas le bon type pour la bonne donnée...

    N'hésites pas à relire l'article
    T'inquiète, je ferme les yeux, et je le vois qui s'inscrit sur mes paupières

    La seule chose qui m'intrigue encore, est cette remarque au début de l'article sur la réécriture de boost::any:
    Citation Envoyé par Réécrivons boost::any
    je voudrais disposer d'un type qui puisse stocker des valeurs de (presque) n'importe quel autre type
    Pourquoi presque ??? Qu'y a-t-il que l'on ne puisse pas stocker ?

    Davidbrcz:
    une classe Object racine, c'est signe de problèmes
    C'est à dire ? à quel point de vu les problèmes ? J'ai pas trouvé le post

    Lavock:
    Alors les extern template, aparrement c'est pour C++0x hu hu donc moi à on avis, mon compilo à pas du l'intégrer, sans compter que la doc dessus est pas très fournie



    Ceci dit, vu que j'ai globalement compris ou était mon problème et compris comment le résoudre, je vais considérer ce topic comme résolu !

    Merci beaucoup à tous !!

  13. #13
    Rédacteur

    Avatar de Davidbrcz
    Homme Profil pro
    Ing Supaéro - Doctorant ONERA
    Inscrit en
    Juin 2006
    Messages
    2 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ing Supaéro - Doctorant ONERA

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 307
    Par défaut
    Pas trop le temps de cherche le message mais il disait en substance que les classes génériques dont héritent toutes les autres classes n'apporte rien qui justifie leur existence. Leur présence brise juste le LSP en permettant de mélanger tout et n'importe et les fonctions qu'elle assure peuvent être transformée/déplacée.

    D'ailleurs tu le dit toi même:
    J'ai une classe que je vais appeler "Object". Le problème de cette classe est qu'elle est très générale : je ne sais absolument pas "quoi", et surtout que devra faire un objet de type Object...
    Si tu ne sait pas à quoi à sert une classe (que tu as toi même créée); alors il est grand de se poser des questions sur ton code et de revoir ta conception.
    "Never use brute force in fighting an exponential." (Andrei Alexandrescu)

    Mes articles dont Conseils divers sur le C++
    Une très bonne doc sur le C++ (en) Why linux is better (fr)

  14. #14
    Membre chevronné Avatar de Lavock
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    560
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 560
    Par défaut
    C'est tout l'intérêt du any. Tu ne connais pas le type que tu manipule, mais tes interlocuteurs oui. Et il est effectivement plus propre de ne pas leur imposé un héritage bidon.

    Dans l'interêt du LSP, tu peux toujours faire une interface Reconaissable, qui sera juste charger de se reconnaitre...

  15. #15
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Citation Envoyé par cs_ntd Voir le message
    Donc en quelque sorte, on pourrait dire que l'on ne peut pas déduire le type "réel", mais que si on l'indique, on est capable de voir si il correspond bien au pointeur mis en mémoire ?
    Je ne me trompe pas dans ma reformulation ?
    C'est plus fort que ça.
    Une variable a 2 types : le type statique, celui de ton code, qui est connu à la compilation; et un type dynamique qui dépend de l'objet effectivement en mémoire. Avec un type normale (pas d'héritage) ou une variable par valeur, le type statique et le type dynamique coïncide.
    Mettons que tu as une classe Base et une classe Derive qui dérive publiquement de Base. Mettons que Base a au - une fonction virtuelle (par expl, son destructeur). Maintenant, tu as un pointeur (ou une référence) sur Base. dynamic_cast va chercher le type dynamique du pointeur et s'il correspond à celui que tu demandes, alors il te retourne un pointeur du type demandé. Sinon, il échoue et te retourne un pointeur NULL. En ce sens, il permet un typage plus fort. Tu peux regarder sur mon tuto sur les fonctions virtuelles les sections où je parle des types statiques/dynamiques et du dynamic_cast.
    J'ai pas encore mis à jour mais le pointeur de Derive peut être différent du pointeur Base en cas d'héritage multiple ou virtuel. D'où l'importance d'utiliser dynamic_cast.

    Citation Envoyé par cs_ntd Voir le message
    Certe ! je me rappelle quelques jolis crash pendant mes tests, alors que je n'indiquais pas le bon type pour la bonne donnée...
    Evites les casts à la C : (type)valeur et les void*. Tu élimineras beaucoup de soucis.

    Citation Envoyé par cs_ntd Voir le message
    La seule chose qui m'intrigue encore, est cette remarque au début de l'article sur la réécriture de boost::any:
    Pourquoi presque ??? Qu'y a-t-il que l'on ne puisse pas stocker ?
    Je ne connais pas l'article par coeur , mais je pense qu'il fait allusion au fait que le type doit être constructible par copie :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    template <class T>
    class value
    {
      T t;
     
      public:
      value(const T& t_) : t(t_) { } // nécessite que T soit constructible par copie
    };
    Il y a peut être d'autres restrictions, mais rapidement, elles ne me sautent pas aux yeux.

    Citation Envoyé par cs_ntd Voir le message
    C'est à dire ? à quel point de vu les problèmes ? J'ai pas trouvé le post
    Citation Envoyé par Davidbrcz Voir le message
    Pas trop le temps de cherche le message mais il disait en substance que les classes génériques dont héritent toutes les autres classes n'apporte rien qui justifie leur existence. Leur présence brise juste le LSP en permettant de mélanger tout et n'importe et les fonctions qu'elle assure peuvent être transformée/déplacée.
    -> Pour moi, je vois le fait de dériver tous les objets d'un BigMasterGodObject, comme une façon de faire du void* en croyant faire de l'objet.

  16. #16
    Membre éclairé Avatar de cs_ntd
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Décembre 2006
    Messages
    598
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2006
    Messages : 598
    Par défaut
    Attention attention !

    Il est vrai que la classe "Object" que j'ai introduit ici ne sert strictement à rien, mais c'est très loin d'être en réalité une classe "creuse" !!! Loin de la !
    Si je n'ai pas indiqué les autres "fonctionnalités", c'est parce que je pensait qu'elles étaient inutiles à la résolution de mon problème, mais il est vrai que le contexte a été source d'un malentendu.

    Mon but en créeant une class Object, n'était pas d'introduire une classe dont tous les autres objets pourraient dériver, mais c'est vrai que le nom peut paraitre mal choisi

    En fait, si vous voulez connaitre le concept, "Object" représente un objet graphique, composé notemment d'un ensemble de point, de la manière de le dessiner, fournissant une méthode de dessin, un algorithme d'optimisation du tracé.....
    Et en fait, à l'interieur de cette classe, j'ai besoin de définir un conteneur qui puisse contenir "n'importe quoi", donc en fait, un std::vector<any>

    Pourquoi ? Parceque certains objets n'auront pas besoins des mêmes paramètres que les autres, mais aussi que certaines instances bien précises pourront avoir des paramètres différents.

    Exemple : Je veux dessiner un cercle (par points, donc je cré un objet "Object", dans lequel j'insère mes points). A ce niveau, pas besoin de distinguer cercle et carré par exemple : ce sont 2 surfaces fermées définies par un ensemble de points (donc je veux dire par la, pas d'héritage).

    Sauf que la 1ère instance de ce cercle sera fixe (i.e. elle ne se déplacera pas sur mon écran), mais elle devra contenir une chaine de caractères (disons de type std::string) qu'elle devra écrire à chaque fois que cet instance est dessinée.
    La deuxième instance elle, devra bouger (donc il lui faut un paramètre coordonées, disons de type Point), mais n'aura rien à écrire.

    Deux problèmes :
    1) pourquoi ne pas définir un attribut de Object de type string, et un autre de type point ? Parceque autant ne pas réserver de la mémoire pour rien, surtout que les attributs pourraient être d'un type plus volumineux.
    2) Mais surtout, je ne sais pas encore quels seront les types nécessaires, car les objets n'auront pas de comportement prédéfinis, càd c'est l'utilisateur qui décidera entièrement de ce que doit faire un Object, de son comprtement, et donc des paramètres qui lui sont nécessaires. Et pour éviter de devoir à chaque fois passer comme arguments des paramètres + l'object, et de trainer le paramètre partout, je préfère passer un Object seul, avec le paramètre comme attribut...

    D'où la nécessité selon moi d'avoir un membre de type 'any' dans ma classe... Après il y avait peut-être une autre manière de faire, mais que je ne connais pas, et que je n'ai pas imaginé...

    Donc non, mon but n'était pas de créer un type BigMasterGodObject qui représenterait tout ce qui existe dans l'univers




    Pour en revenir à mes questions sur any :

    3DArchi : D'accord, ça reste très lié à l'héritage... Bon je te promet d'apprendre tes tutos par coeur . Plus sérieusement, je crois qu'il va falloir que je les relise plusieurs fois avant de bien assimiler le concept. C'est le genre de truc que tu lit, tu te dit "C'est bon j'ai compris !" , tu le relis une semaine plus tard, et tu te rend compte que en fait et baaa ta pas compris grand chose
    Nan ça reste un peu frais comme connaissances les pointeurs et tout ça... va me falloir encore un peu de temps pour maitriser la Chôse...

    Bon donc sinon pour le presque, , j'eu cru que les constructeurs par recopie étaient définis automatiquement, apparement ça ne l'est pas dans certains cas, mais bon si ça reste que ça...

  17. #17
    Rédacteur

    Avatar de Davidbrcz
    Homme Profil pro
    Ing Supaéro - Doctorant ONERA
    Inscrit en
    Juin 2006
    Messages
    2 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ing Supaéro - Doctorant ONERA

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 307
    Par défaut
    Si ta classe Object (que j'aurais plus appelée Drawable ou un truc du genre) définit des comportement commun entre tous les objets qui peuvent être dessinés, alors tu veux utiliser du polymorphisme dynamique. Il ne te faut donc pas un conteneur de any mais un conteneur de Object*.

    Sur ce conteneur, tu appelera des méthodes dont l'interface est définie par la classe Object (fonction virtuelle pure) mais qui seront implémentée dans les classes dérivées.

    Pourquoi ? Parceque certains objets n'auront pas besoins des mêmes paramètres que les autres, mais aussi que certaines instances bien précises pourront avoir des paramètres différents.

    Exemple : Je veux dessiner un cercle (par points, donc je cré un objet "Object", dans lequel j'insère mes points). A ce niveau, pas besoin de distinguer cercle et carré par exemple : ce sont 2 surfaces fermées définies par un ensemble de points (donc je veux dire par la, pas d'héritage).
    Sauf que la 1ère instance de ce cercle sera fixe (i.e. elle ne se déplacera pas sur mon écran), mais elle devra contenir une chaine de caractères (disons de type std::string) qu'elle devra écrire à chaque fois que cet instance est dessinée.
    La deuxième instance elle, devra bouger (donc il lui faut un paramètre coordonées, disons de type Point), mais n'aura rien à écrire.

    Deux problèmes :
    1) pourquoi ne pas définir un attribut de Object de type string, et un autre de type point ? Parceque autant ne pas réserver de la mémoire pour rien, surtout que les attributs pourraient être d'un type plus volumineux.
    2) Mais surtout, je ne sais pas encore quels seront les types nécessaires, car les objets n'auront pas de comportement prédéfinis, càd c'est l'utilisateur qui décidera entièrement de ce que doit faire un Object, de son comprtement, et donc des paramètres qui lui sont nécessaires. Et pour éviter de devoir à chaque fois passer comme arguments des paramètres + l'object, et de trainer le paramètre partout, je préfère passer un Object seul, avec le paramètre comme attribut...
    Ce sont des détails d'implémentations. Osef de comment ta classe est représentée en interne, tant qu'elle peut être dessiné ca suffit. En interne, c'est la cuisine de chaque classe, ca ne nous préoccupe de l'extérieur.

    Là, tu es en train de ""résoudre"" un GRAVE problème de conception par un hack technique (et oui, je dis bien hack, boost::any est beau mais je n'ai jamais trouvé de cas valable pour l'utiliser, il reste pour moi un hack technique). Bref, je ne pourrais te conseiller que de relire quelques cours sur le polymorphisme et ses bienfaits.

    Sinon, voici un exemple fait en 5 mins.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
     
    class Drawable
    {
    // bla bla, tout ce qui est commun à tes objets graphiques.
    public:
    virtual void dessiner(Surface& s) const =0;
    };
     
    class Carre:
    {
    Point Coin1, Coin2;
    public:
    virtual void dessiner(Surface& s) const 
    {
    //dessiner 4 lignes.
    }
    };
     
    class Polygone:
    {
    std::vector<Point> points;
    public:
    virtual void dessiner(Surface& s) const 
    {
    //relier le point i au point i+1 en bouclant
    }
    };
     
    // d'autres classes (cercle, what ever)
     
     
    // plus loin (dans une autre classe ou une autre fonction)
    Surface s;
    std::vector<Drawable*> v;
    //
    for(int i=0;i<v.size();++i)
    {
    v[i]->dessiner(s);
    }
    "Never use brute force in fighting an exponential." (Andrei Alexandrescu)

    Mes articles dont Conseils divers sur le C++
    Une très bonne doc sur le C++ (en) Why linux is better (fr)

  18. #18
    Membre éclairé Avatar de cs_ntd
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Décembre 2006
    Messages
    598
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2006
    Messages : 598
    Par défaut
    Arf

    Trop précis ou pas assez je fut ^^

    Déjà, comment tu "traduit" en polymorphisme, le fait que :
    Citation Envoyé par cs_ntd
    certaines instances bien précises pourront avoir des paramètres différents
    (mais je ne l'ai pas dit, en nombre différent également)
    Certe, je pourrais dire à l'utilisateur de faire ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class Carre//En reprenant la class Carre de ton exemple
    {
    //Attributs, fonction, ...
    }
    class Carre1:public Carre
    {
        int param;
    };
    class Carre2:public Carre
    {
        std::string param;
    };
    class Carre3:public Carre
    //Pas le même nombre d'attributs que dans les autres, donc une classe template impossible
    {
        A param1; //A étant une classe quelquonque
        B param2; //idem
    };
    /etc...
    Mais je trouve que c'est du gachis de temps et de ligne, et même par héritage, polymorphisme & cie, je n'ai rien vu qui permette de faire ça élegament disons...
    De plus que Carre1/2/3 ne seront certainement appelé qu'une seule fois !

    Ensuite, la méthode "dessiner", ainsi que toute les autres fonctions, seront rigouresement identiques pour chaque "forme", car (et tu a du zapper une de mes phrases), je ne fait que dessiner des ensembles de points.
    4 points suffisent pour un carre
    1000 pour un cercle mettons
    6 pour un hexagone,
    ...
    Au final, n'importe quelle forme que je dessine, je la dessine de la même manière.

    A la place de l'appeler "Object", j'aurais pu faire (et peut-être dû):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    class Ensemble_points
    {
    public:
        std::vector<Point> sesPoints;
        void relier();
    }
    Donc si je ne me trompe pas, avec ta méthode, je devrais redéfinir la même fonction "relier" à chaque fois. Arf


    Dernier point : mon problème n'est pas de contenir les "Object", comme tu semble le dire ici :
    Il ne te faut donc pas un conteneur de any mais un conteneur de Object*
    mais bien de stocker des paramètres d'évolution de l'objet(Object) pour une instance bien précise.
    Je reprend l'exemple de mon précédent post:
    Un "Ensemble_points" ne bouge pas, mais à besoin d'écrire certaines informations qui lui sont propres dans un fichiers => besoin d'un "attribut" string
    Un autre "Ensemble_points" bouge (mais n'écrit rien) => pas besoin d'"attribut" string, mais d'un attribut Point
    Un troisième "Ensemble_points" a besoin de réaliser une tâche TASK, qui necessite des paramètres A de type "Alpha", et B de type "Beta" => Besoin d'un attributs de type "Alpha" et "Beta".



    Donc pour résoudre ces problèmes, j'ai donc penser à:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    class Ensemble_points
    {
        std::vector<any> param; //Permet un nombre et un type différent de paramètres pour chaque instance
    public:
        std::vector<Points> sesPoints;
        void relier(); //La même fonction à chaque fois
    }//Le problème du stockage d'objet ne se pose pas
    A moins que ce moi qui comprene vraiment rien à ce que tu raconte ?(et oué il est tard )

  19. #19
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Salut,
    Il est vrai qu'à la description de ce que tu dis, on sent quand même que l'héritage semble adapté, d'où l'approche plus lisible d'avoir une liste sur la classe de base.
    Ensuite comment varier le comportement d'instance ? Et bien, à la lecture de ton problème, j'ai tendance à penser au DP décorateur ou stratégie (ou faq).

  20. #20
    Membre éclairé Avatar de cs_ntd
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Décembre 2006
    Messages
    598
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2006
    Messages : 598
    Par défaut
    Salut,

    Enfait, je sais pas trop...

    J'ai passé pas mal de temps a regarder ce que tu m'a donné, et je pense en avoir compris ça :

    Ca se rapporte à identifier, pour un type, certains comportements spécifiques, puis permettre à cet objet d'en changer dynamiquement (Stratégie)

    Ou alors de déterminer des "qualités" de l'objet (gout, périssable...), et pour déterminer en fonction de ces qualités un comportement (Décorateur)

    Et je ne peut pas ni identifier, ni déterminer quelque chose :s
    Je ne sais pas (mais vraiment pas) ce que devra faire l'objet (à par être dessiné, mais c'est pareil pour toutes les instances).
    Et je ne connais aucune des qualités qui lui seront "nécessaire".

    Donc oui dans l'absolu, je pourrai peut-être appliquer un de ces modèle de conception, mais je condamne alors l'utilisateur à créer une classe pour chaque "instance" de l'objet !

    Car globalement, toutes les instances de l'objets auront un comportement différent (mais vraiment différent , ce qui signifie que, a par qu'ils seront tous dessiné ces objets, je ne peut rien prévoir d'autre).
    Donc ça obligerais à chaque fois l'utilisateur à créer soit une Strategie, soit un Décorateur, pour pouvoir utiliser une seule et unique fois son objet...

    Après, par contre, si l'utilisateur a envie de définir un comportement spécifique, pas de souci, il en reste libre, et il reste libre d'appliquer un des modèle de conception que tu a cité. Mais j'ai pas envie de le lui imposer, surtout que dans le cas général, je n'ai pas l'impression qu'il sera adapté.

    Après, si tu arrive à me sortir un exemple qui corresponde à mon cas, alors pas de soucis, je l'adopte tout de suite (ou presque ), mais la en l'occurence, je ne vois vraiment pas comment adapter un de ces modèles

    Je sens que vous avez un message à me faire passer, mais j'arrive vraiment pas à l'entendre , et moi de mon coté j'ai l'impression que vous vous méprenez un peu sur ce que je veux faire !
    J'apprécie néanmoins beaucoup votre aide et vos conseils . Ca m'évite aussi de vomir un code horrible et dénué de sens

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Réponses: 0
    Dernier message: 19/03/2013, 20h46
  2. Réponses: 1
    Dernier message: 14/06/2011, 18h02
  3. Réponses: 5
    Dernier message: 05/02/2009, 16h20
  4. Réponses: 14
    Dernier message: 13/07/2007, 12h05
  5. Type structure en param d'une fonction
    Par totoche dans le forum VB 6 et antérieur
    Réponses: 5
    Dernier message: 28/02/2007, 14h26

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo