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

C++ Discussion :

factory & références


Sujet :

C++

  1. #1
    screetch
    Invité(e)
    Par défaut factory & références
    Je bloque un peu sur mon problème la; je souhaite interdire l'opérateur new sur un objet pour forcer a utiliser une fonction "create" :

    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
    #include <cstdlib>
    #include <cstdio>
     
    class refcountable
    {
    public:
        template< typename T >
        static T* create()
        {
            return new T;
        }
        template< typename T, typename P1 >
        static T* create(P1 p1)
        {
            return new T(p1);
        }
    private:
        void* operator new(const size_t size) { return ::operator new(size); }
        void  operator delete(void* memory) { return ::operator delete(memory); }
    };
     
    struct A : public refcountable
    {
        A() { };
        A(int& i) { };
    };
    ainsi ici, on ne peut plus ecrirel'objectif au final c'est que la méthode create renvoie au final un smart pointer au lieu du raw pointeur, du coup on a plus moyen d'avoir un pointeur sur A
    (en ajoutant que l'opérateur & sera interdit... niark niark)
    interdiant donc totalement le raw pointeur. c'est du moins l'objectif.
    Mais ici je suis face a un petit problème :

    avant il etait impossible d'appeler
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    A a(1);
    main.cc:32: error: no matching function for call to 'A::A(int)'
    main.cc:26: note: candidates are: A::A(int&)
    main.cc:25: note:                 A::A()
    main.cc:24: note:                 A::A(const A&)
    mais la, il est parfaitement possible d'appeler
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    A* a = refcountable::create<A>(1);
    PIRE (et dangereux)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    A a;
    A a2(a); //copie de a
    se transforme en :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    A* a = refcountable::create<A>();
    A* a2 = refcountable::create<A>(*a); // la fonction create va recevoir un temporaire qui va etre créé, puis détruit, on a plus la référence directe sur a
    comment puis-je résoudre ces problèmes ? vous voyez ou je veux en venir ?

  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
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    Je pense que tu as compris que l'instanciation de create se fait avec P1 par valeur et non par référence, d'où ces temporaires.
    En lecture rapide de ton code, je vois deux solutions :
    -> Regarder du côté de l'artillerie Boost pour la programmation générique ;
    -> Le faire à la main à coup de enable_if et de SFINAE sur la signature des constructeurs de A pour que P1 corresponde. Quelque chose comme ça (écrit complètement à la volée donc sans aucun sens mais pour donner l'idee) :
    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
     
       template< typename T, typename P1 >
        static T* create(P1 p1)
           enable_if<T::T(P1)>
        {
            return new T(p1);
        }
     
       template< typename T, typename P1 >
        static T* create(P1 &p1)   --> En fait des argument_trait<P1>::ref_type
           enable_if<T::T(P1 &)>  --> idem
        {
            return new T(p1);
        }
     
       template< typename T, typename P1 >
        static T* create(P1 const &p1)   --> En fait des argument_trait<P1>::const_ref_type
           enable_if<T::T(P1 const &)>  --> Idem
        {
            return new T(p1);
        }
    C'est le genre d'idée que je commencerais par creuser.

  3. #3
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    De même, très vite, comme ceci, sans avoir trop réfléchi non plus:
    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
    template< typename T >
    class refcountable
    {
    public:
        static refcountable<T>* create()
        {
            return new T;
        }
        template< typename P1 >
        static refcountable<T>* create(P1 const & p1)
        {
            return new T(p1);
        }
    private:
        void* operator new(const size_t size) { return ::operator new(size); }
        void  operator delete(void* memory) { return ::operator delete(memory); }
    };
     
    struct A : public refcountable<A>
    {
        A() { };
        A(int& i) { };
    };
    Mais bon, comme j'ai déjà écrit mon smart ptr qui fait ce que tu souhaites (sauf que le mien n'est même pas intrusif), je vais pas trop creuser .

  4. #4
    screetch
    Invité(e)
    Par défaut
    oui je voyais bien que ca se faisait par valeur.
    mais si je le passe en référence, alors il y a du code parfaitement valide qui ne voudra pas compiler
    et si je le passe en référence const, on ne pourra plus appeler le constructeur si celui-ci veut des références (c'est malgré tout ce sur quoi je m'oriente, dans la mesure ou du code valide pourrait être rejeté mais aucun code invalide accepté)

    l'inconvénient du enable_if c'est que la j'ai mis un exemple avec des constructeurs a 0 et 1 arguments, mais si il y en a 10 ? on va devoir tester les 10 ? ca va mettre un gros bordel.

  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
    Points : 13 017
    Points
    13 017
    Par défaut
    Avec 10 arguments, tu n'aura plus le problème de la construction par copie. Tu dois pouvoir alors décliner en ref et ref const qui sont 2 types différents. Mais si ta classe n'a qu'une signature en non const, cela peut poser problème. Après, pour gérer la combinatoire, faudrait passer par Boost.Preprocessor (peut être avec les variadic templates?).

  6. #6
    screetch
    Invité(e)
    Par défaut
    rah tu m'as grillé de quelques secondes :p
    oui, effectivement il suffit d'avoir deux methodes, une qui prend en const et une en non const. ca genere 2^10 methodes cependant non ? (chaque parametre peut prendre deux valeurs, const ou non const)

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

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Regarde du côté de boost:pp pour générer autant de code
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  8. #8
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    Regarde #include "boost/call_traits.hpp"

    Sinon aussi #include "boost/make_shared.hpp" où on ne s'embarasse pas des reférences non const.
    Pareil pour les in_place_factory: pas de place pour les non const.

  9. #9
    screetch
    Invité(e)
    Par défaut
    les temps de compil vont exploser; il s'agirait de 1024 méthodes correspondant au nombre d'arguments si je souhaites appeler le constructeur a 10 arguments, templatisées sur la valeur de retour et chacun des arguments. Tout ca pour les 3 cas dans le programme ou on aura effectivement besoin d'une référence en argument (en fait je ne me sers jamais des références non const en plus, ca serait plus proche de 0)

    je vais mettre la perfection de côté et rester sur un truc simple et qui marche
    au pire du pire, le "new" pourrait etre protected et si un cas non géré se produisait l'utilisateur pourrait alors créer sa propre methode "create" qui va bien.

    mais je suis content de savoir que ce petit bout de code serait techniquement possible...

    Merci pour votre aide =)

  10. #10
    screetch
    Invité(e)
    Par défaut
    et comme je suis tetu, j'ai généré les fichiers "create" avec un petit bout de python (je ne suis pas fan des macros pour des gros bouts de code generé une fois pour toute, un coup de script python et ca roule)

    résultat : deux secondes pour compiler au lieu de 300ms
    la ou ca devient interessant c'est que meme en multipliant les appels a create, ca n'a pas l'air de prendre beaucoup plus de temps... hmmmmmm...

  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
    Points : 13 017
    Points
    13 017
    Par défaut
    En même temps, 10 paramètres dans un constructeur, ça commence à faire vraiment beaucoup. Ca traduirait probablement un problème de conception (une classe avec trop de responsabilité ?). Il n'y a pas de chiffres magiques malheureusement, mais c'est un indicateur.

  12. #12
    screetch
    Invité(e)
    Par défaut
    je pensais limiter a 6.
    dans le pire des cas, on packe les parametres dans une structures.
    c'est aussi un de mes conseils de developpement en general; si on au une fonction "update" ou une autre fonction TRES SOUVENT redéfinie (la fonction update d'un jeu vidéo est l'exemple ultime) alors si on change le type d'un argument il faut le modifier dans tous les fichiers ou la fonction pourrait etre redéfinie.
    Pire, si on oublie un endroit, le compilateur peut considérer qu'il s'agit d'une nouvelle fonction (au lieu d'un override) et le comportement devient incompréhensible.
    En regle générale, ma limite est 6 (elle laisse la place, il me semble meme que c'est assez permissif). Au dela, il vaut mieux commencer a grouper quelques parametres dans une structure. Si on ajoute un parametre, on l'ajoute dans la structure, aucune signature ne change.

  13. #13
    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
    Points : 13 017
    Points
    13 017
    Par défaut
    Dans ces cas, j'ai tendance à utiliser un monteur (ami de la classe à construire) qui va alors chercher les paramètres à construire et attaque directement la classe.

  14. #14
    screetch
    Invité(e)
    Par défaut
    Merci bocoup, quoi qu'il en soit, résolu!
    je poste mon petit code python pour les curieux :
    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
    #! /bin/python
    def template(f, n, const, types = '', params = '', args = ''):
    	if n > 0:
    		if const:
    			if types:
    				template(f, n-1, False, ('class A%d, ' % n)+types, ('const A%d& a%d, ' %(n,n)) + params, ('a%d, '%n) + args)
    				template(f, n-1, True, ('class A%d, ' % n)+types, ('const A%d& a%d, ' %(n,n)) + params, ('a%d, '%n) + args)
    			else:
    				template(f, n-1, False, ('class A%d ' % n), ('const A%d& a%d' %(n,n)), ('a%d'%n))
    				template(f, n-1, True, ('class A%d ' % n), ('const A%d& a%d' %(n,n)), ('a%d'%n))
    		else:
    			if types:
    				template(f, n-1, False, ('class A%d, ' % n)+types, ('A%d& a%d, ' %(n,n)) + params, ('a%d, '%n) + args)
    				template(f, n-1, True, ('class A%d, ' % n)+types, ('A%d& a%d, ' %(n,n)) + params, ('a%d, '%n) + args)
    			else:
    				template(f, n-1, False, ('class A%d ' % n), ('A%d& a%d' %(n,n)), ('a%d'%n))
    				template(f, n-1, True, ('class A%d ' % n), ('A%d& a%d' %(n,n)), ('a%d'%n))
    	elif const:
    		if types :
    			f.write('template< class T, %s > static inline T* create(%s) { return new T(%s); }\n' % (types, params, args));
    		else:
    			f.write('template< class T > static inline T* create() { return new T(); }\n');
     
     
    for i in range(0,7):
    	f = open('factory%d.inl' % i, 'w');
    	template(f, i, True);
    	template(f, i, False);
    c'est cradoc mais vous voyez comment ca marche hein

  15. #15
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Citation Envoyé par screetch Voir le message
    rah tu m'as grillé de quelques secondes :p
    oui, effectivement il suffit d'avoir deux methodes, une qui prend en const et une en non const. ca genere 2^10 methodes cependant non ? (chaque parametre peut prendre deux valeurs, const ou non const)
    Et qu'une seule en C++0x. Est-que ton compilo gère le perfect forwading+variadic template ?

  16. #16
    screetch
    Invité(e)
    Par défaut
    pas toute les versions. c'est sensé etre un petit peu portable, donc meme si ca marchait sur windows j'aurais des problemes sur d'autres plate-formes

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

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Dommage parce que ça serait vraiment beaucoup plus simple :').

    D'ailleurs, je me demande si y'a un compilo qui implémente les deux? Je sais que VS2010 a le perfect forwarding mais pas les variadics, gcc supporte les variadiques templates mais quid du perfect forwarding?
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  18. #18
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    GCC propose le perfect forwarding depuis qu'ils ont intégré les rvalue references (GCC 4.3 il me semble)

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

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Oki, j'avais vu passer les rvalue mais pas le perfect forwarding. Manque quand même le nullptr.
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

Discussions similaires

  1. [Livres/Références] Vos avis..
    Par Community Management dans le forum Livres
    Réponses: 6
    Dernier message: 25/07/2005, 19h31
  2. Références / tutoriels MFC COM
    Par DivisionParZéro dans le forum MFC
    Réponses: 3
    Dernier message: 03/02/2004, 17h49
  3. Passage d'un tableau par référence?
    Par sebduth dans le forum C
    Réponses: 9
    Dernier message: 16/07/2003, 18h32
  4. [Concept] Table de référence
    Par matlo dans le forum Décisions SGBD
    Réponses: 3
    Dernier message: 20/01/2003, 15h01

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