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

Langage C++ Discussion :

[Conception] Refonte et design pour maintenance et extensibilité aisée


Sujet :

Langage C++

  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 199
    Par défaut [Conception] Refonte et design pour maintenance et extensibilité aisée
    Bonjour!

    Pour expliquer mon problème je trouve qu'un code sera très parlant :
    Je met à disposition des utilisateurs l'enum suivante :
    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
     enum class PixelFormat
    {
        R8U, ///< R 8 bits 8 unsigned
        RG8U, ///< RG 16 bits 88 unsigned
        RGB8U, ///< RGB 24 bits 888 unsigned
        RGBA8U, ///< RGBA 32 bits 8888 unsigned
    };
     
     
    unsigned int getBytesPerPixel(PixelFormat Format)
    {
        switch(Format)
        {
            case PixelFormat::R8U: return 1;
            case PixelFormat::RG8U: return 2;
            case PixelFormat::RGB8U: return 3;
            case PixelFormat::RGBA8U: return 4;
        }
    }
    Seulement là j'ai besoin de rajouter soit de nouveaux format, soit de nouvelles propriétés (connaître le type du PixelFormat, savoir si on à affaire à un format compressé ou non, etc).
    Et là je me rend compte que je vais vite avoir un code in-maintenable

    Seulement je ne sais pas du tout par quoi remplacer. J'ai pensé à une classe ou l'utilisation de template mais rien ne me plaît vraiment..

    Si vous avez une solution à me donner, une piste à suivre ou un design pattern je serais bien content!

    Merci beaucoup à tous!

  2. #2
    Membre éprouvé
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 766
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 766
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    enum class PixelFormat
    {
        R8U = 1, ///< R 8 bits 8 unsigned
        RG8U = 2, ///< RG 16 bits 88 unsigned
        RGB8U = 3, ///< RGB 24 bits 888 unsigned
        RGBA8U = 4, ///< RGBA 32 bits 8888 unsigned
    };

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 199
    Par défaut
    Merci de ta réponse mais cela n'est valable que si la seule propriété que je veux connaître est le nombre de bits par format de pixel.

    Seulement maintenant je cherche à étendre mes formats et je souhaiterais éviter les switch à répétitions pour avoir un code propre et surtout porté sur "l'avenir", comprendre par là qu'il soit facilement maintenable et extensible.

    Merci beaucoup pour ta réponse néanmoins!

  4. #4
    Membre éprouvé
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 766
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 766
    Par défaut
    Ben tant qu'on ne sait pas comment tu veux les utiliser...

    Le seul cas d'utilisation que tu as fourni était un switch.

  5. #5
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 199
    Par défaut
    J'ai une classe qui représente une Image et j'ai le code suivant (en raccourcis) :
    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
    class Image
    {
        public :
            Image::Image(const uvec2& Size, PixelFormat Format, const unsigned char* Pixels);
     
        private :
            uvec2  m_Size;   ///< Dimensions de l'image
            PixelFormat m_Format; ///< Format de pixels
            std::vector<unsigned char> m_Pixels; ///< Tableau des pixels
    };
     
     
    // Dans le Image.cpp
    // Constructeur
    Image::Image(const uvec2& Size, PixelFormat Format, const unsigned char* Pixels) :
    m_Size  (Size),
    m_Format(Format),
    m_Pixels(Pixels, Pixels + Size.x * Size.y * getBytesPerPixel(Format))
    {
    }
    Imaginons maintenant que je crée une fonction qui à partir d'une image renvoi l'image ayant subi une symétrie verticale (ou tout autre transfo peu importe), j'aimerais ne l'appliquer que si le PixelFormat ne représente pas des donnés compressées :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    void Image::flip()
    {
        if( isCompressedFormat(m_Format) )
            throw std::runtime_error("Le PixelFormat ne doit pas être compréssé")
     
        // Le traitement
    }
    Et bien là déjà je me retrouve avec la fonction bool isCompressedFormat(PixelFormat), et donc un switch en plus à maintenir. Et si j'ajoute d'autres fonctions du genre ou d'autres PixelFormat je me retrouve avec une bouillie in-maintenable et difficilement extensible...

  6. #6
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut
    Bonjour

    Classes de traits pour le format et politiques pour les fonctions + spécialisation template pour l'évolutivité. Lire Présentation des classes de Traits et de Politiques en C++

  7. #7
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 199
    Par défaut
    D'accord, mais en fait pour une classe de trait il me fallait un paramètre template et c'est la que je sèche complètement.

    Je ne vois absolument pas quoi mettre de pertinent. En tout cas merci pour l'article fort intéressant!

  8. #8
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,
    Citation Envoyé par victor_gasgas Voir le message
    D'accord, mais en fait pour une classe de trait il me fallait un paramètre template et c'est la que je sèche complètement.

    Je ne vois absolument pas quoi mettre de pertinent. En tout cas merci pour l'article fort intéressant!
    Ce qui est bien avec les template, c'est que ca peut se cacher partout

    Un simple exemple, tu peux créer une classe template qui transforme une valeur quelconque en un type particulier qui est totalement différent de toute autre valeur, et cela très simplement :
    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
    template <int V>
    struct intToType
    {
        enum 
        {
            value = v // permet de récupérer la valeur en cas de besoin :D
        };
    };
    /* avec cette simple structure,
       intToType<2> t et
       intToType <3> sont deux type totalement différents :D
       Nous pourrions donc envisager de créer un calcul de la factorielle,
       par exemple, qui serait automatiquement généré à la compilation,
       la valeur résultante étant seule codée en dur dans le code ;)
     */
    template < int i>
    struct factorielle;
    /* pour mettre fin à la récursion (cas de base : i == 1 :D */
    template <int >
    struct factorielle<1>
    {
        enum {value = 1};
    };
    /* pour toute autre valeur (devra cependant etre positive :D ) */
    template <int i>
    struct factorielle
    {
        enum { value = i * factorielle<i-1>::value};
    };
    /* les valeurs seront codée en dur dans le code */
    int main()
    {
        int fact_10 = factorielle<10>::value; // 3628800
        int fact_6 = factorielle<6>::value; // 720
    }
    Dans le cas qui te concerne, tu pourrais donc envisager des spécialisation partielle sous la forme de
    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
    template < /* typename T, typename U */, int FormatValue>
    struct Policy
    {
        /* la politique " générale" */
    } ;
    template <typename T, typename U */
    struct Policy<T, U, R8U>
    {
     
    // politique adaptée à  R 8 bits 8 unsigned
    /* si tu veux récupérer la valeur en elle meme, tu peux utiliser
    }
    /* voir, si tu veux pouvoir récupérer la valeur en restant général
    template < /* typename T, typename U */, int FormatValue>
    struct Policy
    {
        typedef intToType<Formatvalu> value_to_type;
        /* la politique " générale" */
        void foo()
        {
            /* récupération de la valeur */
            int v = value_to_type::value;
           /* utilisation de v */
        }
    } ;
    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

  9. #9
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 199
    Par défaut
    Merci beaucoup pour tes explications!

    J'en suis arrive alors au code ci-dessous:
    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
    #include <iostream>
     
     
    enum class PixelFormat
    {
        R8U,    ///< R 8 bits 8 unsigned
        RG8U,   ///< RG 16 bits 88 unsigned
        RGB8U,  ///< RGB 24 bits 888 unsigned
        RGBA8U  ///< RGBA 32 bits 8888 unsigned
    };
     
     
    template<PixelFormat Format>
    struct PixelFormatInfo
    {
     
    };
     
     
    template<>
    struct PixelFormatInfo<PixelFormat::R8U>
    {
        static const int value = 1;
    };
     
     
    int main()
    {
        PixelFormat format;
     
        std::cout << "Value : " << PixelFormatInfo<format>::value << std::endl;
     
        return 0;
    }
    Seulement il ne fonctionne pas car les templates sont résolus à la compilation. Donc je me demande comment faire parce que la solution à base de spécialisation semblait très prometteuse

    Merci encore!

  10. #10
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    En fait, tu peux te baser sur les memes idées que celles exposées dans ce post, en gardant simplement la fameuse phrase
    Citation Envoyé par David Wheeler
    "all problems in computer science can be solved by another level of indirection" .
    Je m'explique : tu as des formats compressibles et des formats non compressibles.

    Tu peux donc partir de deux classes qui réagissent sur base de ce critère :
    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
    struct UnCompressibleFormat
    {
        /* si on tente de compresser un format incompressible, 
         * on lance une exception ;)
         */
        template <typename Compressed, typename ToCompress>
        static Compressed compress(ToCompress const & )
        {
            throw UnCompressibleFormatException();
        }
        /* juste parce que nous pourrions en avoir besoin :D */
        enum{ isConvertible = false};
    };
    struct CompressibleFormat
    {
     
        template <typename Compressed, typename ToCompress>
        static Compressed compress(ToCompress const & to )
        {
            /* faudra voir comment on s'y prend, mais, ici, nous pourrions
             * créer un visiteur prenant to en paramètre et renvoyant
             * le format ad hoc :D
             */
        }
        /* juste parce que nous pourrions en avoir besoin :D */
        enum{ isConvertible = true};
     
    };
    A partir de là, tu peux envisager la spécialisation de ta classe PixelFormatInfo
    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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    template <int Format>
    class PixelFormatInfo; // pour sortir sur une erreur de compilation si nous 
                           // n'utilisons pas une des spécialisation prévues ;)
    template <>
    class PixelFormatInfo<R8U>
    {
        public :
            enum{ formatType = R8U};
            typedef UnCompressibleFormat compressFormat_type;
            bool isConvertible() const{return compressFormat_type::isConvertible;}
            /* y a peut etre d'autres données sympas :D */
    };
    template <>
    class PixelFormatInfo<RG8U>
    {
        public :
            enum{ formatType = RG8U};
            typedef CompressibleFormat compressFormat_type;
            bool isConvertible() const{return compressFormat_type::isConvertible;}
           <typename Compressed, typename ToCompress>
           Compressed compress(ToCompress const & to )
           {
              compressFormat_type::compress<Compressed, ToCompress>(to);
           }
            /* y a peut etre d'autres données sympas :D */
    };
    template <>
    class PixelFormatInfo<RGB8U>
    {
        public :
            enum{ formatType = RGB8U};
            typedef CompressibleFormat compressFormat_type;
            bool isConvertible() const{return compressFormat_type::isConvertible;}
           <typename Compressed, typename ToCompress>
           Compressed compress(ToCompress const & to )
           {
              compressFormat_type::compress<Compressed, ToCompress>(to);
           }
            /* y a peut etre d'autres données sympas :D */
    };
    /* on peut enfin faire dériver une classe de Image dont le comportement
     * s'adapte au format utilisé :D
     */
    template <int Format>
    class FormatedImage : public Image
    {
        PixelFormatInfo<Format> infos_;
        public:
            void flip()
            {
                 typeCompression compressed  = 
                 infos_.compress<typeCompression,
                                 FormatedImage<infos_::value_type> > (*this);
            }
    };
    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

  11. #11
    Expert confirmé

    Avatar de dragonjoker59
    Homme Profil pro
    Software Developer
    Inscrit en
    Juin 2005
    Messages
    2 036
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Software Developer
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 2 036
    Billets dans le blog
    12
    Par défaut
    Tu peux aussi utiliser la méthode décrite par Laurent Gomila pour son moteur 3D : http://loulou.developpez.com/tutorie...3d/partie5/#L2

    La partie compression n'est pas prise en compte, mais la base est là pour pouvoir la mettre en place.
    Si vous ne trouvez plus rien, cherchez autre chose...

    Vous trouverez ici des tutoriels OpenGL moderne.
    Mon moteur 3D: Castor 3D, presque utilisable (venez participer, il y a de la place)!
    Un projet qui ne sert à rien, mais qu'il est joli (des fois) : ProceduralGenerator (Génération procédurale d'images, et post-processing).

  12. #12
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 199
    Par défaut
    Pour tout dire j'étais partis du tuto de Laurent Gomilla. Et dans les sources voila ce que l'on trouve :

    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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    enum TPixelFormat
    {
        PXF_L8,       ///< Luminosité 8 bits
        PXF_A8L8,     ///< Alpha et luminosité 16 bits
        PXF_A1R5G5B5, ///< RGB 16 bits 1555
        PXF_A4R4G4B4, ///< RGB 16 bits 4444
        PXF_R8G8B8,   ///< RGB 24 bits 888
        PXF_A8R8G8B8, ///< ARGB 32 bits 8888
        PXF_DXTC1,    ///< Format compressé DXT1 8 bits
        PXF_DXTC3,    ///< Format compressé DXT3 16 bits
        PXF_DXTC5     ///< Format compressé DXT5 16 bits
    };
     
     
    inline unsigned int GetBytesPerPixel(TPixelFormat Format)
    {
        switch (Format)
        {
            case PXF_L8 :       return 1;
            case PXF_A8L8 :     return 2;
            case PXF_A1R5G5B5 : return 2;
            case PXF_A4R4G4B4 : return 2;
            case PXF_R8G8B8 :   return 3;
            case PXF_A8R8G8B8 : return 4;
            case PXF_DXTC1 :    return 1;
            case PXF_DXTC3 :    return 2;
            case PXF_DXTC5 :    return 2;
            default :           return 0;
        }
    }
     
     
     
     
     
    inline std::string FormatToString(TPixelFormat Format)
    {
        switch (Format)
        {
            case PXF_L8 :       return "PXF_L8";
            case PXF_A8L8 :     return "PXF_A8L8";
            case PXF_A1R5G5B5 : return "PXF_A1R5G5B5";
            case PXF_A4R4G4B4 : return "PXF_A4R4G4B4";
            case PXF_R8G8B8 :   return "PXF_R8G8B8";
            case PXF_A8R8G8B8 : return "PXF_A8R8G8B8";
            case PXF_DXTC1 :    return "PXF_DXTC1";
            case PXF_DXTC3 :    return "PXF_DXTC3";
            case PXF_DXTC5 :    return "PXF_DXTC5";
            default :           return "Format inconnu";
        }
    }
    }
    Et à moins de dire une bêtise, cette solution me semble peu extensible. En effet si je rajoute le format PXF_B8G8R8A8, je vais devoir modifie les deux fonctions ci-dessus. Mais si j'oublie d'en compléter une, j'aurais juste une erreur au runtime. On pourrait dans le cas default du switch envoyé une exception plutôt.
    Et de même le problème de l’extensibilité se retrouve si j'ai une nouvelle fonction bool isCompressed(TPixelFormat Format) car je vais devoir bien faire attention à mettre tout.

    La solution de koala01 à base de template semble tout indiquée, puisque là c'est une erreur à la compilation qui serait lancée. Je me suis trituré le cerveau une bonne partis de la matinée et j'ai pas encore trouvé comment l'utilisée (c'est pour cela que je n'avait pas répondu plus tôt ). En effet sa solution est très élégante mais elle m'oblige à revoir l'architecture de l'application. Ce qui en somme veut tout simplement dire qu'elle n'étais pas robuste pour deux sous.

    En effet mes images sont différentes de l'ordinaire car chacune est constitué de plusieurs niveaux de mipmap si on le souhaite, pour être ensuite utilisé par l'API graphique OpenGL. Les mipmaps consistes à avoir plusieurs niveaux de détails. Ainsi une image de taille 128*32, peut, si l'utilisateur le souhaite, aussi contenir les niveaux :
    - 64*16,
    - 32*8,
    - 16*4,
    - etc jusqu'à 1x1.

    J'en arrive donc à l'interface suivant :
    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
    class Image
    {
     
    public:
        class ImageLevel
        {
        public:
            ImageLevel(const uvec2 &dimensions = uvec2(1,1), PixelFormat format = PixelFormat::RGB8U);
     
            ImageLevel(const uvec2 &dimensions, PixelFormat format, const ubyte* pixels);
     
            uvec2 getDimensions() const;
     
            PixelFormat getFormat() const;
     
            ubyte * getPixels();
     
    	const ubyte* getPixels() const;
     
        private:
            uvec2 m_dimensions;
            PixelFormat m_format; 
            std::vector< ubyte > m_pixels; 
        };
     
     
    public:
        Image(std::size_t nbLevels); 
     
        std::size_t levels() const;
     
        bool empty() const;
     
        ImageLevel& operator[](std::size_t i);
     
        const ImageLevel& operator[](std::size_t i) const;
     
    private:
        std::vector< ImageLevel > m_levels;
    };
    Or la avec les template je ne sais plus du tout comment faire.

    La vérification des images (que tous les niveaux ont bien le même format, les bonnes dimensions, etc) est géré avant l'envoi vers OpenGL, car après tout l'utilisateur peut faire se que bons lui semble s'il ne les utilise pas dans ce cadre là
    Or dans la classe Texture liant avec l'API, je peux aussi avoir besoin des fonctions getBytesPerPixel, etc.

  13. #13
    Expert confirmé

    Avatar de dragonjoker59
    Homme Profil pro
    Software Developer
    Inscrit en
    Juin 2005
    Messages
    2 036
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Software Developer
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 2 036
    Billets dans le blog
    12
    Par défaut
    Tu ne veux absolument pas utiliser la génération de mipmaps fournie par OpenGL ?

    J'ai fait différemment de Laurent pour ma prise en charge des formats de pixel pour mon moteur.
    J'ai fait ainsi :
    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
     
    // Les formats supportés
    typedef enum
    {	ePIXEL_FORMAT_L4
    ,	ePIXEL_FORMAT_A4L4
    ,	ePIXEL_FORMAT_L8
    ,	ePIXEL_FORMAT_A8L8
    ,	ePIXEL_FORMAT_A1R5G5B5
    ,	ePIXEL_FORMAT_A4R4G4B4
    ,	ePIXEL_FORMAT_R8G8B8	
    ,	ePIXEL_FORMAT_A8R8G8B8
    ,	ePIXEL_FORMAT_DXTC1
    ,	ePIXEL_FORMAT_DXTC3
    ,	ePIXEL_FORMAT_DXTC5
    ,	ePIXEL_FORMAT_YUY2
    ,	ePIXEL_FORMAT_COUNT
    }	ePIXEL_FORMAT;
     
    namespace details
    {
    	// La fonction de conversion résolue à la compilation
    	template <ePIXEL_FORMAT PfSrc, ePIXEL_FORMAT PfDst>
    	inline void _convert( unsigned char const * p_pSrc, unsigned char * p_pDest );
    }
     
    template <ePIXEL_FORMAT format> struct pixel_definitions;
    On a donc une fonction de conversion templatisée qui permettra la vérification à la compilation de l'existence de la conversion.
    On implémente donc ça pour chaque conversion disponible (je ne mets le code que d'un seul format : de L8 vers le monde)

    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
    42
    43
    44
    namespace details
    {
    	// On implémente la fonction template pour chaque conversion supportée
    	template <> inline void _convert<ePIXEL_FORMAT_L8, ePIXEL_FORMAT_L8>( unsigned char const * p_pSrc, unsigned char * p_pDest )
    	{
    		*p_pDest = *p_pSrc;
    	}
    	template <> inline void _convert<ePIXEL_FORMAT_L8, ePIXEL_FORMAT_A8L8>( unsigned char const * p_pSrc, unsigned char * p_pDest )
    	{
    		p_pDest[0] = *p_pSrc;
    		p_pDest[1] = 0xFF;
    	}
    /* ... */
    }
     
    // On implémente la structure magique qui contient les informations sur le format
    template <> struct pixel_definitions<ePIXEL_FORMAT_L8>
    {
    	enum { size = 1 };
    	static inline std::string to_string()
    	{
    		return "8 bits luminosity";
    	}
    	template <ePIXEL_FORMAT PF>
    	static inline void convert( unsigned char const * p_pSrc, unsigned char * p_pDest )
    	{
    		details::_convert<ePIXEL_FORMAT_L8, PF>( p_pSrc, p_pDest );
    	}
    	// Une fonction de conversion résolue à l'exécution, si besoin est
    	static inline void convert_dyn( unsigned char const * p_pSrc, unsigned char * p_pDest, ePIXEL_FORMAT p_ePixelFmtDst )
    	{
    		switch( p_ePixelFmtDst )
    		{
    		case ePIXEL_FORMAT_L8:		convert<ePIXEL_FORMAT_L8	>( p_pSrc, p_pDest );	break;
    		case ePIXEL_FORMAT_A8L8:	convert<ePIXEL_FORMAT_A8L8	>( p_pSrc, p_pDest );	break;
    		case ePIXEL_FORMAT_A1R5G5B5:	convert<ePIXEL_FORMAT_A1R5G5B5	>( p_pSrc, p_pDest );	break;
    		case ePIXEL_FORMAT_A4R4G4B4:	convert<ePIXEL_FORMAT_A4R4G4B4	>( p_pSrc, p_pDest );	break;
    		case ePIXEL_FORMAT_R8G8B8:	convert<ePIXEL_FORMAT_R8G8B8	>( p_pSrc, p_pDest );	break;
    		case ePIXEL_FORMAT_A8R8G8B8:	convert<ePIXEL_FORMAT_A8R8G8B8	>( p_pSrc, p_pDest );	break;
    		case ePIXEL_FORMAT_YUY2:	convert<ePIXEL_FORMAT_YUY2	>( p_pSrc, p_pDest );	break;
    		default:			throw std::runtime_error( "No conversion defined" );	break;
    		}
    	}
    };
    Enfin, grâce à tout ce bordel tu peux implémenter des fonctions utilisant les versions template:

    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
    unsigned int GetBytesPerPixel( ePIXEL_FORMAT p_pfFormat )
    {
    	unsigned int l_uiReturn;
    	switch( p_pfFormat )
    	{
    	case ePIXEL_FORMAT_L8		: l_uiReturn = pixel_definitions<ePIXEL_FORMAT_L8	>::size;	break;
    	case ePIXEL_FORMAT_A8L8		: l_uiReturn = pixel_definitions<ePIXEL_FORMAT_A8L8	>::size;	break;
    	case ePIXEL_FORMAT_A1R5G5B5	: l_uiReturn = pixel_definitions<ePIXEL_FORMAT_A1R5G5B5	>::size;	break;
    	case ePIXEL_FORMAT_A4R4G4B4	: l_uiReturn = pixel_definitions<ePIXEL_FORMAT_A4R4G4B4	>::size;	break;
    	case ePIXEL_FORMAT_R8G8B8	: l_uiReturn = pixel_definitions<ePIXEL_FORMAT_R8G8B8	>::size;	break;
    	case ePIXEL_FORMAT_A8R8G8B8	: l_uiReturn = pixel_definitions<ePIXEL_FORMAT_A8R8G8B8	>::size;	break;
    	case ePIXEL_FORMAT_DXTC1	: l_uiReturn = pixel_definitions<ePIXEL_FORMAT_DXTC1	>::size;	break;
    	case ePIXEL_FORMAT_DXTC3	: l_uiReturn = pixel_definitions<ePIXEL_FORMAT_DXTC3	>::size;	break;
    	case ePIXEL_FORMAT_DXTC5	: l_uiReturn = pixel_definitions<ePIXEL_FORMAT_DXTC5	>::size;	break;
    	case ePIXEL_FORMAT_YUY2		: l_uiReturn = pixel_definitions<ePIXEL_FORMAT_YUY2	>::size;	break;
    	default				: l_uiReturn = 0;							break;
    	}
    	return l_uiReturn;
    }
     
    void ConvertPixel( ePIXEL_FORMAT p_eSrcFmt, unsigned char const * p_pSrc, ePIXEL_FORMAT p_eDestFmt, unsigned char * p_pDest )
    {
    	switch( p_eSrcFmt )
    	{
    	case ePIXEL_FORMAT_L8 :		pixel_definitions<ePIXEL_FORMAT_L8		>::convert_dyn(p_pSrc, p_pDest, p_eDestFmt );	break;
    	case ePIXEL_FORMAT_A8L8 :	pixel_definitions<ePIXEL_FORMAT_A8L8		>::convert_dyn(p_pSrc, p_pDest, p_eDestFmt );	break;
    	case ePIXEL_FORMAT_A1R5G5B5 :	pixel_definitions<ePIXEL_FORMAT_A1R5G5B5	>::convert_dyn(p_pSrc, p_pDest, p_eDestFmt );	break;
    	case ePIXEL_FORMAT_A4R4G4B4 :	pixel_definitions<ePIXEL_FORMAT_A4R4G4B4	>::convert_dyn(p_pSrc, p_pDest, p_eDestFmt );	break;
    	case ePIXEL_FORMAT_R8G8B8 :	pixel_definitions<ePIXEL_FORMAT_R8G8B8		>::convert_dyn(p_pSrc, p_pDest, p_eDestFmt );	break;
    	case ePIXEL_FORMAT_A8R8G8B8 :	pixel_definitions<ePIXEL_FORMAT_A8R8G8B8	>::convert_dyn(p_pSrc, p_pDest, p_eDestFmt );	break;
    	case ePIXEL_FORMAT_DXTC1 :	pixel_definitions<ePIXEL_FORMAT_DXTC1		>::convert_dyn(p_pSrc, p_pDest, p_eDestFmt );	break;
    	case ePIXEL_FORMAT_DXTC3 :	pixel_definitions<ePIXEL_FORMAT_DXTC3		>::convert_dyn(p_pSrc, p_pDest, p_eDestFmt );	break;
    	case ePIXEL_FORMAT_DXTC5 :	pixel_definitions<ePIXEL_FORMAT_DXTC5		>::convert_dyn(p_pSrc, p_pDest, p_eDestFmt );	break;
    	case ePIXEL_FORMAT_YUY2 :	pixel_definitions<ePIXEL_FORMAT_YUY2		>::convert_dyn(p_pSrc, p_pDest, p_eDestFmt );	break;
    	}
    }
    Enfin, pour ton problème d'images avec le même format, tu peux très bien envisager de mettre un argument template à la classe Image, cet argument étant le format de pixel :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    template <ePIXEL_FORMAT> class Image;
    Si vous ne trouvez plus rien, cherchez autre chose...

    Vous trouverez ici des tutoriels OpenGL moderne.
    Mon moteur 3D: Castor 3D, presque utilisable (venez participer, il y a de la place)!
    Un projet qui ne sert à rien, mais qu'il est joli (des fois) : ProceduralGenerator (Génération procédurale d'images, et post-processing).

  14. #14
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 199
    Par défaut
    J'ai jamais dis que je n'utilisais pas la génération hardware de mimap, bien au contraire!

    L'utilisateur à le choix entre pas de mipmap, la génération hardware, ou le chargement de mipmap perso.

    Quels avantages me diras-tu entre les deux derniers? Et bien tout simplement les formats DXT contiennent directement les mipmaps dans les fichiers.dds et j'ai même encore mieux. Si le niveau 0 est tout bleu, le niveau 1 est tout jaune, le niveau 2 est tout rouge, etc et ainsi de suite je me créer une texture pour débuger mes mipmaps Très utiles en soit, pour plus d'infos se post très intéressant en image :

    Mipmap debugging with dds file

    Sinon je suis arrivé au même principe que toi pour ta classe pixel_definition, seulement dans GetBytesPerPixel c'est toujours un switch manuel, et donc si tu rajoute un format de pixel mais que t'oublie de le rajouter dans le switch alors la Boum Surtout que tu renvoi 0, ne serait-il pas mieux une exception?



    Pour mon problème d'image si je met juste un param template alors la catastrophe car toute les images ayant un format différents ne seront même pas du même type. C'est pour cela que koala01 les faisaient toutes hériter d'une classe de base!
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template<int Format>
    class FormatedImage : public Image
    {
        // ...
    };

  15. #15
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par victor_gasgas Voir le message
    Sinon je suis arrivé au même principe que toi pour ta classe pixel_definition, seulement dans GetBytesPerPixel c'est toujours un switch manuel, et donc si tu rajoute un format de pixel mais que t'oublie de le rajouter dans le switch alors la Boum Surtout que tu renvoi 0, ne serait-il pas mieux une exception?
    Et, que dirais tu carrément d'une erreur de compilation

    Encore une fois, l'ajout d'une abstraction complémentaire devrait nous y aider

    Commençons par modifier un tout petit peu l'énumération pour qu'elle reflète les valeurs correctes [EDIT]Du moins, au plus tard quand tu écriras un code qui essaye de les appeler [/EDIT]
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    enum class PixelFormat
    {
        R8U = 1, ///< R 8 bits 8 unsigned
        RG8U = 2, ///< RG 16 bits 88 unsigned
        RGB8U = 3, ///< RGB 24 bits 888 unsigned
        RGBA8U = 4, ///< RGBA 32 bits 8888 unsigned
    };
    De cette manière, [CODEINLINE]PixelFormatInfo<R8U>::formatType[CODEINLINE](qui pourrait d'ailleurs s'appeler size ) renvoye... la taille par pixel

    D'ailleurs, modifions les spécialisation de PixelFormatInfo, non seulement pour disposer de size qui sera plus parlant, mais aussi pour pouvoir disposer d'une chaine de caractère l'identifiant
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    template <>
    class PixelFormatInfo<R8U>
    {
        public :
            enum{ size= R8U};
            typedef UnCompressibleFormat compressFormat_type;
            typedef PixelFormatInfo<R8U> pixelformat_type;
            bool isConvertible() const{return compressFormat_type::isConvertible;}
           static const std::string toString{return "R8U";}
    };
    /** meme chose pour RG8U, RGB8U et RGBA8U  ;) */
    Il nous faut ensuite 16 foncteurs de conversion (pour prendre l'ensemble des cas envisageables) dont quatre renvoient simplement la valeur "source" ...
    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
     
    template <template<typename> class Src,
                     template<typename> class Dest>
    class Converter;
    /* les quatre cas qui ne convertissent rien :D */
    template <template<typename> class Src>
    class Converterl<Src, Src>
    {
        void operator()(unsigned char const * p_pSrc, unsigned char * p_pDest)
        {
            memcpy(p_pdDest,p_pSrc,Src::size);
        }
    };
    /* les autres ressembleront à  */
    template <>
    class Converter<PixelFormatInfo<R8U>, PixelFormatInfo<RG8U>>
    {
        void operator()(unsigned char const * p_pSrc, unsigned char * p_pDest)
        {  
            /* la manière de convertir ;-) */
        }
    };
    Et, enfin, la class template FormatedImage prendra la forme de
    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 <int Format>
    class FormatedImage : public Image
    {
        PixelFormatInfo<Format> infos_;
        typedef PixelFormatInfo<Format> pixeformat_type;
        public:
            /* on s'arrange tout de suite pour prendre n'importe quelle 
             * spécialisation du convertisseur :D
             */
           template <typename T > //T dois etre de type PixelFormatInfo :D
                                  // ou au moins disposer d'un typedef pixelformat_type
           void convert( T const & format, 
                        unsigned char const * p_pSrc, 
                        unsigned char * p_pDest)
           {
               Converter<pixeformat_type, T::pixelformat_type>()(p_pSrc, p_pDest);
           }
        private:
           unsinged const char * piexls_;
    };
    C'est y pas magique pas un seul switch... case et une erreur de compilation si, d'aventure, tu rajoute un format de pixels et que tu ne prévois pas les conversions correspondantes
    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

  16. #16
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 199
    Par défaut
    Alors la j'avoue être plus que conquis par l'utilisation de template. En effet tout se déroule durant la phase de compilation, se qui est parfait. La fonction convert() me convent parfaitement.
    Mais ce que j'aime par dessus tout c'est un code non redondant

    Donc je m'en vais faire mes tests tout ce passe bien sur de petits exemples, jusqu'au moment où je me rend compte que mes Images sont chargés dynamiquement, donc je pers les templates. En effet pour l'instant l'image retournée possède le même format que celui dans le fichier.

    Il faut savoir que j'ai une classe Texture, qui est un handle sur une texture (2D pour faire simple) sur le GPU. Or celle-ci possède une constructeur prenant des images.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class Texture
    {
        Texture(const std::shared_ptr<Image> &image);
     
        // Le reste de la classe
    }
    Et la je sais pas comment m'en sortir. Dois-je faire :
    - Une fonction template qui renvoi une FormattedImage, mais alors en utilisant le polyphormisme dans le constructeur de Texture je perds le paramètre template et donc tout le travail réalisé précédement.
    - Une fonction qui renvoie une Image mais la pareil j'ai perdu mon paramètre template.
    - Une autre solution que j'ai pas encore trouvée...

    Alors je me dis pas de soucis :
    "all problems in computer science can be solved by another level of indirection" .
    Mais la je suis bien loin de le trouver je pense

    En tout cas merci beaucoup pour votre aide!

  17. #17
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    En fait, plus ça va, et plus j'ai le sentiment que la responsabilité de la conversion ne devrait pas échoir à la classe image, mais plutot à une forme de visiteur...

    En effet, la classe Image est une classe ayant sémantique d'entité, surtout en ce qui concerne la conversion: si tu convertis une image qui est dans un format donné en une image qui est dans un autre format, tu obtiens, fatalement... une nouvelle image

    L'avantage, c'est que nous avons déjà tout ce qui nous sera utile pour y arriver : il "suffit" de placer la fonction convert() dans une autre classe

    Je m'explique :

    Grace à la classe Converter, il nous est possible d'invoquer
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Converter<type_source, type_destination>()(donnee_source, donnee_destination);
    depuis n'importe où dans le code...

    Comme on dispose de pointeur (ou de référence) sur la classe de base, "YAKA" mettre en place un double dispatch qui nous permette de récupérer les types réels, et les données correspondantes

    Le DP visiteur peut nous venir, en partie du moins, en aide car son principe est, justement, de mettre le double dispatch en oeuvre, sous une forme proche de
    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
    42
    43
    44
    45
    class Visitor
    {
    public:
        void visit(Derived1 /* const */ & d) /* const */
        {
            // ce qu'il faut faire avec un objet de type Derived1
        }
        void visit(Derived2 /* const */ & d) /* const */
        {
            // ce qu'il faut faire avec un objet de type Derived2
        }
        void visit(Derived3 /* const */ & d) /* const */
        {
            // ce qu'il faut faire avec un objet de type Derived3
        }
    };
    class Base
    {
        public:
            virtual void accept(Visitor /* const &*/ v)/* const*/ =0; 
    };
    class Derived1 : public Base
    {
        public:
            virtual void accept(Visitor /* const &*/ v) /* const */
            {
                return v.visit(*this);
            }
    };
    class Derived2 : public Base
    {
        public:
            virtual void accept(Visitor /* const &*/ v) /* const */
            {
                v.visit(*this);
            }
    };
    class Derived3 : public Base
    {
        public:
            virtual void accept(Visitor /* const &*/ v) /* const */
            {
                v.visit(*this);
            }
    };
    [NOTA]Dans le cas présent, Derived1, Derived2 et Derived3 sont autant de spécialisation de... FormatedImage<PixelFormat>, et la fonction visit() du visiteur peut parfaitement prendre les arguments qui seront utiles

    D'un autre coté, nous savons d'office le format de destination, et nous n'aurons donc pas à nous inquiéter d'implémenter le double dispatch pour cela (nous devrons seulement veiller à ... etre en mesure d'accéder au tableau d'unsigned char )

    Mais il nous faut une "classe intermédiaire" nous permettant de faire le lien entre le visteur et l'image de destination...

    Nous pouvons très bien la généraliser sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template <int format>
    struct  visitorHelper
    {
        visitorHelper( /* paramètres utiles */): destination( new FormatedImage <Format>( /* paramètres utiles */){}
        FormatedImage * destination;
    };
    A partir de là, nous pouvons envisager de généraliser le convertisseur sous une forme qui pourrait ressembler à
    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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    /* nous allons généraliser le convertisseur sur base du format de 
     * destination...
     *
     * il nous faut donc une classe de base pour le convertisseur pour que 
     * toutes les spécialisations puissent etre utilisées :D
     */
    class ImageConverter
    {
        public:
            /* la fonction visit() va renvoyer un pointeur sur l'image de
             * destination : on peut la considérer, au niveau du type de retour
             * comme une Imge :D
             * on rend la fonction virtuelle pure car il n'y aura que lorsque
             * nous utiliserons les spécialisations spécifiques au type
             * d'images de destination que nous serons en mesure d'avoir 
             * l'implémentation
             */
             */
           virtual Image * visit(FormatedImage<RBU> const &)/* const */;
           virtual Image * visit(FormatedImage<RG8U > const &)/* const */;
           virtual Image * visit(FormatedImage<RGB8U > const &)/* const */;
    };
    template <int DestinationType>
    class SpecialisedImageConverter : public ImageConverter
    {
        typedef FormatedImage<DestinationType> destination_type;
        public:
           virtual Image * visit(FormatedImage<RBU> const & src)/* const */
           {
              ConvertHelper <destination_type::size> helper(/* paramètres utiles */ );
              Convert<FormatedImage<RBU>::pixelformat_type,
                            destination_type::pixelformat_type>()
                            (src.data(), helper.destination->data());
               return helper.destination;
     
           }
           virtual Image * visit(FormatedImage<RG8U > const & src)/* const */
           {
              ConvertHelper <destination_type::size> helper(/* paramètres utiles */ );
              Convert<FormatedImage<RG8U >::pixelformat_type,
                            destination_type::pixelformat_type>()
                            (src.data(), helper.destination->data());
               return helper.destination;
           }
           virtual Image * visit(FormatedImage<RGB8U > const & src)/* const */
           {
              ConvertHelper <destination_type::size> helper(/* paramètres utiles */ );
              Convert<FormatedImage<RGB8U >::pixelformat_type,
                            destination_type::pixelformat_type>()
                            (src.data(), helper.destination->data());
               return helper.destination;
           }
    };
    Et, comme le visiteur renvoie un pointeur sur une image, nous aurons donc, au niveau de la classe Image la fonction virtuelle pure
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class Image
    {
        public:
            virtual Image* accept(ImageConverter /* const */ &) /* const */ =0;
    };
    et, au niveau de la classe dérivée
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    template <int Format>
    class FormatedImage : public Image
    {
        /* comme précédemment */
        public:
            virtual Image * accept(ImageConverter /* const */ & v)/*const */
            {
                v.visit(*this);
            }
    };
    Le tout pouvant être utilisé, tout simplement, sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    Image * convertTo(PixelFormat pixel, Image * src)
    {
        SpecialisedImageConverter<pixel> converter;
        Image* converted=src->accept(converter);
        return converted;
    }
    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

  18. #18
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 199
    Par défaut
    Merci je pense qu'avec tout ça je vais bien arriver à ce que je veux. Merci beaucoup!

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Recherche designer pour [Nouveau Concept] !
    Par MoMoney dans le forum Autres
    Réponses: 0
    Dernier message: 07/04/2010, 21h28
  2. [Conception] Plusieurs design pour un même contenu
    Par cocoyot dans le forum Webdesign & Ergonomie
    Réponses: 1
    Dernier message: 22/11/2007, 15h14
  3. [Conception] - Organisation des pages pour une requete.
    Par ShinJava dans le forum PHP & Base de données
    Réponses: 14
    Dernier message: 24/10/2005, 16h33
  4. [Observateur] existe-t-il un package proposant ce design pour C# ?
    Par sopi dans le forum Design Patterns
    Réponses: 1
    Dernier message: 25/05/2005, 21h48

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