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 :

Classes templates : classes politiques, polymorphisme, spécialisation de méthodes templates avec des templates


Sujet :

Langage C++

  1. #1
    Membre à l'essai
    Profil pro
    Développeur informatique
    Inscrit en
    Juin 2003
    Messages
    14
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2003
    Messages : 14
    Points : 10
    Points
    10
    Par défaut Classes templates : classes politiques, polymorphisme, spécialisation de méthodes templates avec des templates
    Bonjour,
    Je souhaiterais modifier le comportement d'un code déjà écrit (celui des classes utilitaires de la bibliothèque d'imagerie NPP utilisant CUDA ). Mon problème est qu'il utilise des classes templates :
    • Image
    • ImagePacked héritant de Image (ImagePacked.h)
    • ImageCPU héritant de ImagePacked (ImagesCPU.h)
    • ImageNPP héritant de ImagePacked


    • AllocatorCPU utilisée dans ImagePacked (ImageAllocatorsCPU.h)
    • AllocatorNPP utilisée dans ImagePacked

    Les constructeurs et destructeur de imageCPU utilisent les méthodes de la classe (ici le terme structure serait surement plus approrié) AllocatorCPU par le biais de la classe ImagePacked pour allouer et désalouer les pixels des instances de imageCPU (de même avec *****NPP). Or, pour le cas d'imageCPU, je souhaiterais pouvoir choisir le type de mémoire allouée (non paginée ou paginée) en utilisant les fonctions adéquates (resp. malloc (ou new) et cudaMallocHost) puis les désalouer avec la bonne fonction (resp. free (ou delete) OU cudaFreeHost). Pour l'instant, c'est la mémoire non paginée qui est utilisée.
    L'idée serait donc d'introduire un paramètre extérieur définissant le type de mémoire à utiliser et de le conserver jusqu'à la désalocation.

    Comment puis-je faire sans trop modifier le code ?

    Merci d'avance pour toute réponse,
    Roll'Froy

    ImagePacked.h
    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
    namespace npp 
    {
      ...
     
      template<typename D, size_t N, class A>
      class ImagePacked: public npp::Image
      {
      public:
        ...
     
        ImagePacked(unsigned int nWidth, unsigned int nHeight): Image(nWidth, nHeight)
                                   , aPixels_(0)
                                   , nPitch_(0)
        {
          aPixels_ = A::Malloc2D(width(), height(), &nPitch_);
        }
     
        virtual
        ~ImagePacked()
        {
          A::Free2D(aPixels_);
        }
     
        ...
     
      private:
        D * aPixels_;
        unsigned int nPitch_;
      };
    } // npp namespace
    ImagesCPU.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    namespace npp
    {
     
      template<typename D, size_t N, class A>
      class ImageCPU: public npp::ImagePacked<D, N, A>
      {
        ...
      };
     
      typedef ImageCPU< Npp8u, 1, npp::AllocatorCPU<Npp8u,1> > ImageCPU_8u_C1;
      ...
    } // npp namespace
    ImageAllocatorsCPU.h
    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
    namespace npp
    {
      template <typename D, size_t N>
      class AllocatorCPU
      {
      public:
        static
        D *
        Malloc2D(unsigned int nWidth, unsigned int nHeight, unsigned int * pPitch)
        {
          NPP_ASSERT(nWidth * nHeight > 0);
     
          D * pResult = new D[nWidth * N * nHeight];
          *pPitch = nWidth * sizeof(D) * N;
     
          return pResult;
        };
     
        static
        void
        Free2D(D * pPixels)  
        {
          delete[] pPixels;
        };
      };
    } // npp namespace

  2. #2
    Membre à l'essai
    Profil pro
    Développeur informatique
    Inscrit en
    Juin 2003
    Messages
    14
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2003
    Messages : 14
    Points : 10
    Points
    10
    Par défaut Solution envisagée...
    La solution vers laquelle je me tourne pour l'instant est de rajouter un paramètre optionnel binaire aux constructeurs de ImageCPU. Ce paramètre (usePinnedMemory), qui a comme valeur par défaut False, est enregistré dans une variable d'instance. Cette variable servirait à modifier le comportement des constructeurs et destructeur de ImageCPU. Ceux-ci appellent les constructeurs et destructeur de la classe mère (ImagePacked).

    Je rencontre plusieurs difficultés :
    1. Déjà, comment "shunter" les constr. et destr. de la classe mère ? C'est à dire les appeler si usePinnedMemory = false et utiliser une partie de leur code quand usePinnedMemory = true ?
    2. N'est-ce pas possible de faire plus propre ? Car, avec ma méthode, je dois modifier une bonne partie de ImageCPU.h ...
    3. Les paramètres des templates sont
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      <typename D, size_t N, class A>
      et je fais une spécialisation : J'en fais qu'une mais ça demande à faire beaucoup de modifs pour rajouter qu'une seule fonctionnalité...


    N'hésitez pas à me demander plus d'infos, j'essaye d'être clair mais ces histoires de polymorphisme de classes templates ont du mal à passer ...

    Roll'Froy

    ImagesCPU.h
    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
    namespace npp
    {
      template<size_t N, class A>
      class ImageCPU<Npp8u, N, A> : public npp::ImagePacked<Npp8u, N, A>
      {
      public:
     
        ImageCPU()
        {
          this->usePinnedMemory_ = false;
        }
     
        ImageCPU(unsigned int nWidth, unsigned int nHeight, bool usePinnedMemory = false): ImagePacked<Npp8u, N, A>(nWidth, nHeight)
                                                                                         , usePinnedMemory_( usePinnedMemory )
        {
          this->aPixels_ = A::Malloc2D(this->width(), this->height(), &this->nPitch_, usePinnedMemory_);
        }
     
        ImageCPU(const npp::Image::Size & rSize, bool usePinnedMemory = false): ImagePacked<Npp8u, N, A>(rSize)
                                                                              , usePinnedMemory_( usePinnedMemory )
        {
          this->aPixels_ = A::Malloc2D(this->width(), this->height(), &this->nPitch_, usePinnedMemory_);
        }
     
        ImageCPU(const ImageCPU<Npp8u, N, A> & rImage, bool usePinnedMemory = false): Image(rImage)
                                                                                    , usePinnedMemory_( usePinnedMemory )
        {
          this->aPixels_ = A::Malloc2D(this->width(), this->height(), &this->nPitch_, usePinnedMemory_);
          A::Copy2D(this->aPixels_, this->nPitch_, rImage.pixels(), rImage.pitch(), this->width(), this->height());
        }
     
        ~ImageCPU()
        {
          A::Free2D(ImagePacked<Npp8u, N, A>::data(), usePinnedMemory_);
        }
     
      private:
        bool usePinnedMemory_;
      };
    } // npp namespace
    ImageAllocatorsCPU.h
    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
    namespace npp
    {
      template <typename D, size_t N>
      class AllocatorCPU
      {
      public:
     
        static
        D *
        Malloc2D(unsigned int nWidth, unsigned int nHeight, unsigned int * pPitch, bool usePinnedMemory = 0)
        {
          NPP_ASSERT(nWidth * nHeight > 0);
          D * pResult;
     
          if ( usePinnedMemory )
          {
            cudaError_t eResult;
            size_t mem_size = nWidth * N * nHeight * sizeof(D);
            eResult = cudaMallocHost( (void**) &pResult, mem_size );
            NPP_ASSERT(cudaSuccess == eResult);
          }
          else
            pResult = new D[nWidth * N * nHeight];
     
          *pPitch = nWidth * sizeof(D) * N;
     
          return pResult;
        };
     
        static
        void
        Free2D(D * pPixels, bool usePinnedMemory = 0)
        {
          if ( usePinnedMemory )
          {
            cudaError_t eResult;
            eResult = cudaFreeHost( pResult );
            NPP_ASSERT(cudaSuccess == eResult);
          }
          else
          {
            delete[] pPixels;
          }
        };
      };
    } // npp namespace

  3. #3
    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 ne suis pas sur d'avoir complètement compris ton problème. Pourquoi n'instancies-tu pas ta classe image avec un allocateur différent selon que le type d'allocation que tu souhaites ? As-tu jeter un coup d'oeil à ce tutoriel : Présentation des classes de Traits et de Politiques en C++ (et peut-être celui ci : Mariage de la Programmation Orientée Objet et de la Programmation Générique : Type Erasure) ?

  4. #4
    Membre à l'essai
    Profil pro
    Développeur informatique
    Inscrit en
    Juin 2003
    Messages
    14
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2003
    Messages : 14
    Points : 10
    Points
    10
    Par défaut
    Salut,
    Merci pour ta réponse. Hum, je pense que tu as compris mon problème : je souhaite implémenter une variante pour le type d'allocation dans la mémoire CPU. Donc, soit je modifie mon AllocatorCPU pour que je puisse choisir le type d'allocation que je veux (mémoire paginée/non paginée), soit je crée un "AllocatorCPUwithPN" (PN=PinnedMemory) et je choisis l'allocateur quand j'instancie ImagePacked (ce que tu me propose). Le problème, je pense, est que ces 2 méthodes demande pas mal de modifications amont (dans les classes ImageCPU ou/et ImagePacked).

    J'ai lu les articles que tu cites mais j'ai du mal à tout assimiler. J'ai compris la base des templates, cependant la marche suivante est un peu dure à franchir... Peut-être devrai-je me pencher sur un livre comme C++ Template Metaprogramming : Concepts, Tools, and Techniques from Boost and Beyond ?

    Bon, essayons ce que tu proposes :
    Déjà, les Allocator*** sont bien des classes de politique ?
    Puis-je utiliser les définitions des types ImageCPU*** dans ImageCPU.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    typedef ImageCPU< Npp8u,  1, npp::AllocatorCPUwithPN<Npp8u, 1>  > ImageCPUwithPN_8u_C1;
    ou dois-je spécialiser ma classe template ImagePacked pour chaque Allocator***?

    Rien à voir avec les templates : peut-on appeler les constructeurs d'une classe mère (ou grand-mère) dans ceux (différents) de la classe fille ? Si c'est possible, comment gérer l'initialisation des variables d'instances de la classe mère (ou grand-mère) ?

    Roll'Froy

  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
    Salut,
    (rapidement)
    Citation Envoyé par k-djo Voir le message
    J'ai lu les articles que tu cites mais j'ai du mal à tout assimiler. J'ai compris la base des templates, cependant la marche suivante est un peu dure à franchir... Peut-être devrai-je me pencher sur un livre comme C++ Template Metaprogramming : Concepts, Tools, and Techniques from Boost and Beyond ?
    Il y a quelques livres de ref. sur la prog générique mais je ne suis pas le mieux placé pour te dire vers lequel aller de préférence.

    Citation Envoyé par k-djo Voir le message
    Bon, essayons ce que tu proposes :
    Déjà, les Allocator*** sont bien des classes de politique ?
    C'est ce qui y ressemble le +
    Citation Envoyé par k-djo Voir le message
    Puis-je utiliser les définitions des types ImageCPU*** dans ImageCPU.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    typedef ImageCPU< Npp8u,  1, npp::AllocatorCPUwithPN<Npp8u, 1>  > ImageCPUwithPN_8u_C1;
    ou dois-je spécialiser ma classe template ImagePacked pour chaque Allocator***?
    L'idée est bien de ne pas modifier les classes ImagesTRUCMUCHE mais bien de spécialiser tes comportements dans les Allocator. Quelque chose du genre :
    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
     
    struct AllocatorCPU_P 
    {
       static void *allocate(size_t){...}
       static void free(void*){...}
    };
     
    struct AllocatorCPU_NP
    {
       static void *allocate(size_t){...}
       static void free(void*){...}
    };
    etc...
    template<...,class Allocator> class Image
    {
     
    /*...*/
       void foo()
       {
           /*....*/
          Allocator::allocate(/*...*/);
       }
    }
    typedef ImageCPUWithP Image<AllocatorCPU_P>;
    typedef ImageCPUWithNP Image<AllocatorCPU_NP>;
    Comme un typedef a besoin d'instancier tous les paramètres génériques (en attendant C++0x), tu auras certainement besoin de ça : Template rebinding en C++

    Citation Envoyé par k-djo Voir le message
    Rien à voir avec les templates : peut-on appeler les constructeurs d'une classe mère (ou grand-mère) dans ceux (différents) de la classe fille ? Si c'est possible, comment gérer l'initialisation des variables d'instances de la classe mère (ou grand-mère) ?
    Ca sent mauvais comme question Tu peux donner un exemple de ce que tu veux faire ?
    Tu peux appeler les constructeurs des classes mères (pas grand-mère) et des classes héritées virtuellement n'importe où dans l'arbre d'héritage, de préférence en suivant l'ordre adéquat :
    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
     
    class A
    {};
    class B : public A
    {};
    class V
    {};
    class C : virtual public V
    {};
     
    class derivee : public B, public C
    {
       derivee()
       : V(), B(), C()
        //,  A() ->KO
       {}
    };
    Il ne faut pas hériter virtuellement pour pouvoir le faire (ne jamais tordre la syntaxe pour besoin de conception) ! C'est pour ça que je te demande ce que tu veux faire concrètement.

  6. #6
    Membre à l'essai
    Profil pro
    Développeur informatique
    Inscrit en
    Juin 2003
    Messages
    14
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2003
    Messages : 14
    Points : 10
    Points
    10
    Par défaut Polymorphisme de classes template OU spécialisation de méthode template de classe template (paramètres ≠ ) ?
    Bonjour,

    Tout d'abord, merci 3DArchi pour ta réponse, elle m'a bien servi (malgré qu'elle ait été apparemment écrite "rapidement" ) !
    Au départ je pensais juste modifier un peu le code d'AllocatorCPU mais, au final, c'est tout son comportement que je devais changer. Donc, comme tu me l'as conseillé, j'ai créé un AllocatorCPUwithPM possédant son propre comportement (en effet, pourquoi changer la conception ?!) et ça fonctionne...

    Maintenant, je cherche à généraliser mon code à un niveau supérieur. J'ai en effet une fonction function( Image(CPU/NPP), Image(CPU/NPP) ) que je surchargeais :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    void function( ImageCPU &, ImageCPU &);
    void function( ImageCPU &, ImageNPP &);
    void function( ImageNPP &, ImageCPU &);
    void function( ImageNPP &, ImageNPP &);
    ( ImageCPU et ImageNPP sont des classes template, j'ai volontairement omis les paramètres... )
    or le code était le même à part celui de méthodes de copie appartenant à des objets de type ImageNPP, ex :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
        | anotherImageNPP.copyFromHost( myImageCPU );
     ou |
        | anotherImageNPP.copyFromDevice( myImageNPP );
    et
        | anotherImageNPP.copyToHost( myImageCPU );
     ou |
        | anotherImageNPP.copyToDevice( myImageNPP );
    anotherImageNPP étant un objet interne à la fonction et myImage*** étant un des 2 paramètres de la fonction.
    Donc, déjà, j'ai surchargé ces méthodes, en supprimant "Host" ou "Device". Après, je vois deux solutions pour passer l'un des deux paramètres de ma fonction, qui peuvent être un objet de type soit ImageCPU soit ImageNPP, à mes méthodes copyFrom et copyTo :
    1. le polymorphisme en utilisant la classe mère ImagePacked;
    2. templater ces méthodes et les spécialiser.

    Le problème est que je ne vois pas comment implémenter ces solutions :
    1. Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
       
      void function( ImagePacked & myImage1, ImagePacked & myImage2)
      {
        ...
        anotherImageNPP.copyFrom( myImage1 );
        ...
        anotherImageNPP.copyTo( myImage2 );
      }
      Comment "caster" mes objets myImage# dans leur type (classe fille) d'origine alors que je ne le connais pas ?
    2. Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
       
      namespace
      {
        class ImageNPP
        {
              template <class I> void ImageNPP::copyFrom( I image) {}
        }
       template <ImageNPP> void ImageNPP::copyFrom( ImageNPP image) { ... }
       template <ImageCPU> void ImageNPP::copyFrom( ImageCPU image) { ... }
      }
      Je crois que ça pourrait fonctionner si je manipulais des classes classiques mais avec des classes templates ?

    le point difficile pour moi, c'est que les classes Image ( ImageCPU, ImageNPP et ImagePacked) sont templatées ( leurs paramètres : <typename D, size_t N, class A> ).

    Si les deux solutions sont possibles, laquelle est "la meilleure" ?

    Toujours en vous remerciant de me répondre ou de me lire,
    Roll'Froy

  7. #7
    Membre à l'essai
    Profil pro
    Développeur informatique
    Inscrit en
    Juin 2003
    Messages
    14
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2003
    Messages : 14
    Points : 10
    Points
    10
    Par défaut
    [~Hors Sujet]
    Citation Envoyé par 3DArchi Voir le message
    Comme un typedef a besoin d'instancier tous les paramètres génériques (en attendant C++0x), tu auras certainement besoin de ça : Template rebinding en C++
    Hum, je n'ai pas vu où je pouvais utiliser le "Template rebinding"...

    Citation Envoyé par 3DArchi Voir le message
    Envoyé par k-djo
    Rien à voir avec les templates : peut-on appeler les constructeurs d'une classe mère (ou grand-mère) dans ceux (différents) de la classe fille ? Si c'est possible, comment gérer l'initialisation des variables d'instances de la classe mère (ou grand-mère) ?
    Ca sent mauvais comme question Tu peux donner un exemple de ce que tu veux faire ?
    Tu peux appeler les constructeurs des classes mères (pas grand-mère) et des classes héritées virtuellement n'importe où dans l'arbre d'héritage, de préférence en suivant l'ordre adéquat...
    Heu, en effet ça sent peut-être pas très bon... Ce à quoi je pensais ressemble à ça :
    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
     
    class Mere
    {
      Mere() {...}
      Mere( int truc ) : bidule(3)
     {
       machin = truc * bidule;
     }
      ...
      int machin, bidule;
    }
    class Fille
    {
      Fille() : Mere () {...}
      Fille( int chose ) : Mere::bidule(7)
      {
        chose -= 5;
        Mere::Mere( chose );
      }
      ...
      int machin;
    }
    Je ne suis pas sûr d'avoir bien représenter la chose, je fatigue un peu .
    [/~Hors Sujet]

    Sinon, je ne suis pas contre qu'un modo change le titre de la discussion, genre : "Classes templates : classes politiques, polymorphisme, spécialisation de méthodes templates ayant pour paramètre une classe template...(modif des fichiers UtilNPP)".

    Roll'Froy

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

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    Citation Envoyé par k-djo Voir le message
    Comment "caster" mes objets myImage# dans leur type (classe fille) d'origine alors que je ne le connais pas ?
    Le virtuel ne s'applique que sur l'objet appelant et pas sur les paramètres. Donc, ça me parait une solution difficile à mettre en oeuvre, d'autant que tu ne peux faire de downcast (toujours déconseillé) puisque tu ne connais pas le type effectif. La solution générique m'a l'air plus appropriée.
    Citation Envoyé par k-djo Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    namespace
    {
      class ImageNPP
      {
            template <class I> void ImageNPP::copyFrom( I image) {}
      }
     template <ImageNPP> void ImageNPP::copyFrom( ImageNPP image) { ... }
     template <ImageCPU> void ImageNPP::copyFrom( ImageCPU image) { ... }
    }
    Je ne comprend pas très bien ton problème.
    Je verrais bien un truc comme ça (en reprenant les éléments de la discussion) :
    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
    56
    57
    58
    59
    typedef unsigned int size_t;
    struct AllocatorCPU_P
    {
       static void *allocate(size_t){/*...*/return 0;}
       static void free(void*){/*...*/}
    };
     
    struct AllocatorCPU_NP
    {
       static void *allocate(size_t){/*...*/return 0;}
       static void free(void*){/*...*/}
    };
     
    template<class Foo, class Allocator> class Image
    {
     
    };
     
    typedef Image<int,AllocatorCPU_P> ImageCPUWithP;
    typedef Image<int, AllocatorCPU_NP> ImageCPUWithNP ;
     
     
    template<typename cible_T, typename source_T>
    struct conversion_image;
     
    template<> struct conversion_image<ImageCPUWithP,ImageCPUWithNP>
    {
       static void copy(ImageCPUWithP &dest, ImageCPUWithNP const &source)
       {
          // do it
       }
    };
     
    template<> struct conversion_image<ImageCPUWithNP,ImageCPUWithP>
    {
       static void copy(ImageCPUWithNP&dest,  ImageCPUWithP const &source)
       {
          // do it
       }
    };
     
    // L'autre solution si certains paramètres doivent rester libre :
    template<class T> struct conversion_image<Image<T,AllocatorCPU_P>,Image<T, AllocatorCPU_NP> >
    {
       static void copy(Image<T,AllocatorCPU_P> &dest, Image<T, AllocatorCPU_NP> const &source)
       {
          // do it
       }
    };
     
     
    int main()
    {
       ImageCPUWithP i1;
       ImageCPUWithNP i2;
     
       conversion_image<ImageCPUWithP,ImageCPUWithNP >::copy(i1,i2);
       return 0;
    }
    Mais je n'ai peut être pas tout compris. Quelle serait pour toi le prototype idéal de tes fonctions et dans quelles classes (en omettant les problèmes de syntaxe qu'on pourra voir ensuite) ?

  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 k-djo Voir le message
    [~Hors Sujet]
    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
     
    class Mere
    {
      Mere() {...}
      Mere( int truc ) : bidule(3)
     {
       machin = truc * bidule;
     }
      ...
      int machin, bidule;
    }
    class Fille
    {
      Fille() : Mere () {...}
      Fille( int chose ) : Mere::bidule(7)
      {
        chose -= 5;
        Mere::Mere( chose );
      }
      ...
      int machin;
    }
    Je ne suis pas sûr d'avoir bien représenter la chose, je fatigue un peu .
    [/~Hors Sujet]
    Je ne comprends pas ce besoin (qui sent vraiment mauvais ). Pourquoi ça ne suffit pas :
    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 Mere
    {
      Mere() {...}
      Mere( int truc ) : bidule(3)
     {
       machin = truc * bidule;
     }
      ...
      int machin, bidule;
    }
    class Fille
    {
      Fille() : Mere () {...}
      Fille( int chose ) : Mere(chose-5)
      {
      }
      ...
      int machin;
    }

  10. #10
    Alp
    Alp est déconnecté
    Expert éminent sénior

    Avatar de Alp
    Homme Profil pro
    Inscrit en
    Juin 2005
    Messages
    8 575
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Juin 2005
    Messages : 8 575
    Points : 11 860
    Points
    11 860
    Par défaut
    Citation Envoyé par k-djo Voir le message
    Salut,
    Merci pour ta réponse. Hum, je pense que tu as compris mon problème : je souhaite implémenter une variante pour le type d'allocation dans la mémoire CPU. Donc, soit je modifie mon AllocatorCPU pour que je puisse choisir le type d'allocation que je veux (mémoire paginée/non paginée), soit je crée un "AllocatorCPUwithPN" (PN=PinnedMemory) et je choisis l'allocateur quand j'instancie ImagePacked (ce que tu me propose). Le problème, je pense, est que ces 2 méthodes demande pas mal de modifications amont (dans les classes ImageCPU ou/et ImagePacked).
    C'est le problème que permettent de résoudre les politiques (cf la 2ème partie de mon article). Tu paramétrises plusieurs parties indépendantes d'une classe en confiant l'implémentation à des types passés en paramètre à ton template. Tu peux ainsi externaliser l'implémentation pour l'allocation/désallocation de mémoire (une implémentation appellera delete, l'autre cudaFreeHost, par exemple).

    Si tu as des questions ou problèmes avec les templates, traits, politiques, la métaprogrammation ou autres, n'hésite pas on est là pour y répondre

  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
    Citation Envoyé par Alp Voir le message
    C'est le problème que permettent de résoudre les politiques (cf la 2ème partie de mon article).
    C'est effectivement une des premières pistes que j'avais donné avec ton article en référence. Et d'après le dernier code, c'est une piste suivie

  12. #12
    Membre à l'essai
    Profil pro
    Développeur informatique
    Inscrit en
    Juin 2003
    Messages
    14
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2003
    Messages : 14
    Points : 10
    Points
    10
    Par défaut
    Salut,
    Merci pour vos réponses, je me demandais si le jeune gourou des templates allait intervenir . Donc pour les Allocators - classes politiques -, c'est bon.

    RAPPEL : je code en CUDA (basé sur le C/C++), d'où les termes GPU ou device et CPU ou host. Je "complète" la librairie NPP pour des applications de traitement d'images, d'où les classes templates ImageCPU et ImageNPP (utilisant la mémoire GPU) avec les paramètres template <D, N, A> :
    • D : type (des composantes) des pixels;
    • N : nombre de plans/composantes;
    • A : Allocator.


    Pour rendre mon code générique au niveau supérieur, j'ai en fait juste templaté ma fonction ("function" ou, dans le cas présent, "filterNxN") :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    template < typename D, size_t N, class As, class Ad, template <typename D1, size_t N1, class A1> class source_T, template <typename D2, size_t N2, class A2> class dest_T >
    void filterNxN( source_T<D, N, As> &, dest_T<D, N, Ad> &, vector<float> &, float, float);
    Ça fonctionne bien; je l'appelle en utilisant la détermination automatique des paramètres templates (en les omettant). J'utilise ce template compliqué au lieu d'un simple "template < class source_T, class dest_T >" parce que j'appelle, à l'intérieur, un kernel (une fonction CUDA) template que j'instancie avec D et N.
    Mais je rencontre 2 nouveaux problèmes :
    1. L'utilisateur est obligé d'appeler la fonction template dans un fichier .cu (fichier source CUDA) alors qu'il doit pouvoir utiliser les fonctions dans son code C++ de façon transparente. Une solution serait de faire un wrapper avec une fonction pour chaque instance du template. Ça sera peut être mieux dans le cas où je met mes fonctions dans une bibliothèque. Mais est-ce la meilleure solution ? Dans ce cas, quelle implémentation utiliser ? Simplement celle-ci ?
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
       
      void filterNxN_8u_C3R_CPUtoGPU( ImageCPU_8u_C3 iS,  ImageNPP_8u_C3 iD )
      {
        FilterNxN( iS, iD );
      OU (?)
        FilterNxN < Npp8u, 3, AllocatorCPU, AllocatorNPP, ImageCPU_8u_C3, ImageNPP_8u_C3 > ( iS, iD );
      }
      // avec
      typedef ImageCPU< Npp8u, 3, AllocatorCPU<Npp8u, 3> > ImageCPU_8u_C3;
      typedef ImageNPP<Npp8u, 3, AllocatorNPP<Npp8u, 3> > ImageNPP_8u_C3;
    2. Comment m'assurer que l'utilisateur va passer des paramètres de types ImageCPU ou ImageNPP et non une classe template quelconque avec les bons paramètres (Truc<D, N, A>) ? En faisant du template rebinding (j'essaye toujours de voir où je pourrais utiliser cette méthode ) ? En utilisant une des techniques de la discussion Restriction sur une fonction template ?


    [HORS SUJET]
    Citation Envoyé par 3DArchi Voir le message
    Pourquoi ça ne suffit pas :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
      ...
      Fille( int chose ) : Mere(chose-5) {
    Parce que l'instruction
    Citation Envoyé par k-djo Voir le message
    est juste là pour indiquer qu'on veut faire un pré-traitement (sur chose) ...
    [/HORS SUJET]

    En vous remerciant,
    Roll'Froy

  13. #13
    Alp
    Alp est déconnecté
    Expert éminent sénior

    Avatar de Alp
    Homme Profil pro
    Inscrit en
    Juin 2005
    Messages
    8 575
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Juin 2005
    Messages : 8 575
    Points : 11 860
    Points
    11 860
    Par défaut
    Pour 1. : un compilateur CUDA ne peut pas compiler de templates ?

    Pour 2. : tu peux ne pas demander de passer ImageNPP ou autre, mais directement prendre en paramètre template les différents paramètres templates qu'il faudra passer à ImageNPP ou quoi. Mais c'est moins souple. Tu voudras peut-être plutôt te renseigner sur Boost.Concept_Check (en définissant donc un concept Image à respecter pour que ça soit un type image valide, par exemple), ou plus généralement sur SFINAE ?

    EDIT :
    sinon pour 2), tu peux faire quelque chose comme déclarer une fonction template filterNxN, mais ne pas la définir. Et ensuite, tu fais une spécialisation pour les paramètres de la forme ImageCPU<qqch> et ImageNPP<qqch>. Sauf que la spécialisation partielle de fonction est interdite, donc il faudra que tu encapsules le tout dans une structure template, avec une fonction statique.

  14. #14
    Membre à l'essai
    Profil pro
    Développeur informatique
    Inscrit en
    Juin 2003
    Messages
    14
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2003
    Messages : 14
    Points : 10
    Points
    10
    Par défaut
    Citation Envoyé par Alp Voir le message
    Pour 1. : un compilateur CUDA ne peut pas compiler de templates ?
    Si si, mais NVCC, le compilateur CUDA, va automatiquement faire traiter le code C++ par GCC. Or la définition d'un template et ses instanciations doivent être traiter par le même compilo...

    Citation Envoyé par Alp Voir le message
    Pour 2. : tu peux ne pas demander de passer ImageNPP ou autre, mais directement prendre en paramètre template les différents paramètres templates qu'il faudra passer à ImageNPP ou quoi. Mais c'est moins souple.
    Bein, c'est ce que je fais, non ? Mais ça restreint seulement le type des paramètres template de mes objets passés en paramètres de la fonction et non le type des objets eux-même...

    Citation Envoyé par Alp Voir le message
    Tu voudras peut-être plutôt te renseigner sur Boost.Concept_Check (en définissant donc un concept Image à respecter pour que ça soit un type image valide, par exemple),
    Ça m'embête un peu de rajouter une bibliothèque .

    Citation Envoyé par Alp Voir le message
    ou plus généralement sur SFINAE ?
    Je ne vois pas très bien comment utiliser cette méthode pour "spécialiser" mon template (et non faire une "redirection" en cas d'erreur de paramètre comme dans l'exemple de la FAQ)...

    Citation Envoyé par Alp Voir le message
    EDIT :
    sinon pour 2), tu peux faire quelque chose comme déclarer une fonction template filterNxN, mais ne pas la définir. Et ensuite, tu fais une spécialisation pour les paramètres de la forme ImageCPU<qqch> et ImageNPP<qqch>. Sauf que la spécialisation partielle de fonction est interdite, donc il faudra que tu encapsules le tout dans une structure template, avec une fonction statique.
    Hum, au départ, j'ai justement templaté cette fonction parce que j'avais 4 implémentations quasi-identiques (2 types de paramètres * 2 paramètres).

    Roll'Froy

  15. #15
    Alp
    Alp est déconnecté
    Expert éminent sénior

    Avatar de Alp
    Homme Profil pro
    Inscrit en
    Juin 2005
    Messages
    8 575
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Juin 2005
    Messages : 8 575
    Points : 11 860
    Points
    11 860
    Par défaut
    Ils font donc factoriser la partie qui n'est pas identique entre les implémentations dans une entité extérieure.

    Mais quel est le critère qui distingue qui détermine que tu vas avoir besoin de telle ou telle implémentation ?

  16. #16
    Membre à l'essai
    Profil pro
    Développeur informatique
    Inscrit en
    Juin 2003
    Messages
    14
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2003
    Messages : 14
    Points : 10
    Points
    10
    Par défaut
    Salut Alp,
    Heu, je me suis peut-être mal exprimé. Voici ma fonction template :

    template < typename D, size_t N, class As, class Ad, template <typename D1, size_t N1, class A1> class source_T, template <typename D2, size_t N2, class A2> class dest_T >
    void filterNxN( source_T<D, N, As> & imageToFilter, dest_T<D, N, Ad> & imageFiltered, vector<float> vFilterMat, float weight = 0, float delta = 0) {...}

    J'aurais souhaité que les 2 premiers paramètres de ma fonction puissent n'être que de type
    • ImageCPU<typename D, size_t N, class A>
      ou
    • ImageNPP<typename D, size_t N, class A>

    Le code de ma fonction est, en fait, toujours le même. C'est juste que, dans sa définition, j'utilise des surcharges différente de mes méthodes de copie.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    ImageNPPdeTravail.copyFrom( imageToFilter )
    ...
    ImageNPPdeTravail.copyTo( imageFiltered )
    Je n'utilise pas la même implémentation de mes méthodes de copie si mon paramètre est de type ImageCPU ou de type ImageNPP.

    La solution pourrait être simplement d'envoyer une erreur au compilo.

    Roll'Froy

  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
    Y'a pas ce genre de restrictions sur les templates (concept), donc soit tu vois avec concept_check ou à la mano avec du SFINAE, ou alors tu laisses comme ça, et tu restes dans l'esprit :
    tu peux te tirer une balle dans le pied si tu veux... (ie je peux l'instancier avec un int mais ça me pétera à la gueule.)
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  18. #18
    Membre à l'essai
    Profil pro
    Développeur informatique
    Inscrit en
    Juin 2003
    Messages
    14
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2003
    Messages : 14
    Points : 10
    Points
    10
    Par défaut
    Citation Envoyé par Goten Voir le message
    tu peux te tirer une balle dans le pied si tu veux...
    L'idée est d'empêcher les futurs utilisateurs de mes fonctions de le faire par inadvertance (me concernant, il y a moins de chance) .

    Utiliser du SFINAE, pourquoi pas, mais est-ce que ça serait possible d'avoir un exemple dans ce cas concret, s'il vous plait ? Parce que je n'arrive pas à saisir comment faire avec seulement celui de la FAQ...

    Cependant mon problème concernant l'instanciation de mes template dans un fichier source CUDA peut rejoindre celui-ci:
    Citation Envoyé par k-djo Voir le message
    L'utilisateur est obligé d'appeler la fonction template dans un fichier .cu (fichier source CUDA) alors qu'il doit pouvoir utiliser les fonctions dans son code C++ de façon transparente. Une solution serait de faire un wrapper avec une fonction pour chaque instance du template. Ça sera peut être mieux dans le cas où je met mes fonctions dans une bibliothèque. Mais est-ce la meilleure solution ? Dans ce cas, quelle implémentation utiliser ? Simplement celle-ci ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    void filterNxN_8u_C3R_CPUtoGPU( ImageCPU_8u_C3 iS,  ImageNPP_8u_C3 iD )
    {
      FilterNxN( iS, iD );
    OU (?)
      FilterNxN < Npp8u, 3, AllocatorCPU, AllocatorNPP, ImageCPU_8u_C3, ImageNPP_8u_C3 > ( iS, iD );
    }
    // avec
    typedef ImageCPU< Npp8u, 3, AllocatorCPU<Npp8u, 3> > ImageCPU_8u_C3;
    typedef ImageNPP<Npp8u, 3, AllocatorNPP<Npp8u, 3> > ImageNPP_8u_C3;
    Si j'utilise cette solution, le problème est réglé (non?). Je perd alors le côté fonction générique et je dois implémenter les fonctions wrapper correspondant à chaque type possible des paramètres du template mais je garde l'idée de (n'avoir à) coder qu'une seule fois du code identique...

    Roll'Froy

  19. #19
    Alp
    Alp est déconnecté
    Expert éminent sénior

    Avatar de Alp
    Homme Profil pro
    Inscrit en
    Juin 2005
    Messages
    8 575
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Juin 2005
    Messages : 8 575
    Points : 11 860
    Points
    11 860
    Par défaut
    Citation Envoyé par k-djo Voir le message
    Utiliser du SFINAE, pourquoi pas, mais est-ce que ça serait possible d'avoir un exemple dans ce cas concret, s'il vous plait ? Parce que je n'arrive pas à saisir comment faire avec seulement celui de la FAQ...
    Hmm, un truc tout bête :
    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
    template <template <typename, size_t, class> typename ImageType> 
    struct Helper
    {
    };
     
    template <typename D, size_t N, class A>
    struct Helper< ImageCPU<D, N, A> >
    {
      typedef char is_valid_tag;
    };
     
    template <typename D, size_t N, class A>
    struct Helper< ImageNPP<D, N, A> >
    {
      typedef char is_valid_tag;
    };
    Et ensuite tu utilises SFINAE dans cet esprit-là :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template <typename T>
    void foo(const T& t, typename Helper<T>::valid_tag = 'a')
    {
    // ...
    }
    // ça ne compilera que pour les T tq Helper<T> a un nested type nommé 'valid_tag' qui peut être construits depuis un char
    (c'est vraiment à l'arrache, mais l'idée est là)

  20. #20
    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,
    Tes classes n'héritent-elles pas de ImagePacked ? N'est-ce pas sur celle-ci que tu devrais construire tes filtres génériques ?

Discussions similaires

  1. Classe pour envoyer des mails avec des template
    Par RobertP dans le forum Langage
    Réponses: 1
    Dernier message: 24/12/2011, 10h49
  2. Question de liaison avec des template
    Par dj.motte dans le forum Langage
    Réponses: 18
    Dernier message: 26/09/2008, 17h42
  3. Réponses: 1
    Dernier message: 22/08/2007, 15h48
  4. Réponses: 6
    Dernier message: 29/11/2006, 11h56
  5. Créer un type matrice avec des templates
    Par souading3000 dans le forum C++
    Réponses: 2
    Dernier message: 15/06/2006, 11h24

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