@Bousk: Le POD a de meilleures performances et est lui-même plus simple, mais au prix d'avoir une sémantique de valeur incomplète, puisqu'il peut être modifié comme une entité.
@Bousk: Le POD a de meilleures performances et est lui-même plus simple, mais au prix d'avoir une sémantique de valeur incomplète, puisqu'il peut être modifié comme une entité.
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.
Non, pas forcement vrai. Meme dans le cas de choses complexes, dans le monde du calcul, on procede tres rarement ainsi. Dans tous les codes de calcul que j'ai ecrit, je n'ai que du POD ou du POD encapsule dans un pointeur intelligent et que j'utilise apres comme du POD. Je partage a 100% l'avis de Joel car c'est l'approche usuelle dans le domaine HPC.
Dommage que Laurent Gomilla ne traine plus sur dvp, j'aurais été curieux d'avoir son avis sur la question. Sa lib est à cheval entre les deux: contraintes de perf importantes d'une part pour les calculs graphiques, et contraintes de modélisation (objet) d'autre part, car il s'agit d'une lib dont le but est d'être simple d'utilisation et robuste.
« L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
Spinoza — Éthique III, Proposition VII
Et la doc 2.0 est strictement identique pour ces points.
http://www.sfml-dev.org/documentatio..._1_1Sprite.php
http://www.sfml-dev.org/documentatio...1_1Vector2.php
L'interdiction de modification doit venir du fait qu'on l'utilise de manière const, et non camouflé derrière la présence de membres getter const uniquement.une sémantique de valeur incomplète, puisqu'il peut être modifié comme une entité
Si je veux créer une méthode externe qui modifie la position, je veux en avoir le pouvoir sans fioriture. Je passe ma position par référence et c'est réglé.
Là non seulement l'utilisateur peut mentir à son application, puisque passer sa position par référence non const, seules des méthodes const existent.
Mais en plus cette utilisation est doublement source d'erreur et de mauvaise habitude pour lui, puisqu'il a finalement la possibilité de modifier sa position par simple assignation d'une nouvelle position. Vous n'interdisez pas l'opérateur d'assignation n'est-ce pas ? Ni le constructeur par copie ? Sinon vous devez déclarer friend toute classe qui devra manipuler une Position ?
Pour empêcher ce 2° "problème", il va alors devoir enfin passer par des références constantes, et donc la présence de getter const n'apporte rien de plus qu'un accès direct aux membres, et la "protection : il ne peut pas modifier sa position" est déjà présente.
La plus-value initiale est donc caduque.
Ou alors vous manipulez uniquement des copies... bof bof.
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.
Mais dans ce cas, on ne peut plus l'affecter, le remplacer par une nouvelle valeur.
C'est justement le principe: L'affectation remplace la valeur par une nouvelle, donc c'est normal qu'elle soit autorisée.Si je veux créer une méthode externe qui modifie la position, je veux en avoir le pouvoir sans fioriture. Je passe ma position par référence et c'est réglé.
Là non seulement l'utilisateur peut mentir à son application, puisque passer sa position par référence non const, seules des méthodes const existent.
Mais en plus cette utilisation est doublement source d'erreur et de mauvaise habitude pour lui, puisqu'il a finalement la possibilité de modifier sa position par simple assignation d'une nouvelle position. Vous n'interdisez pas l'opérateur d'assignation n'est-ce pas ? Ni le constructeur par copie ? Sinon vous devez déclarer friend toute classe qui devra manipuler une Position ?
La plus-value est qu'on ne peut pas modifier la valeur comme une entité. On ne peut que la remplacer, l'écraser complètement par une autre. Pas de modification partielle.Pour empêcher ce 2° "problème", il va alors devoir enfin passer par des références constantes, et donc la présence de getter const n'apporte rien de plus qu'un accès direct aux membres, et la "protection : il ne peut pas modifier sa position" est déjà présente.
La plus-value initiale est donc caduque.
Ou alors vous manipulez uniquement des copies... bof bof.
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.
Devoir créer une nouvelle Position, puis l'affecter par l'opération = pour finalement remplacer la position initiale...
Quand tu te rends comptes que tu feras en général m_position = Position(new_x, m_position.getY()); c'est verbeux au possible et n'apporte... rien ? C'est bien ça, strictement rien.
Enfin si, ça apporte du temps d'exécution supplémentaire. Eventuellement inliné et réduit.
La lisibilité n'est est pas améliorée. La sémantique pas plus également.
m_position.x = new_x; est simple, rapide, on ne peut plus clair pour l'utilisateur.
Je suis pour le distingo des sémantiques de valeur et entité en général, mais dans ce cas présent ça n'apporte rien du tout.
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.
Mais HPC est un domaine assez particulier. J'imagine par exemple que vous n'utilisez jamais de smart pointers.
Enfin c'est pour dire qu'AMHA, toutes ces règles, idiomes et bonnes pratiques sont valables dans le cas général, donc pas toujours. Et finalement, elles deviennent caduques une fois qu'on les a comprise.
« L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
Spinoza — Éthique III, Proposition VII
Tu cites exactement la phrase ou je dis que j'utilise des pointeurs intelligents (en fait, je n'ai que ca)
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.
Dans un univers (réel) 3D je te l'accorde, ça serait plus pratique... comme je l'ai dit dans un de mes premiers posts, les mecs qui bossent sur HPC ou sur embeeded system, je les comprends, mais c'est de la déformation professionnelle de programmer sous contrainte hyper réduite quand tu peux te permettre le zèle de concevoir des trucs plus robustes, même pour un cout plus important en performance. Apres si on veut pousser le bouchon plus loin, on peut laisser tomber les moteurs 3D existants et puis engager tous les types de Future Crew pour faire un jeu 3D moderne...
Je ne dis pas qu'il faut pousser ce raisonnement dans l'autre extrême, ce n'est pas parce qu'on a plus de mémoire et que nos processeurs peuvent faire 1000 fois plus qu'avant qu'il faut se dire "c'est bon je m'en fout des histoire de performance", mais si ton environnement et tes contraintes t'autorisent plus que ce que tu ne t'autorise habituellement, autant en profiter pour prendre en compte des aspects de la programmation qui sont souvent déconsidérés, comme la maintenabilité, la réutilisabilité...
Quelqu'un a parlé de Laurent, et de ce qu'il penserait, et bien pour avoir déjà eu pas mal de conversation de ce style là (et oui je prône les services alors que j'ai toujours pensé donnée) avec lui, sans vouloir parler en son nom, il est plutôt pro-service ; c'est d'ailleurs la dernière conversation que j'ai eu avec lui à ce sujet qui a convertit mon esprit influençable à la notion de service (influençable... en même temps quand quelqu'un comme Laurent vous dit quelque chose vous avez juste envie de boire ses paroles ).
Edit : voici une des conversations. => (le sujet n'est pas tout à fait le même, mais on peut voir qu'il préfère la conception pensée en terme de service)
Nullius in verba
Excusez mon intrusion,
A travers mon regard très amateur, il me semble plus facile :
-D'avoir TOUT le temps une fonction pour accéder aux membres d'un objet.
-De ne pas avoir à penser à mettre const lors de chacune des instanciations d'une structure. (l'objet n'est lui, écrit qu'une fois).
Tout depend le type de programmation derriere. Dans mon cas, j'ai les deux. J'ai une appli qui est bourrine, vectorisee, mais en fin de compte, la structure est bien pensee pour l'evolution dans le domaine. S'il avait fallu passer par des accesseurs, je pense que j'aurai perdu 50 a 90% de la perf de l'application (en passant de debug a optimise, je gagne un facteur 10). Sachant que dans mon domaine, on fait la chasse aux 10% de gain !
L'autre est orientee objet, avec des tableaux qui passent d'un processus a un autre, d'une instance a une autre. Elle est bien plus souple (meme si la premiere version non industrielle ne l'etait pas), et la vectorisation a ete faite avec boost.simd. On aurait pu faire des accesseurs pour recuperer les donnees, et je me suis pose la question (ca aurait permis de calculer a la volee certaines proprietes plutot que de les charger), mais ca ne changerait rien au probleme. Oui, ca me permettrait potentiellement de cacher les details pour ajouter des thread-storage, changer l'ordre de stockage...
Martin Fowler dit qu'il ne faut pas faire de l'over-engineering. Ca en fait partie (surtout qu'on a les outils pour refactorer un projet en 3 clics de souris !)
Bonsoir,
Personnellement je rejoint assez l'avis d'Emmanuel et Joel : j'aurais tendance à commencer avec un POD (enfin des données membres publiques), et si le besoin s'en fait ressentir (si il est réel, il arrivera assez vite sur un élément aussi simple, AMA).
Pour ce qui est de la pensée service évoqué par Kaamui, pourquoi devrais-t-on associer service à fonction ? Personnellement que ce soit une fonction membre, libre prenant un objet du type considéré en paramètre, une donnée membre, ça reste un service à mes yeux.
Un point qu'à soulevé Koala01 et qui me touche le plus c'est les deux syntaxes différentes entre une fonction membre et une donnée membre, mais c'est plus lié au langage et je ne suis pas convaincu qu'introduire des indirections pour la corriger soit pertinent.
Le second point sur l'éventuelle besoin de changement d'implémentation me touche beaucoup moins dans ce cas. En général je suis d'accord avec ce point et c'est un des intérêts de l'encapsulation (séparer interface / implémentation). Avec une classe aussi simple, j'ai tendance à penser que l'interface et l'implémentation sont confondues, et que je n'ai donc pas de besoin de les séparer.
Dans le cas d'une classe Point, si c'est juste un point au sens d'un couple de réel indépendamment de tout hardware, c'est bien son propre d'offrir deux membres, ni plus ni moins. Si il y a un lien fort avec un besoin hardware comme l'exemple de Koala01 avec son couple dans un tableau C, je vois deux schéma :
- Soit le besoin est au coeur même de la conception et la solution la plus simple est peut-être de directement faire de ce type un typedef sur std::array<2,int> ...
- Soit le besoin n'est pas au coeur même de la conception, et dans ce cas j'ai tendance à penser que la solution la plus propre est de n'introduire aucune dépendance (ni dans un sens, ni dans l'autre) et de faire une "couche" d'adaptation pour coupler la conception même et le hardware.
Si par contre c'est un point au sens géométrique, propre à un espace, là j'aurais tendance à offrir un interface plus complexe qu'un simple couple pour offrir les services nécessaires à des changements de répères, des changements d'implémentaiton, etc. Implémentation qui s'appuiera surement sur une autre classe Point étant simplement un couple.
J'ai aussi vu l'argument d'avoir une sémantique de valeurs immuables, et cependant lorsque l'idée des classes matrices a été évoqué, ce point est passé à la trappe sous prétexte qu'il faut la voir comme un conteneur. Donc un élément de M(2,1) doit avoir une sémantique de valeur immuable et un élément de M(100,100) non ? Quel est la limite alors ? Et sinon un élément de M(1,1) (un int) la syntaxe n'est pas homogène, on mets ça dans une classe et avec une fonction membre const ?
Mah, le C++ s'est affranchi du restrict, sous une forme peu orthodoxe, je le reconnais
La supposition d'aliasing des données pointée a disparu avec C99 si je ne m'abuse, même si cette fonctionnalité n'a été implémentée que tardivement dans les compilateurs. La plupart des compilateurs C++ appliquent la même règle, et elle doit être dans C++11 (si elle n'est pas déjà dans C++03).
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 struct v1 { float x, y, z, w; } ALIGNED(8); struct v2 { float x, y, z, w; } ALIGNED(8); void function(v1 *vec1, v2 *vec2); // le compilateur considère qu'il n'y a pas d'aliasing entre vec1 et vec2 (même si (char*)vec1 == (char*)vec2).
Concrètement, ça permet au compilateur d'optimiser encore un peu plus le code, puisque les dépendances entre les variables sont cassées (en pratique, ça a surtout généré pour moi des bugs tordus, parce que le compilateur peut maintenant réordonner le code autour de certains cast ; cf. http://blog.worldofcoding.com/2010/0...-problems.html).
Je te l'accorde, c'est crade
[FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.
Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.
Le compilo C++ "assume no aliasing" par défaut désormais?
Ou comment créer des bugs...
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.
En fait, il "suffit", peu ou prou de s'intéresser à la définition (celle que l'on trouve dans n'importe quel dico ) du type que l'on veut créer.
La définition d'une matrice commence sans doute par "un ensemble de...".
A partir de là, peu importe le nombre d'éléments que va contenir ta matrice, elle aura les caractéristiques d'une collection, à savoir le fait d'être copiable (ou non, en fonction du type d'éléments qu'elle contient), d'être assignable, et de fournir un (ou plusieurs) moyen d'accéder à chacun de ses éléments, dusse-t-elle n'en contenir qu'un seul
Par contre, un point est un repère, et on pourrait sans doute en dire autant des couleurs, d'un montant, et de tout ce que tu peux assimiler à une valeur.
Que ton repère puisse "glisser" sur une seule, sur deux dimension(s) ou sur 10 ne changera rien au fait que, si tu fait glisser le curseur d'une seule des dimensions, tu obtiens un nouveau repère.
l'immuabilité des classes ayant sémantique de valeur ne vient, au final, chaque fois que de cette simple constatation: il n'y a, purement et simplement, pas lieu de fournir une méthode quelconque permettant de modifier les valeurs utilisée par ta classe.
Le résultat est donc que, effectivement, les seules fonctions qui aient un sens sont des fonctions constantes, mais, encore une fois, ce n'est pas parce que les seules fonctions exposées sont constantes qu'il faut décider de faire n'importe quoi du point de vue de la const correctness: tu as un objet qui est, par nature, immuable, mais tu veilles malgré tout à le transmettre sous la forme d'un objet constant quand il ne doit pas être modifié, autrement, bien qu'étant immuable, tu aurais l'occasion d'assigner à ta référence une autre valeur, et de faire potentiellement des dégâts
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
Je n'ai pas écrit ça sans réfléchir. Pensons en terme de services donc : celui rendu par la classe Position est de contenir les deux coordonnées qui forment une position dans l'espace (quel qu'il soit). Celui rendu par la fonction int Position::x() const est de fournir une coordonnée de cette position. Ça ne peut rien être de plus, sinon tu lui donnes trop de responsabilités. Partant de là, tu écartes donc toute idée de "tests" (suis-je au bord du monde, dois-je inverser y parce que je suis dans un miroir, etc.). Cette fonction ne feras jamais rien d'autre que de retourner la valeur de x. J'espère qu'on est d'accord jusque là.
La seule autre chose que tu peux donc modifier, c'est l'exemple donné par Koala : comment est stockée cette valeur et comment on y accède. Deux variables séparées, un tableau de deux éléments, un pointeur vers une mémoire gérée manuellement, un valarray, ... Et là tu peux effectivement faire des changements dans ta classe qui auront des répercussions dans le code appelant. Mais même alors, pourquoi modifier la classe si un tel besoin se fait sentir ? Revenons aux services : on a besoin d'obtenir la position sous forme d'un valarray. Est-ce à Position de le fournir ? Ou faut-il laisser une fonction libre faire la conversion ?
Je n'ai rien contre l'encapsulation. Mais si je me permets d'intervenir sur ce sujet, c'est que j'en ai déjà écrit plusieurs classes de ce type : des Vector3, des Point2, et j'en passe. Au début, suivant les conseils que j'avais pu lire sur ce forum et ailleurs, qui me disaient que l'encapsulation c'était bien, j'ai sagement caché mes données membres et utilisé des fonctions membres pour y accéder. À ce jour, ça ne m'a jamais servi à rien d'autre que de m'user les doigts. Je souhaite juste éviter ça à l'OP.
Je pense que, quand on en arrive à ce niveau là, il faut se poser la question : qu'est ce qu'on gagne en faisant ça, et qu'est ce qu'on perd. De mon point de vue, le gain est faible (voir nul, cf. plus haut, mais je ne veux pas débattre là dessus), et le coût plus élevé qu'il n'y paraît. Pour une raison discutable (eh oui, on en discute ici même), on se met des boulets aux pieds :
En remplaçant cette classe Position qui fait tant débat par une structure Vector2 dénuée de sémantique (comme dans la SFML), le code est plus simple :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 void Tidus::deplacer(const int dx, const int dy) { pos = Position(pos.x() + dx, pos.y() + dy); }
Ce n'est pas une question de performance ici, mais une question de rapidité d'écriture (moins de temps passé à écrire = plus de temps pour réfléchir), de concision (moins de caractères à taper = moins de chance de faire des erreurs) et de clarté (moins de caractères à lire et opérations explicites = code plus rapidement compris).
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 void Tidus::deplacer(const Vector2& dpos) { pos += dpos; }
Là encore, je n'ai rien contre le code verbeux (je n'utilise jamais de using namespace), mais seulement quand ça apporte quelque chose : que ça lève une ambiguïté pour le lecteur, ou que ça empêche de faire des bêtises. Ici je ne vois ni l'un ni l'autre.
Pour référence, les deux seules bibliothèques que je connais qui traitent ce sujet :
- Ogre::Vector2 (Ogre3D) : membres publics
- sf::Vector2 (SFML 2.0) : membres publics
@koala01: Je pense que tu as mal compris ce que je voulais dire (je parlais de matrice au sens mathématique du terme). Ce passage de mon message était là pour mettre en évidence qu'un point ou même qu'une simple valeur (*) c'est (**) une matrice, alors pourquoi traiter un point, un vecteur, un int, une matrice de manières différentes ?
(*) Je force le trait volontairement pour insister, mais l'idée est quand même là.
(**) C'est loin d'être parfaitement rigoureux, mais on va faire avec : au sein de cette discussion, ce sont bien tous des familles.
PS: Et la définition d'une matrice ne sera jamais "un ensemble de ...".
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager