petit exemple: fabriquer votre propre opérateur ternaire - C++ - Programmation
Marsh Posté le 25-07-2003 à 20:00:35
Tres interressant, j'utilise une technique similaire pour générer duc ode utilisant des opérations ternaires en AltiVec à partir de l'écriture a*b+c. Tres tres bon exposé
Marsh Posté le 25-07-2003 à 20:42:29
Je flagge...pas le temps de lire de suite et certainement au-dessus de mon niveau, mais j'aime apprendre!:jap:
Marsh Posté le 25-07-2003 à 20:56:50
pas facile a comprendre tout ca!
Est ce que tu pourrais expliquer ceci:
Code :
|
La sortie pour ceux qui ne peuvent pas executer :
Foo() |
Marsh Posté le 25-07-2003 à 21:06:22
j'avais dit pas de question à la con.... je vois pas ce que tu comprends pas: a) il y a pas de constructeur par defaut pour les classes *Script b) on construit l'objet qu'on retourne (normal quoi)
edit : « Press any key to continue »
ho le windozien
Marsh Posté le 25-07-2003 à 21:08:36
++Taz a écrit : |
ben ouais c'est pas sencé appeler Foo::AddScript(rhs, lhs)???
Or on y est dans addscript
Mais bon je connais plus le C que le c++
edit : j'ai deja assez de mal comme ca
Marsh Posté le 25-07-2003 à 21:09:41
polo021 a écrit : |
merci de ta participation, au revoir
Marsh Posté le 25-07-2003 à 21:10:32
ReplyMarsh Posté le 25-07-2003 à 21:12:46
polo021 a écrit : |
que je sache, en C comme en C++, le prototype d'une fonction, c'est
<type de retour> nomDeLaFonction([<argument>])
je crois que tu ne sais pas ça. franchement je m'attendais pas à ça comme question, on est vendredi mais quand meme
Marsh Posté le 25-07-2003 à 21:22:58
disons plutot : a quoi sert ceci alors
Code :
|
c bien ca que tu appeles quand tu fais Foo::AddScript(rhs, lhs), n'est cce pas
Marsh Posté le 25-07-2003 à 21:26:56
deux 2 choses l'une:
- tu n'a pas lu l'explication sur le role des classes Script
- « déconseillé aux débutants », pour moi c'est la première fois que tu vois une classe: on ne peut pas faire plus simple que les classes Script. si tu veux apprendre le C++, regarde les bibliolinks, si tu veux qu'on t'aide à créer ta première classe, on t'aidera, si tu veux un conseille de livre, je te donnerai des références. Mais là je peux rien pour toi. En tout cas pas dans ce topic.
Marsh Posté le 25-07-2003 à 21:42:49
polo021 a écrit : restons en là alors |
ben fais un autre topic / envoie moi un PM avec le code de AddScript et que te le commente ligne par ligne
Marsh Posté le 25-07-2003 à 23:18:17
interressant je me suis dérouler le bordel dans la tête (ça fait mal mais ça passe)
=> technique notée dans un coin
Marsh Posté le 25-07-2003 à 23:34:12
tiens moi j'ai pas la même trace que polo
Foo() |
polo à des
Foo(Foo)
~Foo()
je crée un nouvo topic pour en parler et j'essaye de me renseigner
Marsh Posté le 26-07-2003 à 10:37:47
polo021 a écrit : |
AddScript est une classe : on est dans opérator+, qui appelle le constructeur de la classe.
-> AddScript n'est pas une fonction : achète des lunettes
Marsh Posté le 26-07-2003 à 10:41:54
leneuf22 a écrit : |
il a fait un topic exprès
http://forum.hardware.fr/forum2.ph [...] h=&subcat=
Marsh Posté le 26-07-2003 à 10:45:11
leneuf22 a écrit : |
Comprends mieux maintenant
Marsh Posté le 26-07-2003 à 21:11:58
si ce genre de petite mémo plait, j'en referais peut être. Non?
Marsh Posté le 27-07-2003 à 01:39:05
Taz a écrit : si ce genre de petite mémo plait, j'en referais peut être. Non? |
C'est bieng ce genre de wrappers, mais il faut bien en mesurer la portée.
Par exemple avec ce genre d'opérations:
Code :
|
On n'économise qu'un objet temporaire Foo avec le wraper AddScript au lieu de la méthode classique (i.e friend Foo Foo::operator+(const Foo&, const Foo& )).
Marsh Posté le 27-07-2003 à 01:48:59
ben c'est déjà pas mal, et puis si tu te réfères au titre du topic, ce n'est pas l'objet du topic. mais je considere que c'est déjà un petit plus non négligeable. pas possible de faire du quaternaire avec que des + avec la meme technique par ce que c'est la meme précédence, c'est donc évalué comme a+b+c+d = ((a+b)+c)+d)
Marsh Posté le 27-07-2003 à 01:58:17
Taz a écrit : ben c'est déjà pas mal, et puis si tu te réfères au titre du topic, ce n'est pas l'objet du topic. mais je considere que c'est déjà un petit plus non négligeable. et puis la voix est ouverte pour faire un opérateur quaternaire (y a plus qu'a rajouté une classe AddAddscript tres simple et a surchargé + et c'est réglé) |
Mais oui je n'ai pas prétendu le contraire
Dommage qu'on ne puisse pas bidouiller les précédences et associativités des opérateurs selon leur type (comme en ocaml) ce qui permettrait des optimisations automatiques pour tous les opérateurs n-aires.
Marsh Posté le 27-07-2003 à 02:09:55
j'ai edité, mais je complète: la seule bidouille qu'on peut ajouter permet de réduire la durée de vie des objets temporaires, on peut alors obtenir au lieu de ça
a=a+b+c+d |
ceci
a=a+b+c+d |
c'est peut etre pas mal de changer la précédence/priorité, mais je sais pas si c'est vraiment source de confusion à ce moment là... et au niveaux des conversion, y a pas de problème?
Marsh Posté le 27-07-2003 à 02:10:56
cela dit ça doit etre difficile de définir une relation pyramidale pour la resolution d'une expression, ça va contre le principe d'évaluation de gauche à droite..., donc je saisis pas trop comment le changement de priorité change quelque chose: tu ne peux pas dire: l'addition de ces 2 types est prioritaire à l'addition de ces 2 types... si? et ça marche comment si ton opérateur n'est pas symétrique? je peux comprendre l'interet mais je suis pas sur que ça soit une bonne chose. et le C++ est ce qu'il est. on surcharge, on ne définit pas, donc ce n'est pas possible de changer la priorité. enfin, là n'est pas la question, on est pas vraiment là pour faire des comparaisons...
Marsh Posté le 27-07-2003 à 02:23:02
Taz a écrit : cela dit ça doit etre difficile de définir une relation pyramidale pour la resolution d'une expression, ça va contre le principe d'évaluation de gauche à droite..., donc je saisis pas trop comment le changement de priorité change quelque chose: tu ne peux pas dire: l'addition de ces 2 types est prioritaire à l'addition de ces 2 types... si? |
au temps pour moi, il s'agit plutot de privilégier l'opérateur de plus grande arité sur les autres. C'est faisable avec un parser LALR1.
Marsh Posté le 27-07-2003 à 02:27:01
SchnapsMann a écrit : |
en infixé, c'est faisable? tu fais une rapide explication/exemple ? j'ai du mal avec les opérateurs b-aires n>2 en infixé. je réfléchis, et c'est possible qu'en pas infixé il me semble
Marsh Posté le 27-07-2003 à 02:56:17
Taz a écrit : en infixé, c'est faisable? tu fais une rapide explication/exemple ? j'ai du mal avec les opérateurs b-aires n>2 en infixé |
rapide non
Il faut se placer dans le cadre d'un compilo LR1 en fé
Ce qui permet de changer les règles de décalage/réduction suivant le type de token en haut de la pile. NB: c'est tjrs de l'infixe mais c'est faisable... c'est justement une limitation des compilos LALR1 qui se contentent de "simples" tables de shift/reduce pour effectuer le parsing.
Ensuite l'idée c'est de raffiner la règle de réécriture d'un opérateur pour le type dont tu veux optimiser l'opérateur n-aire, en changeant son associativité à droite (comme pour le '=' classiquement).
Marsh Posté le 28-07-2003 à 13:37:26
Retour au topic.
Taz il me semble que ton exemple ne marche pas si bien que ca ...
En effet, les operateurs Foo() appellent une à deux fois le constructeur de Foo ... (pour sum et prod non ?)
DONC : il a bien construction d'objet temporaire ??
Ou alors les opérateurs de transtypage entre classes ont un comportement spécial qui m'est inconnu ?? (et donc éclaircissement stp)
De plus, si on veut ajouter une donnée dans Foo (style un double) il faut modifier lourdement le code :
Code :
|
La ca marche, la valeur de d est modifié corretement ET aucun objet temporaire n'est crée. Evidemment, il est nécessaire de surcharger + et * pour qu'ils gérent les cas :
Foo + xxScript
xxScript + Foo
xxScript + xxScript
Foo * xxScript
xxScript * Foo
xxScript * xxScript
Voila ... qu'en penses-tu ??
Marsh Posté le 28-07-2003 à 13:41:12
Joel F a écrit : Retour au topic. |
Sauf optimisation du compilo y a bien ndouble appel du constructeur de recopie.
Marsh Posté le 28-07-2003 à 13:49:52
je vois pas trop de quel cas tu parle? pour a+b, a*b et a*b+c, je ne vois d'objet temporaire?
Marsh Posté le 28-07-2003 à 13:51:44
Taz a écrit : je vois pas trop de quel cas tu parle? pour a+b, a*b et a*b+c, je ne vois d'objet temporaire? |
C dans les operateurs de transtypage vers foo, même s'ils servent pas pour les calculs, y a un petit pb dedans.
Marsh Posté le 28-07-2003 à 13:55:19
Moi je ne vois pas de problème. c'est réalisé de manière la plus atomique possible. je vois meme pas la difference avec ton code, si ce n'est qu'il se passe vraiment quelque chose dedans
Marsh Posté le 28-07-2003 à 14:01:25
Rien que là, il y a un truc qui me gêne un peu :
Code :
|
La création des objets temporaires n'apparaît pas dans les logs parce que j'ai l'impression qu'à aucun moment tu ne fais réellement l'opération arithmétique (pas d'appel au transtypage par l'opérateur Foo(), comme disait Leto2).
Ou alors j'ai rien compris, évidemment...
Marsh Posté le 28-07-2003 à 14:03:25
Taz a écrit : Moi je ne vois pas de problème. c'est réalisé de manière la plus atomique possible. je vois meme pas la difference avec ton code, si ce n'est qu'il se passe vraiment quelque chose dedans |
La différence c qu'il a virré les operateurs de transtypage... J'ai pas regardé en détail mais oui apparement c la même chose que ce que tu as fait
Marsh Posté le 28-07-2003 à 14:05:55
umag a écrit : Rien que là, il y a La création des objets temporaires n'apparaît pas dans les logs parce que j'ai l'impression qu'à aucun moment tu ne fais réellement l'opération arithmétique (pas d'appel au transtypage par l'opérateur Foo(), comme disait Leto2). |
Ben c fait exprès qu'on passe pas par le transtypage, justement pour pas créer d'objet intermédiaire.
Marsh Posté le 28-07-2003 à 14:09:07
en fait y a besoin des opérateurs de transtypage, à moins fournir des constructeurs, sinon voif F(Foo) ne peut fonctionner avec F(a+b)
après ça m'a parru plus élégant et passe partout. c'est vrai que pour a+b+c, on peut encore spécialiser avec une classe Add3Script. mais le pincipal avantage que je vois, c'est qu'avec les opérateurs de conversion, on peut tres bien envisager ce genre de technique avec n'importe quelle classe sans avoir besoin de la modifier.
Marsh Posté le 28-07-2003 à 14:13:40
LetoII a écrit : |
Ben oui, c'est le principe...
Mais il faut quand même faire l'opération quelque part, non ?
Donc l'opération, on la fait par exemple là pour le *+ avec un double :
Code :
|
Et dans ce cas il n'y a effectivement pas d'objet temporaire créé...
Mais il fallait le dire, c'est tout.
Marsh Posté le 25-07-2003 à 18:08:55
/!\ topic sérieux /!\
déconseillé aux débutants. Tout le monde peut lire ce topic et tester le code, mais j'aimerais que l'on reste focalisé sur la technique et pas sur le C++ lui même. Si il y a des points dans le code que vous ne comprenez pas, c'est sans doute une lacune. N'hésitez pas à créer des topics spécifiques (j'aimerais eviter des questions déplacées sur les constructeurs() : truc(chose), les operateur AutreType(), les déclarations imbriquées etc)
pour ceux que ça interesse... ce topic à pour but de présenter une technique avancée du C++. Je ne suis pas un véritable expert, donc je peux très bien raconter des bêtises et faire n'importe quoi.
Le C++ est langage où l'on manipule les objets par valeur et non par « référence » comme dans des langages comme Java ou Python.
Si on analyse l'expression
a = b * c
cela implique généralement et selon la sémantique habituelle, la création d'un objet temporaire:
tmp ( b * c )
a = tmp
dans beaucoup d'application ce genre de sémantique ainsi que la création de nombreux objets temporaires ne posent absolument aucun problème: le coup de la création/copie/destruction étant négligeable. Maintenant, vous avez une application où ces opérations ne sont pas du tout négligeables comme dans le cas de calcul scientifique.
Le but de ce topic est de montrer une technique permettant de réduire le nombre d'objets temporaires créés. Avant de commencer à vous l'expliquer, je signale qu'il existe une autre technique courremment utilisée qui est basée sur le concept de poignée (handle), c'est à dire que différents objets sont en mesures de partager des membres, et l'intégrité est assurée par un mécanisme de copie sur écriture (copy on write). Pour cela, on utilise souvent des pointeurs intelligents comme les boost::shared_ptr.
L'autre technique consiste à jongler sur les mécanismes de surcharge et de résolution de fonction. Je prends ici l'exemple des opérateurs arithmétique + et * mais celà fonctionne avec toutes les fonctions et tous les opérateurs, pour toutes les fonctions n-aires (mais celaest beaucoup plus long)
Simplement pour en revenir à l'expression
a = (b * c) + d
on va essayer transmettre à a des références aux objets b, c et d et éviter ainsi la création de 2 objets temporaires. Pour réaliser ce mécanisme, on va se servir d'objets porteurs qui vous nous servir à transporter les références jusqu'à a.
cette expression va alors se décomposer
a = porteur*(b, c) + d
a = porteur*+(b, c, d)
et tou ça n'implique la création d'aucun objet temporaire. a dispose de références à b, c et d et peut librement effectuer les opérations qu'il souhaite et l'affectation.
bon apres, je sais pas trop expliquer, j'ai essayer de faire un petit exemple, sans doute critiquable. (Notez bien au passage l'implémentation des opérateurs binaires comme fonction externes utilisant les fonctions membres autour de l'affectation, c'est là une tres bonne méthode d'implémentation)
Enjoy