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 :

Problème de classes template + CRTP


Sujet :

Langage C++

  1. #1
    Membre habitué
    Homme Profil pro
    Doctorant en Astrophysique
    Inscrit en
    Mars 2009
    Messages
    312
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Astrophysique
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2009
    Messages : 312
    Points : 176
    Points
    176
    Par défaut Problème de classes template + CRTP
    Bonjour à tous.

    Je suis en train d'implémenter une bibliothèque d'algèbre linéaire pour des petits vecteurs et matrices + des fonctions répondant à mon domaine de recherche et j'ai un petit problème. Je connaissais vaguement le CRTP, et après quelques tests, il s'avère que c'est bien adapté à ce que je veux faire. J'ai donc fait un petit programme de test, mais il reste un souci.

    Voici d'abord le programme de test (le pb est à l'avant dernière ligne) :
    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
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    #include <iostream>
    #include <initializer_list>
    #include <type_traits>
     
    // Abstract class
    template<class TCRTP, class T, unsigned int TSIZE> class AbstractArray
    {
        // Constructor
        public:
            inline AbstractArray() : _data{}
            {
                std::cout<<"AbstractArray::AbstractArray()"<<std::endl;
            }
     
        // Copy constructor
        public:
            template<class TCRTP0, class T0> inline AbstractArray(const AbstractArray<TCRTP0, T0, TSIZE> &rhs)
            {
                std::cout<<"AbstractArray::AbstractArray(const AbstractArray<TCRTP0, T0, TSIZE> &rhs)"<<std::endl;
                for(unsigned int i = 0; i < TSIZE; ++i) {
                    _data[i] = rhs[i];
                }
            }
     
        // Initializer list constructor
        public:
            template<class T0> inline AbstractArray(const std::initializer_list<T0>& rhs)
            {
                std::cout<<"AbstractArray::AbstractArray(const std::initializer_list<T0>& rhs)"<<std::endl;
                const T0* it = rhs.begin();
                for (unsigned int i = 0; i < TSIZE; ++i) {
                    _data[i] = *it;
                    ++it;
                }
            }
     
        // Destructor
        public:
            inline ~AbstractArray()
            {
                std::cout<<"AbstractArray::~AbstractArray()"<<std::endl;
            }
     
        // Subscript operator
        public:
            inline const T& operator[](const unsigned int i) const
            {
                std::cout<<"AbstractArray::operator[](const unsigned int i) const"<<std::endl;
                return _data[i];
            }
            inline T& operator[](const unsigned int i)
            {
                std::cout<<"AbstractArray::operator[](const unsigned int i)"<<std::endl;
                return _data[i];
            }
     
        // Assignment operator
        public:
            template<class TCRTP0, class T0> inline AbstractArray<TCRTP, T, TSIZE>& operator=(const AbstractArray<TCRTP0, T0, TSIZE>& rhs)
            {
                std::cout<<"AbstractArray::operator=(const AbstractArray<TCRTP0, T0, TSIZE>& rhs)"<<std::endl;
                for (unsigned int i = 0; i < TSIZE; ++i) {
                    _data[i] = rhs[i];
                }
                return *this;
            }
     
        // Sum assignment
        public:
            template<class TCRTP0, class T0> inline AbstractArray<TCRTP, T, TSIZE>& operator+=(const AbstractArray<TCRTP0, T0, TSIZE>& rhs)
            {
                std::cout<<"AbstractArray::operator+=(const AbstractArray<TCRTP0, T0, TSIZE>& rhs)"<<std::endl;
                for (unsigned int i = 0; i < TSIZE; ++i) {
                    _data[i] += rhs[i];
                }
                return *this;
            }
     
        // Sum operator
        public:
            template<class T0> inline AbstractArray<TCRTP, typename std::common_type<T, T0>::type, TSIZE> operator+(const AbstractArray<TCRTP, T0, TSIZE>& rhs) const
            {
                return AbstractArray<TCRTP, typename std::common_type<T, T0>::type, TSIZE>(*this) += rhs;
            }
     
        // Data members
        protected:
            T _data[TSIZE];
    };
     
    // Array class
    template<class T, unsigned int TSIZE> class NArray : public  AbstractArray<NArray<T, TSIZE>, T, TSIZE>
    {
        // Constructor
        public:
            inline NArray() : AbstractArray<NArray<T, TSIZE>, T, TSIZE>()
            {
                std::cout<<"NArray::NArray()"<<std::endl;
            }
     
        // Copy constructor
        public:
            template<class TCRTP0, class T0> inline NArray(const AbstractArray<TCRTP0, T0, TSIZE> &rhs) : AbstractArray<NArray<T, TSIZE>, T, TSIZE>(rhs)
            {
                std::cout<<"NArray::NArray(const AbstractArray<TCRTP0, T0, TSIZE> &rhs)"<<std::endl;
            }
     
        // Initializer list constructor
        public:
            template<class T0> inline NArray(const std::initializer_list<T0>& rhs) : AbstractArray<NArray<T, TSIZE>, T, TSIZE>(rhs)
            {
                std::cout<<"NArray::NArray(const std::initializer_list<T0>& rhs)"<<std::endl;
            }
     
        // Destructor
        public:
            inline ~NArray()
            {
                std::cout<<"NArray::~NArray()"<<std::endl;
            }
    };
     
    // Vector class
    template<class T, unsigned int TSIZE> class NVector : public  AbstractArray<NVector<T, TSIZE>, T, TSIZE>
    {
        // Constructor
        public:
            inline NVector() : AbstractArray<NVector<T, TSIZE>, T, TSIZE>()
            {
                std::cout<<"NVector::NVector()"<<std::endl;
            }
     
        // Copy constructor
        public:
            template<class TCRTP0, class T0> inline NVector(const AbstractArray<TCRTP0, T0, TSIZE> &rhs) : AbstractArray<NVector<T, TSIZE>, T, TSIZE>(rhs)
            {
                std::cout<<"NVector::NVector(const AbstractArray<TCRTP0, T0, TSIZE> &rhs)"<<std::endl;
            }
     
        // Initializer list constructor
        public:
            template<class T0> inline NVector(const std::initializer_list<T0>& rhs) : AbstractArray<NVector<T, TSIZE>, T, TSIZE>(rhs)
            {
                std::cout<<"NVector::NVector(const std::initializer_list<T0>& rhs)"<<std::endl;
            }
     
        // Destructor
        public:
            inline ~NVector()
            {
                std::cout<<"NVector::~NVector()"<<std::endl;
            }
    };
     
    // Main
    int main()
    {
        NArray<double, 3> a1({1., 2., 3.});
        std::cout<<std::endl;
        NArray<int, 3> a2({4., 5., 6.});
        std::cout<<std::endl;
        NArray<double, 3> a3({7., 8., 9.});
        std::cout<<std::endl;
        NVector<double, 3> v1({11., 12., 13.});
        std::cout<<std::endl;
        NVector<double, 3> v2({14., 15., 16.});
        std::cout<<std::endl;
        NVector<double, 3> v3({17., 18., 19.});
        std::cout<<std::endl;
        NVector<int, 3> v4({20., 21., 22.});
        std::cout<<std::endl;
        a1 = a2;
        std::cout<<std::endl;
        std::cout<<"TEST -> a1 = "<<a1[0]<<" "<<a1[1]<<" "<<a1[2]<<std::endl;
        std::cout<<std::endl;
        v1 = a2;
        std::cout<<std::endl;
        std::cout<<"TEST -> v1 = "<<v1[0]<<" "<<v1[1]<<" "<<v1[2]<<std::endl;
        std::cout<<std::endl;
        v1 += a2;
        std::cout<<std::endl;
        std::cout<<"TEST -> v1 = "<<v1[0]<<" "<<v1[1]<<" "<<v1[2]<<std::endl;
        std::cout<<std::endl;
        v1 = a3+a3;
        std::cout<<std::endl;
        std::cout<<"TEST -> v1 = "<<v1[0]<<" "<<v1[1]<<" "<<v1[2]<<std::endl;
        std::cout<<std::endl;
        //v2 = v3+v4; // <- This line does not work : "error : no match for "operator+" in "v3+v4"
        std::cout<<std::endl;
        return 0;
    }
    Bref tout fonctionne à part quand j'appelle v2 = v3+v4 où v2 est un vecteur de double, v3 est un vecteur de double et v4 est un vecteur d'integer. (la somme de la ligne précédente v1 = a3 + a3, elle fonctionne, où tout est du même type).

    Auriez vous une idée pour corriger le problème ?

    D'autre part, si vous avez des idées pour améliorer l'efficacité ou la qualité du code (ou si vous constatez des trucs "dangereux" dans le code qui précède), avant que je me lance dans la conversion de l'implémentation actuelle à une implémentation basée sur cet exemple, vos conseils seront grandement appréciés (par contre, je ne vais pas me lancer dans de la "vraie" métaprogrammation car le loop unrolling du compilo connaissant la taille fixe des tableaux me suffit bien).

    Merci infiniment

  2. #2
    Expert éminent sénior

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

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Pour la qualité du code, il y a plusieurs choses à voir:

    "public:" est un marqueur qui se propage, il n'est pas attaché à une instruction particulière. Il est inutile de le répéter devant chaque fonction (ca sent le javaiste)

    inline n'est pas utile dans la définition d'une classe, mais requis partout ailleurs dans un en-tête.

    utilise quelques typedef, éventuellement privés, dans les classes
    par exemple parent_t et self_t
    exemple avec NArray:
    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
    // Array class
    template<class T, unsigned int TSIZE>
    class NArray : public  AbstractArray<NArray<T, TSIZE>, T, TSIZE> {
    	private:
    		typedef AbstractArray<NArray<T, TSIZE>, T, TSIZE> parent_t;
    	public:
    		NArray() : parent_t() {}
     
    		template<class TCRTP0, class T0>
    		NArray(const parent_t &rhs) : parent_t(rhs) {}
     
    		template<class T0>
    		NArray(const std::initializer_list<T0>& rhs) :AbstractArray<NArray<T, TSIZE>, T, TSIZE>(rhs) {}
     
    		// Destructor
    		~NArray() {}
    };
    On ne dois jamais utiliser d'identificateur commencant par un '_', ils sont réservés pour les compilateurs.
    Dans AbstractArray, j'utiliserai l'un des noms suivants:
    • data
    • m_data
    • mData
    • data_m
    avec une préférence pour data.

    Si tu veux une taille fixe, tu peux regarder du coté de <array>, avec std::array<typename, int>

    Il n'est pas toujours requis de rappeler les parametres templates d'une fonction membre.
    par exemple, ceci suffirait dans AbstractArray:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template<class TCRTP0, class T0>
    AbstractArray& operator+=(const AbstractArray<TCRTP0, T0, TSIZE>& rhs);
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  3. #3
    Rédacteur/Modérateur


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

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Bonjour,

    est-ce que v2 += v4 fonctionne ?
    Si oui, alors c'est juste une erreur d'innatention je pense :
    Ton opérateur += est déclaré comme ceci
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    template<class TCRTP0, class T0> inline AbstractArray<TCRTP, T, TSIZE>& operator+=(const AbstractArray<TCRTP0, T0, TSIZE>& rhs)
    Tandis que ton opérateur + est déclaré comme ceci
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    template<class T0> inline AbstractArray<TCRTP, typename std::common_type<T, T0>::type, TSIZE> operator+(const AbstractArray<TCRTP, T0, TSIZE>& rhs) const
    Ce qui t'interdit de sommer un Abstract<double> avec un Abstract<int>, tu ne peux sommer que Abstract<T> avec un autre Abstract<T>.

    En tous cas, les opérateurs + et += devraient avoir une signature quasi similaire, donc ça me surprend.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  4. #4
    Invité
    Invité(e)
    Par défaut
    On ne dois jamais utiliser d'identificateur commencant par un '_', ils sont réservés pour les compilateurs.
    pas exactement.
    _estOk
    tant qu'apres l'underscore est une minuscule et que c'est dans un namespace style la classe, c'est bon.

  5. #5
    Inactif  


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

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 620
    Points
    15 620
    Par défaut
    @galerien69
    En effet, c'est pas interdit. Par contre, on recommande souvent de ne pas utiliser de mot commençant par _ pour éviter les erreurs. Mais c'est plus une question de style et de sécurité

    @Kaluza
    C'est effectivement un problème avec l'absence de paramètre template TCRTP0 comme l'a dit Bousk

    Regarde en détail la signature de la fonction cherchée par le compilateur. Pour évaluer v4 + v3, le compilateur cherche (v4)::operator+
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    v4 -> 
        NV<double, 3> : AA< NV<double, 3>, double, 3>
    
    v4::operator+   -> 
        template<T0>
        NV<double, 3> : AA< NV<double, 3>, double, 3> :: 
        operator+ ( AA< TCRTP = NV<double, 3>, T0, TSIZE =3> )
    
    v3 -> 
        NV<int, 3> : AA< NV<int, 3>, int, 3>
    Il n'y a pas de type T0 pouvant correspondre pour v3 -> le compilateur ne trouve pas

    En ajouter TCRTP en paramètre template, ça fonctionne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template<class TCRTP0, class T0>
    inline AbstractArray<TCRTP0, typename std::common_type<T, T0>::type, TSIZE>
    operator+ (const AbstractArray<TCRTP0, T0, TSIZE>& rhs) const
    {
        return AbstractArray<TCRTP, typename std::common_type<T, T0>::type, TSIZE> (*this) += rhs;
    }

    Par contre, il peut y avoir très clairement un problème avec common_type, puisque que le type retourné peut ne pas correspondre à T dans le TCRP...

    En fait, en terme de design, je trouve pas ça top d'avoir le type manipulé en interne en double dans les paramètres template (dans TCRTP et dans T).
    Je te conseille de garder que le paramètre TCRTP et de récupérer T et SIZE comme membre de TCRTP :
    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
    template<class TCRTP> 
    class AbstractArray {
        (...)
    public:
        template<class TCRTP0>
        inline AbstractArray<
                typename std::common_type<
                    typename TCRTP::internal_type, 
                    typename TCRTP0::internal_type
                >::type
            >
        operator+ (const AbstractArray<TCRTP0>& rhs) const
        {
            typedef typename TCRTP::internal_type lhs_type;
            typedef typename TCRTP0::internal_type rhs_type;
            typedef typename std::common_type<lhs_type, rhs_type>::type internal_return_type;
            typedef AbstractArray<internal_return_type> return_type;
     
            return static_cast<return_type>((*this) += rhs); // moche ça
        }
    };
     
    template<class T, unsigned int TSIZE>
    class NVector : public  AbstractArray<NVector<T, TSIZE>>
    {
        typedef T internal_type;
        typedef SIZE internal_size;
    (...)
    };

    par contre, je ne vais pas me lancer dans de la "vraie" métaprogrammation car le loop unrolling du compilo connaissant la taille fixe des tableaux me suffit bien
    Cette phrase me fait tiquer. En général, on perd du temps à réimplémenter pour des besoins spécifique (souvent, pour des raisons de performances ou parce que le compilateur ne gère pour des libs numériques récentes). Là clairement tu ne cherches pas la performance si tu te contentes du déroulage des boucles et tu utilises un compilateur récent
    Pourquoi refais tu tout ça du coup ?

  6. #6
    Expert éminent sénior

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

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    J'ai une autre suggestion.

    Tu veux constituer une librairie de manipulation de vecteurs et matrices.

    Pour les vecteurs, pourquoi ne pas utiliser std::array<T, N>, et définir les operateurs mathématiques comme fonctions libres.

    De même pour les matrices, se seront des std::array<std::array<T, N>, M>.

    Je ne garantis pas que ce soit le résultat attendu, cependant.

    PS: Je vais personnellement essayer, parce que ce type de librairie me sera utile. Merci pour m'avoir donné ce projet
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  7. #7
    Membre habitué
    Homme Profil pro
    Doctorant en Astrophysique
    Inscrit en
    Mars 2009
    Messages
    312
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Astrophysique
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2009
    Messages : 312
    Points : 176
    Points
    176
    Par défaut
    Merci pour votre aide, tout cela est très instructif, j'avais effectivement oublié un typename.

    @ gbdivers
    En fait, en terme de design, je trouve pas ça top d'avoir le type manipulé en interne en double dans les paramètres template (dans TCRTP et dans T).
    Je te conseille de garder que le paramètre TCRTP et de récupérer T et SIZE comme membre de TCRTP :
    Oui effectivement ta méthode est meilleure. Je me demandais si pour passer les types, une classe de traits n'était pas adaptée.
    Et d'autre part par quoi tu remplacerais ce que tu as noté "moche ça" dans ton code ?

    Cette phrase me fait tiquer. En général, on perd du temps à réimplémenter pour des besoins spécifique (souvent, pour des raisons de performances ou parce que le compilateur ne gère pour des libs numériques récentes). Là clairement tu ne cherches pas la performance si tu te contentes du déroulage des boucles et tu utilises un compilateur récent
    Pourquoi refais tu tout ça du coup ?
    En fait si, je recherche la performance, mais disons pour un temps de codage "raisonnable". Et dans tous les benchs que j'ai pu faire, le fait est que dans la pratique, avec du CRTP et des tableaux C "fixes" tel qu'ici j'obtiens pratiquement les même perfs que si je faisais des opérations sur des variables qui n'étaient pas dans des tableaux. A titre de comparaison les std::valarray sont environ 8 fois plus lents. Je ne sais pas trop pour quoi, mais disons que cette méthode fonctionne très bien sans même passer par de l'expression template bourrin.

    Le but n'est pas du tout de faire quelque chose à la manière de BLAS, mais de pouvoir travailler efficacement avec de petits tableaux fixes : des vecteurs, des tenseurs ou des connexions affines. Comme je n'ai rien trouvé qui me satisfait pleinement, je recode tout from scratch, comme ça en plus je peux "tuner" à la main certains truc dont j'ai besoin. Au final c'est pour faire de la relativité générale.

  8. #8
    Expert éminent sénior

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

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Citation Envoyé par Kaluza Voir le message
    Le but n'est pas du tout de faire quelque chose à la manière de BLAS, mais de pouvoir travailler efficacement avec de petits tableaux fixes : des vecteurs, des tenseurs ou des connexions affines. (...) Au final c'est pour faire de la relativité générale.
    Quand tu auras terminé, et si tu la diffuse sous une licence libre, tu pourrais mettre ici un lien vers ta librairie? Elle m'intéresse beaucoup.

    PS: ma "solution" n'est pas exactement possible parce que operator= et operator+= doivent être des fonctions membres.
    Mais je pense qu'une classe héritant (publiquement?) de std::array peut fonctionner
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  9. #9
    Inactif  


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

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 620
    Points
    15 620
    Par défaut
    Citation Envoyé par Kaluza Voir le message
    Et d'autre part par quoi tu remplacerais ce que tu as noté "moche ça" dans ton code ?
    C'est moche parce que tu écris dans this (dans lhs donc) alors que l'opérateur + ne dois pas modifier lhs. En fait, quand tu écris
    Tu fais en fait :
    Et ça, c'est très moche (l'opérateur ne fait pas ce qu'on attend normalement de lui)

    Citation Envoyé par Kaluza Voir le message
    En fait si, je recherche la performance, mais disons pour un temps de codage "raisonnable". Et dans tous les benchs que j'ai pu faire, le fait est que dans la pratique, avec du CRTP et des tableaux C "fixes" tel qu'ici j'obtiens pratiquement les même perfs que si je faisais des opérations sur des variables qui n'étaient pas dans des tableaux. A titre de comparaison les std::valarray sont environ 8 fois plus lents. Je ne sais pas trop pour quoi, mais disons que cette méthode fonctionne très bien sans même passer par de l'expression template bourrin.

    Le but n'est pas du tout de faire quelque chose à la manière de BLAS, mais de pouvoir travailler efficacement avec de petits tableaux fixes : des vecteurs, des tenseurs ou des connexions affines. Comme je n'ai rien trouvé qui me satisfait pleinement, je recode tout from scratch, comme ça en plus je peux "tuner" à la main certains truc dont j'ai besoin. Au final c'est pour faire de la relativité générale
    Oui, je comprend le raisonnement (tout au moins, je l'entend souvent ; j'ai d'ailleurs déjà eu de tels raisonnements)
    Mais en terme de temps de dev, tu n'auras jamais mieux qu'utiliser une libs toute prête. Et en terme de performance, tu n'auras pas mieux non plus, sauf à beaucoup bosser dessus.
    Par exemple, pour NT2 (la lib numérique de JoelF), ils sont à plusieurs dessus (mais pas à plein temps) depuis plusieurs années
    Mais bon, c'est ton boulot, fais comme tu veux

  10. #10
    Membre habitué
    Homme Profil pro
    Doctorant en Astrophysique
    Inscrit en
    Mars 2009
    Messages
    312
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Astrophysique
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2009
    Messages : 312
    Points : 176
    Points
    176
    Par défaut
    Oui, je comprend le raisonnement (tout au moins, je l'entend souvent ; j'ai d'ailleurs déjà eu de tels raisonnements)
    Mais en terme de temps de dev, tu n'auras jamais mieux qu'utiliser une libs toute prête. Et en terme de performance, tu n'auras pas mieux non plus, sauf à beaucoup bosser dessus.
    Par exemple, pour NT2 (la lib numérique de JoelF), ils sont à plusieurs dessus (mais pas à plein temps) depuis plusieurs années
    Mais bon, c'est ton boulot, fais comme tu veux
    J'ai bien réfléchi avant de me lancer là dedans : quels étaient mes besoins, qu'est-ce qui existait, quel allait être le temps nécessaire pour coder ça et est-ce que ce sera finalement du temps bien utilisé. L'étape lourde est de coder 1 classe avec du CRTP une fois pour toute, et après je dérive tout de ça (et là je gagne un temps monstrueux). C'est toujours mieux d'utiliser des libs toutes prêtes, mais il ne faut pas non plus se borner à ça. Après on arrive à des situations extrêmes ou on a des codes high-end qui s'appuient sur des libraries qui datent d'il y a 30 ans, complètement outdated et pas maintenables du tout (on a quelques personnes qui font encore du fortran 77 au labo, parce que pourquoi s'embêter à apprendre un nouveau langage si ça marche bien et si on a des libs qui nous permettent de faire ce que l'on veut...) que tout le monde continue d'utiliser parce que ça ne sert à rien de réinventer la roue (même si ça revient à mettre une roue de carrosse sur une voiture de sport).

    Si il s'agissait de faire des millions d'opérations sur des matrices 100x100 ou plus j'aurai utilisé une librairie existante : il y en a plein qui font ce genre de choses de manière bien plus optimisée que je ne pourrai le faire. Dans mon cas, c'est plutôt des milliards d'opérations sur des matrices, vecteurs et tenseurs de dimension 3 ou 4 donc la problématique est un peu différente.

  11. #11
    Expert éminent sénior

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

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    J'ai refais une version similaire à la tienne, du moins, en fonctionnalités.
    mais j'ai supprimé le CRTP

    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
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    #include <array>
     
    #include <iostream>
    #include <initializer_list>
    #include <type_traits>
     
    template <typename T, int SIZE>
    class PVector : public std::array<T, SIZE>{
    	public:
    		//using std::array<T, SIZE>::array; //not yet available
    		typedef std::array<T, SIZE> parent_t;
     
    		PVector( const std::array<T, SIZE>& source ): parent_t(source) {}
     
    		template <typename U>
    		PVector(const std::array<U, SIZE>& source) {
    		unsigned int i = 0;
    			for(const U& u : source){
    				(*this)[i++] = u;
    				if(i==SIZE) break;
    			}
    		}
     
    		template <typename U>
    		PVector(const std::initializer_list<U>& source) {
    			unsigned int i = 0;
    			for(const U& u : source){
    				(*this)[i++] = u;
    				if(i==SIZE) break;
    			}
    		}
     
    		template <typename U>
    		PVector& operator=(const U& Other) {
    			PVector Temp(Other);
    			parent_t::swap(Temp);
    			return *this;
    		}
     
    		template <typename U>
    		PVector& operator+=(const std::array<U, SIZE>& source){
    			for (unsigned int i = 0; i < SIZE; ++i) (*this)[i] += source[i];
    			return *this;
    		}
     
    		template <typename U>
    		PVector& operator-=(const std::array<U, SIZE>& source){
    			for (unsigned int i = 0; i < SIZE; ++i) (*this)[i] -= source[i];
    			return *this;
    		}
     
    		template <typename U>
    		PVector& operator*=(const U& facteur){
    			for (T& t: *this) t *= facteur;
    			return *this;
    		}
     
    		template <typename U>
    		PVector& operator/=(const U& facteur){
    			for (T& t : *this) t /= facteur;
    			return *this;
    		}
    };//~Pvector
     
    template <typename T, int SIZE>
    std::ostream& operator<<(std::ostream& os, const PVector<T, SIZE>& v) {
    	bool fresh=true;
    	for (const T& t : v) os<<(fresh?(fresh=false,'['):' ')<<t;
    	return os<<']';
    }
     
    template <typename T1, typename T2, int SIZE>
    using res_t = PVector<typename std::common_type<T1, T2>::type, SIZE>;
     
    template <typename T1, typename T2, int SIZE>
    res_t<T1, T2, SIZE>
    operator+(const PVector<T1, SIZE>& a, const PVector<T2, SIZE>& b){
    	return res_t<T1, T2, SIZE>(a) += b;
    }
     
    template <typename T1, typename T2, int SIZE>
    res_t<T1, T2, SIZE>
    operator-(const PVector<T1, SIZE>& a, const PVector<T2, SIZE>& b){
    	return res_t<T1, T2, SIZE>(a) -= b;
    }
     
    // Vector class
    template<typename T, unsigned int TSIZE>
    class NVector : public  PVector<T, TSIZE> {
    	private:
    		typedef PVector<T, TSIZE> parent_t;
     
    	public:
    		NVector() : parent_t() {}
     
    		template<typename U>
    		NVector(const PVector<U, TSIZE> &rhs) : parent_t(rhs){}
     
    		template<class U>
    		NVector(const std::initializer_list<U>& rhs) : parent_t(rhs){}
     
    		~NVector(){}
    };
     
    // Vector class
    template<typename T, unsigned int TSIZE>
    class NArray : public  PVector<T, TSIZE> {
    	private:
    		typedef PVector<T, TSIZE> parent_t;
     
    	public:
    		NArray() : parent_t() {}
     
    		template<typename U>
    		NArray(const PVector<U, TSIZE> &rhs) : parent_t(rhs){}
     
    		template<class U>
    		NArray(const std::initializer_list<U>& rhs) : parent_t(rhs){}
     
    		~NArray(){}
    };
     
    using namespace std;
     
    // Main
    int main() {
    	PVector<double, 3> alep {1., 2., 3.};
    	cout << "alep   : "<< alep << endl;
    	cout << "alep*=2: "<< (alep *= 2) << endl;
    	cout << "alep/=2: "<< (alep /= 2) << endl;
     
    	NArray<double, 3> a1 {1., 2., 3.};
    	NArray<int, 3> a2({4., 5., 6.});
    	NArray<double, 3> a3({7., 8., 9.});
     
    	cout << "a1="<< a1 << endl;
    	cout << "a2="<< a2 << endl;
    	cout << "a3="<< a3 << endl;
     
    	a1 = a2;
    	cout << "a1=a2: "<< a1 << endl;
     
    	NVector<double, 3> v1({11., 12., 13.});
    	cout << "v1="<< v1 << endl;
     
    	NVector<double, 3> v2({14., 15., 16.});
    	cout << "v2="<< v2 << endl;
     
    	NVector<double, 3> v3({17., 18., 19.});
    	cout << "v3="<< v3 << endl;
     
    	NVector<int, 3> v4({20., 21., 22.});
    	cout << "v4="<< v4 << endl;
     
    	v1 = a2;
    	cout << "v1="<< v1 << endl;
     
    	v1 += a2;
    	cout << "v1 += a2  : "<< v1 << endl;
     
    	v1 = a3+a3;
    	cout << "v2 = a3+a4: "<< v1 << endl;
     
    	v2 = v3+v4;
    	cout << "v2 = v3+v4: "<< v1 << endl;
     
    	return 0;
    }
    Ceci compile avec gcc 4.7.0.

    J'espère que ca t'aidera.
    Ton code présentait quelques surprises, par exemple dans tes constructeurs, il y a des possibilités d'erreur sur les tailles.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  12. #12
    Membre habitué
    Homme Profil pro
    Doctorant en Astrophysique
    Inscrit en
    Mars 2009
    Messages
    312
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Astrophysique
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2009
    Messages : 312
    Points : 176
    Points
    176
    Par défaut
    Ton code présentait quelques surprises, par exemple dans tes constructeurs, il y a des possibilités d'erreur sur les tailles.
    J'arrive pas bien à voir d'où pouvait venir le pb. Tu aurais plus de détails ?

    Si j'arrive à faire ce que je veux, ce sera dans un premier temps sous licence CeCILL-B puis peut être à terme un passage à du GNU.

  13. #13
    Expert éminent sénior

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

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
        // Initializer list constructor
        public:
            template<class T0> inline AbstractArray(const std::initializer_list<T0>& rhs)
            {
                std::cout<<"AbstractArray::AbstractArray(const std::initializer_list<T0>& rhs)"<<std::endl;
                const T0* it = rhs.begin();
                for (unsigned int i = 0; i < TSIZE; ++i) {
                    _data[i] = *it;
                    ++it;
                }
            }
    Dans ce constructeur, si la liste est trop courte, tu recois une exception, au lieu d'initier à 0.
    Ce peut être voulu, mais ce n'est pas explicite.

    J'ai pris l'autre choix, en itérant la liste plutot que de balayer les cases.
    Et j'ai le sentiment (sans plus) que dans les opérations, nous avons un risque de conversion impossible.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  14. #14
    Membre habitué
    Homme Profil pro
    Doctorant en Astrophysique
    Inscrit en
    Mars 2009
    Messages
    312
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Astrophysique
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2009
    Messages : 312
    Points : 176
    Points
    176
    Par défaut
    Ok tu me rassures.
    C'est pris en compte dans ma classe originale. En fait j'avais d'abord commencer à coder ça sans CRTP dans un fichier faisant quelques milliers de lignes, mais ce n'était pas pratique pour l'architecture du reste de la lib. Du coup là je testais un tout petit exemple avec du CRTP et une fois que j'aurais bien compris le bon design à adopter, je convertirais ma version originale dans la version avec CRTP.

  15. #15
    Inactif  


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

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 620
    Points
    15 620
    Par défaut
    Citation Envoyé par Kaluza Voir le message
    J'ai bien réfléchi avant de me lancer là dedans : quels étaient mes besoins, qu'est-ce qui existait, quel allait être le temps nécessaire pour coder ça et est-ce que ce sera finalement du temps bien utilisé. L'étape lourde est de coder 1 classe avec du CRTP une fois pour toute, et après je dérive tout de ça (et là je gagne un temps monstrueux).
    C'est donc une réflexion purement théorique qui a orienté ton choix. Et tu gagnes un temps monstrueux par rapport à la version sans CRTP ou par rapport au libs ?

    Pour moi (avis personnel), c'est de l'optimisation prématurée : tu n'as pas mesurer les performances et détecter les problèmes avant de partir sur cette forme d'optimisation. La démarche n'est pas bonne (plus précisément, tu aurais fait ça dans mon labo, je t'aurais tapé sur les doigts)

    La "bonne" démarche (toujours avis personnel), c'est de partir sur une abstraction de la lib de calcul pour écrire le code métier, d'implémenter une première version de l'abstraction avec une lib existante, mesurer les perfs puis seulement de coder une nouvelle lib si nécessaire.

    Pour être plus concret. On imagine que tu partes d'une abstraction suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    template <class T> 
    struct blas_traits {
        typedef T::vector vector;
        typedef T::matrix matrix;
        typedef T::matrix_vector_prod matrix_vector_prod; // j'aime les noms simple :)
        ...
    };
    Ensuite, tu écris ton code métier en utilisant cette abstraction.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template <class T>
    blas_traits<T>::matrix foo() {
        blas_traits<T>::vector v1;
        blas_traits<T>::matrix m1;
        return blas_traits<T>::matrix_vector_prod(v1, m1);
    }
    Tu peux alors utiliser une lib existante pour implémenter ton abstraction, par exemple boost ublas :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include <boost/numeric/ublas/vector.hpp>
    #include <boost/numeric/ublas/matrix.hpp>
     
    class boost_ublas_tag;
     
    template <> 
    struct blas_traits<boost_ublas_tag> {
        using namespace boost::numeric::ublas;
     
        typedef vector<double>vector;
        typedef matrix<double> matrix;
        typedef matrix_vector_prod1<matrix, matrix, vector> matrix_vector_prod;
        ...
    };
    Là, ton code est fonctionnel, tu peux tester les performances. Ensuite seulement tu implémentes d'autres fonctions si nécessaire

    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
    #include <array>
     
    class mylib_tag;
     
    template <> 
    struct blas_traits<mylib_tag> {
        using std::array;
     
        template<class TCRTP, class T, unsigned int TSIZE> 
        class AbstractArray
        {};
     
        template<class T, unsigned int TSIZE> 
        class vector : public  AbstractArray<NArray<T, TSIZE>, T, TSIZE>
        {};
     
        template<class T, unsigned int TSIZE> 
        class matrix : public  AbstractArray<NVector<T, TSIZE>, T, TSIZE>
        {};
        ...
    };

  16. #16
    Membre habitué
    Homme Profil pro
    Doctorant en Astrophysique
    Inscrit en
    Mars 2009
    Messages
    312
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Astrophysique
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2009
    Messages : 312
    Points : 176
    Points
    176
    Par défaut
    C'est donc une réflexion purement théorique qui a orienté ton choix. Et tu gagnes un temps monstrueux par rapport à la version sans CRTP ou par rapport au libs ?

    Pour moi (avis personnel), c'est de l'optimisation prématurée : tu n'as pas mesurer les performances et détecter les problèmes avant de partir sur cette forme d'optimisation. La démarche n'est pas bonne (plus précisément, tu aurais fait ça dans mon labo, je t'aurais tapé sur les doigts)

    La "bonne" démarche (toujours avis personnel), c'est de partir sur une abstraction de la lib de calcul pour écrire le code métier, d'implémenter une première version de l'abstraction avec une lib existante, mesurer les perfs puis seulement de coder une nouvelle lib si nécessaire.
    Ca ne pars pas "de rien". On travaille avec un code Fortran datant des années 2000 pour simuler la structuration de l'Univers. Ayant pas mal modifier, étendu et optimiser ce code, je commence à le connaître, ses avantages et ses défauts. Récemment, on a fait tourner une simu sur 80000 cpus. Par contre on a touché vraiment les limites du code et l'implémentation actuelle ne va pas permettre d'aller beaucoup plus loin. On a déjà optimiser de tous les côtés, mais pour aller plus loin, c'est une grande refonte du code qui va être nécessaire. Un certain nombre de problèmes de design (surtout du côté de la gestion de la mémoire) ont été identifiés pour de tels runs. Pour le moment ça nous permet de faire de la science et c'est très bien comme ça.

    Toutefois, ayant bien identifier les problèmes actuels sur 80000 cpus, connaissant mes besoins en post-traitement pour le pétaoctet de résultats, et ayant en tête un certain nombre d'extensions pour le futur (et préférant le C++ au Fortran), j'ai passé un certain temps à réfléchir à l'organisation d'un code en C++ qui répondrait exactement à mes besoins. Mais pour cela j'ai besoin de 3 briques de bases : une petite libraire d'algèbre linéaire pour la relativité générale (ce dont je parle ici), un module de géométrie multiconnexe 4D assez générique et un module d'utilitaires pour la gestion de la parallélisation/gestion du temps/gestion de la mémoire. Une fois que j'aurai ces 3 briques de bases optimisées pour éviter certains écueils de l'implémentation Fortran, je pourrai m'en donner à coeur joie.

  17. #17
    Inactif  


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

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 620
    Points
    15 620
    Par défaut
    Te méprend pas, je ne dis pas que tu as tort de faire ça (peut être à la rigueur de le faire comme ça, mais j'ai fait aussi cette erreur quand j'ai abordé ce type de problématique)

    Ce que je veux dire, c'est qu'il n'y a pas de juste milieu. Soit il faut faire toutes les optimisations possibles (et donc accepter de devoir passer du temps à apprendre toutes les problématiques et solutions du calcul numérique, de l'implémentation de libs de calcul, d'optimisation divers sur la compilation, la mémoire, les accélérations matériels, etc, etc, etc), soit il vaut mieux utiliser les outils existants

  18. #18
    Membre habitué
    Homme Profil pro
    Doctorant en Astrophysique
    Inscrit en
    Mars 2009
    Messages
    312
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Astrophysique
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2009
    Messages : 312
    Points : 176
    Points
    176
    Par défaut
    Ah ok, j'avais peut être effectivement mal compris mais maintenant je vois exactement ce que tu veux dire . Je serais dans un labo de l'INRIA avec des gens qui sont à la pointe de la programmation ça se passerait surement autrement, mais autour de moi, la plupart des gens codent en Fortran, n'utilisent pas de pointeurs, ne savent pas ce qu'est un objet et encore moins un template. Comme moi, ils ont appris la programmation sur le tas comme un outil pour la physique, mais disons que j'ai pris gout aux techniques avancées (ce mot est très relatif hein) du C++ pour avoir du code générique et facile à maintenir sur le long terme. J'essaye donc de me débrouiller du mieux que je peux, sans avoir trop de gens au taquet sur ces techniques autour de moi (à part sur stackoverflow et ici).

    Pour en revenir à mon problème, je pense coder quelque chose qui ressemblerait à ça :
    NAbstract<T> : classe CRTP qui implémente toutes les opérations sur les éléments d'un tableau C
    NTraits<T> : classe de traits pour servir de passerelle entre les classes dérivées et NAbstract
    NArray<T, SIZE> : classe dérivée de NAbstract d'un tableau simple
    NVector<T, SIZE> : classe dérivée de NAbstract avec en plus quelques opérations vectorielles
    NMatrix<T, N, M> : classe dérivée de NAbstract avec les opérations matricielles de base
    NTensor<T, ORDER, DIM> classe dérivée de NAbstract pour les opérations sur les tenseurs
    NAffineConnection<T, ORDER, DIM> classe dérivée de NAbstract pour les connexions affines
    D'après les benchmarks que j'ai pu faire avec gcc et intel sur mon pc, sur notre serveur local et sur le supercalculateur qu'on utilise, NAbstract<T> en passant par des CRTP et des tableaux C a l'air d'être aussi rapide que quelque chose codé à la main avec des variables indépendantes. Ca me semble difficile de faire mieux...

    Si tu as des remarques ou des conseils, je suis toujours preneur

  19. #19
    Expert éminent sénior

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

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Citation Envoyé par Kaluza Voir le message
    Je serais dans un labo de l'INRIA avec des gens qui sont à la pointe de la programmation ça se passerait surement autrement…
    Ou pas, j'y ai fait plusieurs stages, et j'ai passé mon temps à raler sur les mêmes problèmes d'antiquité des habitudes.

    Parce que le "jeu" c'était de prototyper pour des gens qui ont réappris à coder quand l'objet leur est tombé dessus, et qu'ils gardent les habitudes du Pascal et autre...

    Cela dis, j'y ai beaucoup appris aussi. Et ton problème est intéressant.

    Cependant, NVector et NArray n'ont pas d'intérêt à être séparées, la première contient strictement l'autre en terme de fonctionnalité.

    Je pense faire une petite librairie, que je placerai sous LGPL.
    Quand j'aurai fais les tenseurs, je la publierai.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  20. #20
    Membre habitué
    Homme Profil pro
    Doctorant en Astrophysique
    Inscrit en
    Mars 2009
    Messages
    312
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Astrophysique
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2009
    Messages : 312
    Points : 176
    Points
    176
    Par défaut
    En fait j'ai du mal à comprendre comment ton code fonctionne gbdivers :
    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
    template<class TCRTP> 
    class AbstractArray {
        (...)
    public:
        template<class TCRTP0>
        inline AbstractArray<
                typename std::common_type<
                    typename TCRTP::internal_type, 
                    typename TCRTP0::internal_type
                >::type
            >
        operator+ (const AbstractArray<TCRTP0>& rhs) const
        {
            typedef typename TCRTP::internal_type lhs_type;
            typedef typename TCRTP0::internal_type rhs_type;
            typedef typename std::common_type<lhs_type, rhs_type>::type internal_return_type;
            typedef AbstractArray<internal_return_type> return_type;
     
            return static_cast<return_type>((*this) += rhs); // moche ça
        }
    };
     
    template<class T, unsigned int TSIZE>
    class NVector : public  AbstractArray<NVector<T, TSIZE>>
    {
        typedef T internal_type;
        typedef SIZE internal_size;
    (...)
    };
    Parce que ce qui est renvoyé, là c'est un AbstractArray<typename std::common_type< typename TCRTP::internal_type, typename TCRTP0::internal_type>::type> donc par exemple un AbstractArray<double>. C'est plutôt un AbstractArray<TCRTP<double, SIZE>> que je devrais renvoyer non ?

    Si il y a un vrai problème là, est-ce que quelqu'un aurait une solution ?

    EDIT : si il y a un vrai problème, j'ai l'impression que ma solution (par un heureux hasard parce que ce n'est pas volontaire) fonctionnerait mieux.

    Parce qu'imaginons la situation suivante ou NArray est le type dérivé et NAbstract est la class de base qui se charge du CRTP :
    NArray<double, 3> = NArray<int, 3>+NArray<double, 6>
    Ca va donner un
    NArray<double, 3> = NAbstract<NArray<int, 3>, double, 6>
    Et NArray<double, 3> aura finalement les bonnes données de type "double".
    Je suis d'accord que ce n'est pas hyper propre, mais ça marche non ? Si il y a une autre solution, je suis preneur.

Discussions similaires

  1. Problème de classe "template"
    Par Josiane22 dans le forum C++
    Réponses: 3
    Dernier message: 27/03/2013, 23h04
  2. problème avec classe template
    Par arthurembo dans le forum Langage
    Réponses: 2
    Dernier message: 09/02/2009, 07h46
  3. Problème déclaration classe template
    Par olivier1978 dans le forum Langage
    Réponses: 3
    Dernier message: 21/11/2007, 23h43
  4. Problème de class template et std::map
    Par bathof dans le forum Langage
    Réponses: 2
    Dernier message: 31/07/2007, 22h18
  5. [DLL/classe template] problème de link
    Par Bob.Killer dans le forum C++
    Réponses: 7
    Dernier message: 31/08/2005, 18h56

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