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 :

expression template et type erasure


Sujet :

C++

  1. #1
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut expression template et type erasure
    Bonjour à tous.
    J'adore le C++, ses subtilités et ses problèmes tordus.

    En l'occurence, j'ai aujourd'hui une difficulté à base de templates.

    Pour rappel, le CRTP est un mode d'héritage template assez courant, qui permet, entre autre, un polymorphisme à la compilation.
    J'ai pris l'habitude d'utiliser CRTP comme nom de ce parametre template. Ca aide à repérer le pattern.

    Voici donc la classe de base réelle de mon problème (à ceci près que j'ai en plus un wrapper autour du stream).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    template <typename Object, typename CRTP>
    struct streamable {
      void operator() (std::ostream& stream, object_type const& o) const {
        static_cast<CRTP&>(*this).write_into(stream, o);
      }
     
      void operator() (std::istream& stream, object_type const& o) const {
        static_cast<CRTP&>(*this).read_from(istream, o);
      }
    };
    Comme vous pouvez le noter, il y a deux operator(), correspondant à deux std::function;

    Cette classe sert de base à toute une hiérarchie de templates, pour créer un système "expression template".
    Cela signifie qu'au final, je peux écrire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    struct S { int a, b; };
     
    using namespace std;
    int main() {
      auto streamer = streamer<S>("a=") + &S::a + ";b=" + &S::b+';';
      S s;
      cin >> streamer(s);
      swap(s.a, s.b);
      cout << streamer(s);
      return 0;
    }
    En gros, il s'agit d'unifier une lambda pour ostream et une pour istream.

    Mon soucis, c'est de rendre ce code compatible avec C++03. (snif)
    Ca fonctionne... mais auto devient :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    streamer_join<
      streamer_join<
        streamer_join<
          streamer_join< streamer_const<S, string>, streamer_member<S, int> >,
          streamer_const<S, string>
        >,
        streamer_member<S, int>
      >,
      streamer_const<S, char>
    >
    Du coup, ce n'est pas très pratique.
    Il me faudrait un type qui soit template uniquement sur le type d'objet à manipuler, qui puisse recevoir cette classe.
    J'ai bien la piste de boost::function, mais comment unir les deux interfaces?

    J'ai envisagé la classe suivante
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    template <typename Object>
    class Streamer {
    private:
        boost::function< void(std::ostream& stream, Object const& o) > output_interface;
        boost::function< void(std::istream& stream, Object const& o) > input_interface;
    public:
        template <typename CRTP>
        Streamer< streamable<Object, CRTP> const& interface): output_interface(interface), input_interface(interface) {} 
     
        //définition des deux operator() pour appeler les interface.
    };
    Ca fonctionnerait plutot bien, mais il y a un petit défaut: le streamable est stocké deux fois. Si vous avez une idée sur la question, je suis preneur.

    Par contre, il y a un autre défaut, plus gros.
    Dans ma classe streamable réelle, les opérator() sont template sur le type de stream:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    template <typename Object, typename CRTP>
    struct streamable {
      template <typename OStream>
      void operator() (stream_wrapper<OStream>& stream, object_type const& o) const {
        static_cast<CRTP&>(*this).write_into(stream, o);
      }
    };
    Et là, subitement, je ne peux plus stocker d'objet function, puisqu'il n'y en a pas qu'une.
    J'en arriverai à vouloir stocker un unique_ptr d'une copie du streamable, mais n'ayant pas de type CRTP, définir le pointeur.

    Je ne vois pas d'échappatoire, et j'ai vraiment besoin de définir une variable (voire un membre d'une classe).
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  2. #2
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,
    Pourquoi n'envisagerais tu pas d'utiliser les variadic templates en faisant en sorte que ta classe de jointure renvoie... une jointure dont le type est défini par le parameter pack
    Je sais pas, moi, quelque chose comme
    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
    40
    41
     
    template <typename T>
    struct stream{
    /* tout ce qu'il faut ici */
    };
     
    /* déclaration anticiptée permettant à la structure de fonctionner
     * avec littéralement n'importe quel nombre de paramètres,
     * de n'importe quel type
     */
    template <typename ... CRTP>
    struct streamer_join;
    /* on extrait un paramètres du parameter pack, et on l'utilise pour l'opérateur <<
     */
    template <typename T, typename ... CRTP>
    struct streamer_join<T, CRTP ...> {
        /* on va renvoyer un streamer_join avec un paramètre en moins dans le
         * pack
         */
        using next_t = streamer_join<CRTP ...>;
        next_t & operator<<(T const & t) const{
            /* il faut le flux pour envoyer la donnée
            stream<T> str;
           str<<t;
           // c'est bien mieux de renvoyer une référence sur un objet existant
           // (mais comme il n'y a aucune donnée membre, ca ne coute que le prix
           // d'une adresse mémoire)
           static next_t next;
            return next;
        }
    };
    /* et, pour mettre fin à la récursion (tu auras une belle erreur "opérateur << n'existe pas"
     * si tu essaye de fournir trop de données ;)
     */
    template <>
    struct streamer_join<T>{
    /* en ne définissant pas l'opérateur <<,
     *(tu auras une belle erreur "opérateur << n'existe pas"
     * si tu essaye de fournir trop de données ;)
     */
    };
    Si je ne me suis pas trompé, tu devrais donc pouvoir t'en sortir avec un code qui serait proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    int main(){
        /*tout ce qu'il faut, c'est l'ordre dans lequel seront fournis les paramètres
         */
        streamer_join<int, std::string, double, char, int> join; 
        join<<12<<"salut"<<3.1415926,'a',128;
    }
    PS : contrairement à mon habitude, le code n'a pas du tout été testé... il se peut qu'il faille l'adapter quelque peu
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  3. #3
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    @koala01: Les templates variadiques n'existe pas en C++03. Par contre, c'est possible de les "simuler" avec une template possédant X types avec valeur par défaut.


    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
    template<class Object, class T0, class T1 = void, class T2 = void/*..., T20 = void*/>
    struct build_streamer_join
    {
      typedef build_streamer_join<
        Object,
        streamer_join<
          typename build_streamer_type<Object, T0>::type,
          typename build_streamer_type<Object, T1>::type
        >,
        T2//, ... T20
      > type;
    };
    template<class Object, class T0>
    struct build_streamer_join<Object, T0, void, void>
    {
      typedef typename build_streamer_type<Object, T0>::type type;
    };
     
     
    build_streamer_join<S, std::string, member<int>, std::string, member<int>, char>::type streamer;
    Ce qui n'est pas terrible non plus.

    Du coup, je propose d'utiliser typeof/__typeof qui sont des extensions gcc/clang et msvc et qui fonctionne grossièrement comme decltype.

    L'autre solution est d'appeler une fonction template plutôt que mettre dans une variable. Par contre, c'est tout pourri niveau localité du code et le contexte de variable devient difficile à propager.

  4. #4
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Merci, je vais étudier vos propositions.

    Notez qu'en fait, après analyse plus détaillée, je n'ai pas besoin de rendre streamable template, puisque mon wrapper de ostream n'en a pas vraiment besoin.
    Le type de charactères sera éventuellement variable, mais alors il sera inclus dans le type de ma hiérarchie.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  5. #5
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par jo_link_noir Voir le message
    @koala01: Les templates variadiques n'existe pas en C++03. Par contre, c'est possible de les "simuler" avec une template possédant X types avec valeur par défaut.
    Ben, il n'était spécifié nulle part que ternel veut utiliser C++03

    Sinon, il y a toujours possibilité d'utiliser la technique des "typelist"... Renseigne toi peut être sur le travail de Alexandrescu et sa bibliothèque loki ... Il y présente quelques techniques d'utilisation template plutôt sympa
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  6. #6
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Même le koala a la vue qui baisse avec les années
    Citation Envoyé par ternel Voir le message
    Mon soucis, c'est de rendre ce code compatible avec C++03. (snif)
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  7. #7
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Bousk Voir le message
    Même le koala a la vue qui baisse avec les années
    Ah, oui!!! Ben, alors, les typelists proposées par alexandrescu me semblent sympa (avec quelques macro pour les utiliser plus facilement, mais tout est bien expliqué sur le site de loki )
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  8. #8
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Il y en a aussi dans la MPL, je crois, et peut-être dans phoenix
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

Discussions similaires

  1. Template et type
    Par olivier1978 dans le forum Langage
    Réponses: 7
    Dernier message: 12/02/2007, 00h55
  2. Réponses: 12
    Dernier message: 27/01/2007, 12h32
  3. expression du mauvais type DomNode
    Par robris dans le forum Oracle
    Réponses: 6
    Dernier message: 16/08/2006, 16h13
  4. [VB.NET]expression d'un type d'expression
    Par new_wave dans le forum Windows Forms
    Réponses: 3
    Dernier message: 05/06/2006, 21h40
  5. Templates et Types.
    Par ousta dans le forum C++
    Réponses: 4
    Dernier message: 30/11/2005, 20h14

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