IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C++ Discussion :

accéder aux valeurs d'une structure avec operateur []


Sujet :

C++

  1. #1
    Membre actif Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Points : 219
    Points
    219
    Par défaut accéder aux valeurs d'une structure avec operateur []
    Bonjour à tous,

    Dans le but de pratiquer un peu, je me propose de réaliser une bibliothèque permettant de réaliser des calculs vectoriels de base.
    Je suis donc parti sur des structures du genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    struct vec3 {
    	float x,y,z;
    };
    et un fonctionnement similaire pour les tenseurs.

    Accéder aux valeurs de la structure via les opérateurs '.' est une bonne chose. Cependant, de manière à faciliter un peu les claculs ultérieurs, je souhaiterais pouvoir accéder aux différentes variables (x, y, z) via l'opérateur [] où, vec[0] retourne x, vec[1] retourne y etc... Idem pour les tenseurs: mat[0][0] retourne xx, mat[0][1] retourne xy etc..
    L'objectif est donc, comme présenté précédemment, de simplifier les algorithmes, mais aussi de laisser la possibilité à l'utilisateur d’appeler les valeurs soit via vec.x soit via vec[0].

    Je me suis dit que ce type de fonctionnement devais être trivial dans le sens où vec.x est sensé être à l'adresse &vec, vec.y est sensé être à l'adresse &vec+sizeof(float) etc... Mais visiblement, je dois ajuster mon approche car *(&vec+sizeof(float)) est de type vec3 et non float.

    Sans nécessairement me souffler la solution, une idée de la direction à prendre pour que je puisse répondre à ma problématique ?

    Merci d'avance.





    EDIT :

    Comme à mon habitude, poster mon message ici m'a apporté la réponse : "pourquoi diable coller un sizeof au milieu de tout ça ???"

    Réponse :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    struct vec3 {
    	float x{.0f};
    	float y{.0f};
    	float z{.0f};
     
    	float & operator[](size_t const id) {
    		return *(&x+id);
    	}
    };
    ceci fonctionne.

  2. #2
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 565
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 565
    Points : 7 642
    Points
    7 642
    Par défaut
    Citation Envoyé par BioKore Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    struct vec3 {
    	float x{.0f};
    	float y{.0f};
    	float z{.0f};
     
    	float & operator[](size_t const id) {
    		return *(&x+id);
    	}
    };
    ceci fonctionne.
    Ceci peut sembler fonctionner. Mais rien n'impose au compilateur de placer les x, y et z dans cet ordre.

    La capacité d'avoir un accès par index est intéressante, le seul moyen sûr serait de créer le tableau et d'y accéder par des alias:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    struct vec3 {
    	float tab[3] = {.0f,.0f,.0f};
    	float& x = tab[0];
    	float& y = tab[1];
    	float& z = tab[2];
    };
    Sauf que là le compilateur même s'il optimise en ne créant rien pour x,y et z (car ce sont des alias qui accèdent directement aux éléments du tableau), la structure devient de fait non copiable car contient des références. Et en plus le compilateur peut garder les 3 variables.
    Ce que je ferais serait d'utiliser x, y et z sous le forme de fonctions.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    struct vec3 {
    	float tab[3] = {.0f,.0f,.0f};
    	float& x() { return tab[0]; }
    	float& y() { return tab[1]; }
    	float& z() { return tab[2]; }
    };

  3. #3
    Membre actif Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Points : 219
    Points
    219
    Par défaut
    Je me disais bien qu'il devais tout de même y avoir un kwak quelque part

    Par contre, j'ai cru comprendre que les éléments d'une structure sont contigus. Suivant ce raisonnement, pourquoi le compilateur viendrait mettre du désordre la-dedans ?

    Sinon la méthode proposée me plait bien (avec les alias). Pour pallier au problème de la "copie" une surcharge du constructeur par copie devrait pouvoir tout remettre dans l'ordre non ?


    Merci encore.

  4. #4
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 565
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 565
    Points : 7 642
    Points
    7 642
    Par défaut
    Citation Envoyé par BioKore Voir le message
    Par contre, j'ai cru comprendre que les éléments d'une structure sont contigus. Suivant ce raisonnement, pourquoi le compilateur viendrait mettre du désordre la-dedans ?
    Tu as raison, d'ailleurs aucun compilateur à ma connaissance ne s'amuse à changer l'ordre, d'autant que la construction doit se faire dans l'ordre de déclaration (et la destruction dans l'ordre inverse). Ta solution a donc un risque très minime. on pourrait ajouter un static_assert( &y == &x + 1 );static_assert( &z == &x + 2 ); pour se protéger de dérives. Cette méthode reste peut-être à préférer!

    Citation Envoyé par BioKore Voir le message
    Sinon la méthode proposée me plait bien (avec les alias). Pour pallier au problème de la "copie" une surcharge du constructeur par copie devrait pouvoir tout remettre dans l'ordre non ?
    Oui, il suffit certainement de les écrire. Mais je viens de faire un essai et l'optimiseur persiste à conserver 3 pointeurs dans la structure et en plus il s'en sert; ce qui crée une augmentation de taille et une indirection gênante.

    Malgré le risque de non portabilité, ta solution me semble donc meilleure.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Salut,
    Citation Envoyé par dalfab Voir le message
    la structure devient de fait non copiable car contient des références.
    Bah, ca, c'est encore assez facile à résoudre.

    Bien sur, il y a la possibilité de définir son propre constructeur (sans oublier son propre opérateur d'affectation), mais il y a peut-être même encore plus simple, car, le véritable problème vient du fait que, dans l'état actuel, tab n'est pas copiable

    Donc, si on s'arrange pour changer cet état de fait, par exemple, en utilisant une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    struct Vec3{
    public:
        float & operator[](size_t index){
            assert(index <3  && "index out of bound"); //essayer d'accéder à un indice trop élevé est une erreur de programmation
            return tab[index]; 
       }
    private:
        std::array<float, 3> tab;
    };
    Après, il est vrai que l'inclusion du fichier <array> sera invasive (comme l'inclusion de n'importe quel fichier d'en-tête dans un autre fichier d'en-tete), avec tout ce que cela comporte en termes d'augmentation du temps de compilation

    Mais, en termes de facilité, on ne trouvera rien de plus simple

    Comme souvent en C++, il faut être conscient que ce que l'on gagne d'un coté, on a de fortes chances de le perdre d'un autre. En l'occurrence, je m'abstiendrai donc bien de plaider pour une forme plutôt qu'une autre, d'autant plus qu'elles sont "assez facilement" interchangeables.

    Je me limite donc à proposer une alternative viable à un problème donné, en te laissant seul juge de celle que tu préfères
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  6. #6
    Membre actif Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Points : 219
    Points
    219
    Par défaut
    De souvenir, glm permet le même emploi que j'attends ; j'ai bien essayé de voir ce à quoi ça ressemblait dans le code mais... Je pense avoir encore besoin de beaucoup de pratique avant de pouvoir comprendre l'implémentation des vecteurs de cette bibliothèque (d'autant plus que, pour glm, les formats doivent être compatibles avec gls non ?).

    Dans un premier temps, je vais continuer avec "ma" méthode. Je ferais évoluer là chose selon le besoin.


    Merci pour ces retours !

  7. #7
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Plutôt qu'une manipulation de pointeurs qui n'est pas garantie marcher, je préférerais passer par un switch qui lui, le serait:
    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    struct vec3 {
    	float x{.0f};
    	float y{.0f};
    	float z{.0f};
     
    	float & operator[](size_t const id) {
    		switch(id) {
    			case 0: return x;
    			case 1: return y;
    			case 2: return z;
    			default: throw std::domain_error("Seules les valeurs 0, 1 et 2 sont acceptées.");
    		}
    	}
    }
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  8. #8
    Membre actif Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Points : 219
    Points
    219
    Par défaut
    Oui !

    Effectivement, en passant un peu de temps dans les codes de GLM, je retrouve ce type de fonctionnement.
    Quel est l'avantage de passer par ce système ? n'est-ce pas plus "gourmand" en terme de ressources (tout est relatif) ?

    Sinon, je profite de ce petit retour pour poser cette question par rapport au code de GLM, comment s'utilisent les templates du genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    namespace glm
    {
    	template<typename T, qualifier Q>
    	struct vec<4, T, Q>
    	{ 
    //...
    C'est la première fois que je vois une écriture comme struct<...>{};. Quelqu'un sait m'expliquer comment cela fonctionne ? Car je trouve ça plutôt pratique de pouvoir différencier les objets via le <...> plutôt que directement par le nom.
    J'ai essayé de créer une structure du même type pour des essais mais le compilateur me dit que ma structure n'est pas une class template...


    Merci !




    Ok, un simple template<size_t N, typename VT> struct vec; fait l'affaire Mais est-ce le meilleur moyen d'en arriver là ?
    A voir. En tout cas, j'arrive à faire fonctionner mes vecteurs.

    Merci encore !

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    En fait, c'est ce que l'on appelle une spécialisation (partielle ou totale, selon le cas) de template.

    Une classe template est prévue pour s'adapter automatiquement au différents types fournis par les paramètres template, par exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template <typename T, typename U>
    struct MyStruct{
        T m_t;  //m_t est de type T, quel qu'il soit
        V m_v; //m_v est de type V, quel qu'il soit
    };
    Du coup, ce qui nous amène à une situation proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    MyStruct<int, double> m1; /* m1.m_t est de type int
                               * m1.m_v est de type double
                               */
    Mais qui nous oblige, par exemple, à écrire un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    MyStruct<int, int> m2; /* m2.m_t est de type int
                            * m2.m_v est de type int
                            */
    si je souhaite que m_v et m_t soient du même type.

    Ce qui est dommage parce que le deuxième int devient redondant avec le premier, et du coup
    1. Ca prend plus longtemps à écrire que si je j'avais pu éviter d'écrire le deuxième
    2. ca laisse "une place à l'erreur" si l'utilisateur de la structure est distrait

    Je vais donc créer une spécialisation (partielle, dans le cas présent) qui aura pour effet de simplifier énormément l'écriture, sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    /* je décide que l'utilisateur peut ne donner qu'un seul type comme paramètre template */
    template <typename T>
    /* mais le compilateur s'attend à en avoir deux... on va donc les lui donner */
    struct MyStruct<T, T>{
       /* Et, du coup, les deux données sont de même type */
       T m_t;
       T m_u;
    };
    Grace à cela, l'utilisateur peut se contenter d'un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    MyStruct<int> m3; // m3.m_t et m3.m_v sont tous les deux de type int
    /* OU OU OU */
    MyStruct<double> m4; // m4.m_t et m4.m_v sont tous les deux de type double
    Bien sur, ca, c'est l'exemple le plus simple que l'on puisse trouver

    Dans les cas les plus complexes, cette possibilité nous permet de choisir de manière arbitraire les types (ou les valeurs, dans le cadre de l'exemple que tu montre) qui seront utilisé, et de définir un comportement bien spécifique à appliquer quand ces types (ou ces valeurs) sont rencontrées.

    D'ans l'exemple que tu montre, si tu remonte un peu dans le code, du devrais trouver une déclaration de la classe vec qui ressemble sans doute à quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template <size_t S, typename T, qualifier Q>
    struct vec/*{
    ...
    }*/;
    (avec un trait de politique nommé "qualifier").

    Cette déclaration (template <size_t S, typneame T, qualifier Q>/*...*/) indique explicitement qu'il faut utiliser trois paramètres template pour créer la classe vec, dont un (le premier) qui indique le nombre d'élément qui devront pouvoir être représentés.

    la spécialisation (partielle) que tu montres
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template <typneame T, qualifier Q>
    struct vec<4, T, Q>{
        /* ... */
    };
    fournit l'implémentation de la structure vec qui doit être spécifiquement utilisée lorsque l'on veut ... représenter 4 éléments, indépendamment de leur type et du qualificateur utilisé
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  10. #10
    Membre actif Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Points : 219
    Points
    219
    Par défaut
    Merci pour ces explications. Spécialisations partielles et complètes... J'avais déjà entendu parlé de ça sans vraiment connaître.

    Dans le cas de ma "reprise" de la bibliothèque, j'ai appliqué le même fonctionnement, mais sans la politique. En gros : template<size_t C, size_t R, typename MT> struct mat;Cet manière de procéder est plutôt assez pratique à l'utilisation ; bien que, dans mon cas, j'aurais très bien pu créer des types mat44<T> plutôt que mat<4,4,T>.

    Pour le moment j'en arrive à une bibliothèque plutôt sympa et relativement complète pour une utilisations simpliste, qui finalement ressemble beaucoup (sans trop de surprises) à glm.
    Après tout, le code comprends principalement des constructeurs et opérateurs. La partie "intelligente" ne fait que quelques lignes ^^.

    Cette bibliothèque me permet de réaliser des fonctions simples (rotations autour d'un axe quelconque et translations) via l'appel d'opérateurs simples eux aussi (dot product et vectoriel).

    Ce que je n'ai pas repris par contre, c'est le système de foncteurs. Je n'arrive pas à saisir pourquoi toutes (ou presque) les fonctions de GLM passent via des foncteurs sur des opérations aussi simples, sans états ni rien.
    Dans mon cas, j'ai utilisé des fonctions simples, normales. peut-être cela permet-il une meilleur gestion dans le cas de threading ?

  11. #11
    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 963
    Points
    32 963
    Billets dans le blog
    4
    Par défaut
    Citation Envoyé par BioKore Voir le message
    j'aurais très bien pu créer des types mat44<T> plutôt que mat<4,4,T>.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template<class T>
    using mat44 = mat<4,4,T>;
    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.

  12. #12
    Membre actif Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Points : 219
    Points
    219
    Par défaut
    Oui, c'est à peu près ce que j'ai fait.
    Ce que je voulais dire c'est que dans mon cas précis, passer par un template spécialisé n'est pas indispensable.

    Pour ma part, je considère que mes vecteurs et matrices, sauf si on le défini par un #define, sont de type float ou double :

    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
     
    #ifdef VEC_HIGH_P
    	using vec2 = vec<2, double>;
    	using vec3 = vec<3, double>;
    	using vec4 = vec<4, double>;
     
    	using mat2 = mat<2,2,double>;
    	using mat3 = mat<3,3,double>;
    	using mat4 = mat<4,4,double>;
     
    #else
    	using vec2 = vec<2, float>;
    	using vec3 = vec<3, float>;
    	using vec4 = vec<4, float>;
     
    	using mat2 = mat<2,2,float>;
    	using mat3 = mat<3,3,float>;
    	using mat4 = mat<4,4,float>;
     
    #endif
    effectuer des opérations sur des vecteurs d'entiers ou matrices d'entiers reste relativement rare je pense.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Citation Envoyé par BioKore Voir le message
    Ce que je n'ai pas repris par contre, c'est le système de foncteurs. Je n'arrive pas à saisir pourquoi toutes (ou presque) les fonctions de GLM passent via des foncteurs sur des opérations aussi simples, sans états ni rien.
    Héhé !! Rappelle toi les principes SOLID, et plus particulièrement S et I!!!

    Un vecteur (mathématique, s'entend) ou une matrice ne sont en définitive que ... des agrégats de données qui vont bien ensembles; auxquels sont associés tout un tas de comportements dont ... l'utilisateur n'a pas forcément besoin.

    Bien sur, on se doute bien qu'à partir du moment où tu te lance dans le calcul vectoriel ou matriciel, tu vas sans doute utiliser à peu près toutes les fonctions possibles et imaginables, mais la vrai responsabilité de tes vecteurs et de tes matrices reste particulièrement simple : permettre la représentation "d'un certain nombre" d'éléments d'un type donné afin de les maintenir bien ensembles. Ni plus, ni moins.

    Le S (SRP, pour mémoire) nous fait donc prendre conscience que de telles structures n'ont besoin d'aucun comportement particuliers en dehors de la construction d'une nouvelle matrice, surtout si les données en questions sont publiques.

    Quant au I (ISP, pour mémoire), il nous rappelle que nous avons toute latitude pour créer des fonctions libres vu que, même si chaque fonction doit s'adapter au type particulier de vecteur ou de matrice manipulé, il n'y en a pas une qui soit à proprement parler indispensable.

    L'un dans l'autre, l'approche suivie par GML est très proche des parties C et S d'un ECS : d'un coté, on a les données avec les notions de vecteurs et de matrices, et, de l'autres, on a les services qui permettent de les manipuler, avec les différentes fonctions
    Citation Envoyé par BioKore Voir le message
    Pour ma part, je considère que mes vecteurs et matrices, sauf si on le défini par un #define, sont de type float ou double :

    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
     
    #ifdef VEC_HIGH_P
    	using vec2 = vec<2, double>;
    	using vec3 = vec<3, double>;
    	using vec4 = vec<4, double>;
     
    	using mat2 = mat<2,2,double>;
    	using mat3 = mat<3,3,double>;
    	using mat4 = mat<4,4,double>;
     
    #else
    	using vec2 = vec<2, float>;
    	using vec3 = vec<3, float>;
    	using vec4 = vec<4, float>;
     
    	using mat2 = mat<2,2,float>;
    	using mat3 = mat<3,3,float>;
    	using mat4 = mat<4,4,float>;
     
    #endif
    Pourquoi faire si compliqué avec une directive préprocesseur

    A priori, seules les fonctions d'une classe (ou d'une structure) template qui sont explicitement appelées devraient obliger le compilateur à générer le code binaire exécutable correspondant, et le fait d'avoir un alias de type correspondant à une spécialisation totale (comme c'est le cas ici) d'une classe (ou d'une structure) template ne force absolument pas le compilateur à générer le code associé à ses fonctions membres.

    Pourquoi ne serais tu pas -- tout simplement -- encore plus précis dans tes alias de types en indiquant clairement le type dans ton alias de type, sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    	using vec2d = vec<2, double>;
    	using vec3d = vec<3, double>;
    	using vec4d = vec<4, double>;
     
    	using mat2d = mat<2,2,double>;
    	using mat3d = mat<3,3,double>;
    	using mat4d = mat<4,4,double>;
     
    	using vec2f = vec<2, float>;
    	using vec3f = vec<3, float>;
    	using vec4f = vec<4, float>;
     
    	using mat2f = mat<2,2,float>;
    	using mat3f = mat<3,3,float>;
    	using mat4f = mat<4,4,float>;
    Cela te simplifierait la vie (car cela fait une option en moins à prendre en compte lors de la configuration), sans vraiment compliquer celle de l'utilisateur, non
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  14. #14
    Membre actif Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Points : 219
    Points
    219
    Par défaut
    Bonjour,

    Merci pour ce retour (un peu tardif, désoler). Dans mon cas, je me suis contenté d'imiter le fonctionnement de GLM, tout en souhaitant développer le code moi-même. Ni plus ni moins.
    Je suis donc tout à fait d'accord sur le principe de donner des suffixes aux types résultants ce qui donne un peu de clarté quand au type sous-jacent.
    J'aime néanmoins à penser que, dans le cas de l'utilisation d'une bibliothèque mathématique (spécifique ou non), à partir du moment où on souhaite une précision donnée, il n'y a pas de raisons que cette dernière varie dans le code sauf lors de l'utilisation de quelques fonctions critiques / spécifiques demandant plus ou moins de précision selon les performances demandées.
    Enfin, la création des opérateurs étant relativement "lourde" dans le cas de telles bibliothèques, je n'ai pas souhaité traiter la possibilité de déclassement ou surclassement de type des objets (double --> float et float --> double). A partir du moment où l'on défini la précision souhaitée lors de la compilation, on empêche quasiment tout risques de réaliser un code du genre vec3d = vec3f.

    Mais qui peut le plus peut le moins, rien n'empêche de cumuler la solution que tu proposes et la mienne.

    Pour en revenir aux foncteur plutôt qu'aux fonctions, j'ai encore un peu de mal à comprendre:
    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 detail
    {
    	template<length_t L, typename T, qualifier Q, bool Aligned>
    	struct compute_distance
    	{
    		GLM_FUNC_QUALIFIER static T call(vec<L, T, Q> const& p0, vec<L, T, Q> const& p1)
    		{
    			return length(p1 - p0);
    		}
    	};
    }
     
    template<length_t L, typename T, qualifier Q>
    GLM_FUNC_QUALIFIER T distance(vec<L, T, Q> const& p0, vec<L, T, Q> const& p1)
    {
    	return detail::compute_distance<L, T, Q, detail::is_aligned<Q>::value>::call(p0, p1);
    }
    // est selon moi identique à :
     
    template<length_t L, typename T, qualifier Q>
    GLM_FUNC_QUALIFIER T distance(vec<L, T, Q> const& p0, vec<L, T, Q> const& p1)
    {
    	return length(p1 - p0);
    }
    // au détail près du paramètre template "bool Aligned" qui reste abstrait pour moi
    // et que je n'ai d'ailleurs pas dans mon code qui marche très bien sans ^^
    Dans mon cas, n'ayant que, en gros, les paramètres de template L et T (pour un vecteur), je ne vois pas encore pourquoi j'irais tout encapsuler dans une structure...

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Pour ce qui concerne les foncteurs :

    Il faut savoir que, avant C++11 (et l'arrivée de std::function), il était particulièrement difficile de transmettre une fonction comme paramètre, car nous n'avions le choix qu'entre deux possibilités:
    1. transmettre un foncteur ou
    2. transmettre un pointeur de fonction


    Il n'y a sans doute pas besoin de longs discours pour t'expliquer pourquoi les gens préféraient éviter le recours aux pointeurs de fonction à chaque fois que c'était possible (mais, si tu y tiens ... Suffit de demander )

    D'un autre coté, dés que l'on parle d'un type de donnée (d'une structure), les choses sont tellement simples :
    • Tu as exactement la même liberté en termes de prototype de l'opérateur () qu'avec n'importe quel autre fonction
    • l'opérateur () peut être static (indépendant de la structure) ou non
    • la structure peut contenir, au besoin, une référence vers l'objet qui sera manipulée, ce qui lève la restriction quant au fait ... de ne faire qu'appel à des fonctions statiques de tes classes ou de tes structures
    • tu peux le transmettre par valeur ou par référence (éventuellement constante)
    • tu peux définir une valeur par défaut pour le paramètre
    • et surtout: on peu le faire intervenir dans une expression template (*)

    (*) exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template <typename Iter, typename Fun>
    void sort(Iter begin, Iter end, Fun functor){
     
    }
    Et, sauf erreur de ma part, cela reste malgré tout plus efficace -- en termes d'utilisation -- que std::function.

    Depuis, on a les expressions lambda et la possibilité de définir un type automatiquement (ex auto T = [](/* ... */){/* ... */;}; Mais, comme cela n'existe que depuis ... C++11 on peut comprendre le souhait de la part des développeurs de GLM de supporter les normes plus anciennes, malgré tout
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  16. #16
    Membre actif Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Points : 219
    Points
    219
    Par défaut
    Très bien vu effectivement. Ceci explique donc tout.

    Il est en effet bien plus simple de transmettre un foncteur en paramètre qu'une fonction ou un pointeur de fonction. CQFD.
    Je dois tester ça, mais il me semble aussi que cela permet plus facilement (à mon gout) de transmettre des fonctions template<typename ...Args>.

    Bref, un très bon "remplaçant" de std::function(), sans compter la retro-portabilité du code.

    Merci pour ces explications.

    Quand au fait que l'on préfère éviter l'utilisation de pointeurs de fonctions, j'imagine qu'une partie de la réponse tiens compte de new et delete non ?

    Je vais de ce pas m'exercer un peu à ce type de fonctionnement. Les std::function marchent bien mais je pense qu'il est bon de savoir utiliser des méthodes alternatives.

    Merci;

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Citation Envoyé par BioKore Voir le message
    Quand au fait que l'on préfère éviter l'utilisation de pointeurs de fonctions, j'imagine qu'une partie de la réponse tiens compte de new et delete non ?
    Non, pas vraiment, parce que, en l'occurrence un pointeur de fonction est bel et bien ce qu'il dit être : une variable numérique entière (généralement non signée) représentant l'adresse mémoire à laquelle se trouve la fonction

    C'est d'avantage lié
    1. à la manière dont on déclare un pointeur de fonction (exemple le plus simple using MathOp = int (* ) (int, int);
    2. aux restrictions imposées sur les fonctions que l'on peut utiliser
    3. aux restrictions imposée en termes de "lookup" (nom pleinement qualifié, quand ce ne sont pas des fonctions libres)

    Mais c'était bien tenté
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

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

Discussions similaires

  1. Accéder aux variables d'une structure
    Par Sansideevraiment dans le forum MATLAB
    Réponses: 1
    Dernier message: 06/10/2016, 16h43
  2. [Débutant] Accéder aux fields d'une structure
    Par MattM dans le forum MATLAB
    Réponses: 2
    Dernier message: 07/10/2015, 17h48
  3. [WB17] Accèder aux données d'une base SQL Server avec Webdev
    Par Biyoum dans le forum WebDev
    Réponses: 8
    Dernier message: 07/10/2014, 11h30
  4. Accéder aux valeurs des éléments d'une matrice
    Par alvinleetya dans le forum OpenCV
    Réponses: 5
    Dernier message: 01/10/2012, 16h19
  5. Réponses: 2
    Dernier message: 09/11/2007, 15h32

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