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 :

container<T> sans contrainte sur T


Sujet :

C++

  1. #1
    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 container<T> sans contrainte sur T
    J'ai une classe qui gère un conteneur d'objets T. Je souhaite que ma classe n'impose pas de contraintes sur T, ou le moins possible.
    Voici à quoi ressemble la classe:
    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<class T>
    class MyContainer
    {
    	...
    	container<mystery<T>::mystery_type> items;
    public:
    	...
    	ret_val<T> foo(param_type ...)
    	{
    		...
    		if (some_condition)
    		{
    			...
    			void *vptr=acquire_some_memory(...);
    			T *p=new(vptr) T;//<--contrainte ici
    			items.insert(..., *p);
    			...
    		}
    		...
    	}
    };
    En l'état actuel, la seule contrainte est la nécessité pour T d'avoir un constructeur par défaut (pas de copy constructor, pas d'assign operator, ni aucun autre operator).
    J'aimerais aller plus loin en supprimant cette dernière contrainte sans changer le prototype de la méthode foo(). Je pense que c'est possible en faisant un peu comme les factory de boost (dans le code, on suppose que T ne possède qu'un seul constructeur qui exige 3 paramètres):
    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
    template<class T>
    class factory
    {
    	context & c_;
    public:
    	factory(context & c): c_(c) {}
    	T *operator()(void *vptr)
    	{
    		...
    		return new(vptr) T(c_.a1, c_.a2, c_.a3);
    	}
    };
     
     
    template<class T>
    class MyContainer
    {
    	...
    	container<mystery<T>::mystery_type> items;
    	factory<T> f;
    public:
    	MyClass(..., factory<T> & fact, ...): f(fact), ... {...}
    	...
    	ret_val<T> foo(param_type ...)
    	{
    		...
    		if (some_condition)
    		{
    			...
    			void *vptr=acquire_some_memory(...);
    			T *p=f(vptr);//<--contrainte déplacée dans factory<T>
    			items.insert(..., *p);
    			...
    		}
    		...
    	}
    };
    factory<T> serait donc un foncteur qui gèrerait la construction de T en fonction des besoins de ce dernier.
    Y a-t-il mieux ?
    Merci.

  2. #2
    Membre régulier
    Profil pro
    Inscrit en
    Avril 2008
    Messages
    87
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Avril 2008
    Messages : 87
    Points : 111
    Points
    111
    Par défaut
    C'est précisément ce que j'ai réglé dans OnStackInitializableArray:

    http://www.developpez.net/forums/d86...lizable-array/

    cependant personne ne l'a compris sur le thread, donc je laisse choire.

    Ici ma solution a été d'implanter trois versions, 0 pour 0 paramètres, 1 pour 1, et à travers in_place factory pour le reste. (0 et 1 servant a éviter d'embêter le client en le faisant écrire des in_place en plus)

    l'avantage de in_place c'est qu'ils ont généré 10 arités dans la bibliothèque de base, et on peut en générer plus en changeant une define.

    ce problème sera réglé dans C++0x avec les template variadics. C'est super chiant à manier car il faut utiliser des récursions template pour accéder aux paramètres dans les ... mais au moins ca évite d'utiliser boost.preprocessor pour générer des fonctions a argument variable a typage fort.

  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
    J'ai lu cette discussion hier et je trouvais les concepts traités intéressants. Mais je n'ai pas prété plus attention que ça à ta classe car l'allocation de tableaux (figés) sur le stack ont un intérêt limité à mes yeux (et puis le code avec plein de boost partout m'a un peu fait peur ).

    Par contre, si tu avais proposé un std::vector en le complétant par ce qu'il lui manque -le fameux "in place factory"- tu aurais eu plus de succès je pense
    Je trouve de fait dommage que l'insertion d'un élément dans std::vector ne puisse se faire que par copie.

    J'ai repris mon courage à deux mains et je viens ainsi de regarder ton code d'un peu plus près (après avoir commenté BOOST_STATIC_ASSERT que le compilo de VS2005 n'a pas apprécié).
    Il semble que ta méthode AssignN() fait un peu ce que je cherche. Il y a une différence cependant, ta méthode doit être appelée explicitement alors que dans mon cas elle serait appelée, peut-être, implicitement par foo(). Mon soucis vient du fait que la "factory" ne peut être passée en paramètre à foo(), raison pour laquelle j'ai ouvert cette discussion pour avoir l'avis du forum.

    Pourrais-tu compléter ton petit exemple d'utilisation en faisant usage de AssignN() afin de voir comment créer une factory dans ce cas ?
    Merci.

    (En gros, MyContainer::foo() renvoit un élément d'un conteneur suivant un critère, en le créant et ajoutant dans le conteneur le cas échéant)

  4. #4
    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,
    En l'absence de variatic templates, il faut jouer avec Boost.Preprocessor pour générer des versions avec 1, 2, ... N éléments.

  5. #5
    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
    Je ne comprend pas cette réponse.
    J'imagine bien que les variadics template permettront de faire une factory générique, mais où cela intervient-il dans mon code ?
    Mon soucis n'est pas de faire une factory, mais de savoir quand l'instancier sachant que cela ne peut se faire au moment de l'appel à foo().

  6. #6
    Membre régulier
    Profil pro
    Inscrit en
    Avril 2008
    Messages
    87
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Avril 2008
    Messages : 87
    Points : 111
    Points
    111
    Par défaut
    Je suis sur que ton truc est faisable.
    dans tous les cas tu peux toujours encapsuler une zone de mémoire brute malloc-ée à la old-style et gérer construction/destruction toi même.

    Je suis d'accord avec toi quand au système "sur la pile" de mon conteneur.
    je n'ai pas dit qu'il te conviendrait, juste la partie AssignN.

    Tu peux reprendre la même idée, et utiliser malloc, ou bien vector<char>.
    remplace mpl::for_each par un std::for_each normal...
    Pourrais-tu compléter ton petit exemple d'utilisation en faisant usage de AssignN() afin de voir comment créer une factory dans ce cas ?
    Merci.
    mais absolument, c'est assez simple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    struct S
    {
      S(int, char, std::string) {}
    };
    int main(int,char**)
    {
      tools::OnStack/*blabla*/... array;
      array.AssignN(boost::in_place(42, 'a', "youpi"));
    }

  7. #7
    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
    Ok merci ça marche

  8. #8
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Je ne vois pas comment tu peux te passer du constructeur de copie (ou d'une sémantique de mouvement).

    Pour le constructeur par défaut, tu peux le remplacer par un paramètre template fournissant une fonction Create renvoyant un T (c'est bien le but de in_place, non ?). Par contre, ils seront tous construits pareil (il faut bien un constructeur par défaut, c'est juste pas obligé d'être celui de la classe).

  9. #9
    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
    Citation Envoyé par camboui Voir le message
    Je ne comprend pas cette réponse.
    J'imagine bien que les variadics template permettront de faire une factory générique, mais où cela intervient-il dans mon code ?
    Mon soucis n'est pas de faire une factory, mais de savoir quand l'instancier sachant que cela ne peut se faire au moment de l'appel à foo().
    Ah, ok. Pour la factory, ça se base sur Boost.Preprocessor pour générer autant de classes que de paramètres à liés.
    Pour Boost.Factory, tu peux voir Tutoriel Boost In Place Factory.

    Citation Envoyé par white_tentacle Voir le message
    Je ne vois pas comment tu peux te passer du constructeur de copie (ou d'une sémantique de mouvement).
    BoosT.In Place Factory utilise un placement new pour ne pas avoir à faire de copie/déplacement.

  10. #10
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    BoosT.In Place Factory utilise un placement new pour ne pas avoir à faire de copie/déplacement.
    Pour la factory, oui, mais pour un conteneur... Tu fais comment pour redimensionner ?

  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
    Ah, ok. Tu veux dire que container<T> apporte des contraintes 'cachées' sur T, c'est ça ?

  12. #12
    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
    Pour être plus complet, le container n'est pas explicitement du genremais plutôt
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    container<mystery<T>::mystery_type>
    Quelque part, comme le suggère 3Darchi, avec des placements new et des containers de pointeurs par exemple (avec des intrusives aussi) on peut éviter toute copie. Il me restait à éviter d'imposer aussi le constructeur par défaut, et c'est faisable. Reste que j'essaie de voir comment implémenter cela de manière élégante sachant que je ne veux pas toucher aux paramètres de foo(params). Quoique finalement je pourrais passer un paramètre supplémentaire à foo() de type template, se serait par défaut une factory qui construit le type T via son constructeur par défaut.

    A propos de redimensionnement, je ne fais presque jamais de resize() avec std::vector pour prendre cet exemple. Par contre j'utilise très souvent la méthode reserve(), puis des push_back().

Discussions similaires

  1. Réponses: 7
    Dernier message: 23/07/2005, 12h50
  2. Réponses: 3
    Dernier message: 28/04/2005, 16h56
  3. [Interbase] Mettre une contrainte sur un champ
    Par mika dans le forum InterBase
    Réponses: 2
    Dernier message: 26/01/2005, 14h04
  4. contrainte sur deux champs d'une table
    Par bdkiller dans le forum PostgreSQL
    Réponses: 3
    Dernier message: 17/09/2004, 18h26
  5. [VB6] Déplacer la form sans cliquer sur la barre de titre
    Par Ingham dans le forum VB 6 et antérieur
    Réponses: 4
    Dernier message: 14/11/2002, 02h09

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