Discussion:
Surcharge avec paramètres de classe.
(trop ancien pour répondre)
Blady
2018-11-22 20:59:09 UTC
Permalink
Bonjour,

Voici un exemple inspiré de celui donné au chapitre 14.5 du livre
"Programming in Ada 2012":

1. procedure Programming_Ada_2012 is
2.
3. type O1 is tagged record
4. X : Float := 0.0;
5. Y : Float := 0.0;
6. end record;
7.
8. procedure Swap (X, Y : in out O1) is
9. T : O1 := X;
10. begin
11. X := Y;
12. Y := T;
13. end Swap;
14.
15. procedure Swap (X, Y : in out O1'Class) is
16. T : O1'Class := X;
17. begin
18. X := Y;
19. Y := T;
20. end Swap;
21.
22. type C1 is new O1 with record
23. R : Float;
24. end record;
25.
26. -- procedure Swap (X, Y : in out C1) is
27. -- T : O1 := O1 (X);
28. -- begin
29. -- O1 (X) := O1 (Y);
30. -- O1 (Y) := T;
31. -- end Swap;
32.
33. S, R : O1;
34. U, V : C1;
35.
36. begin
37. Swap (S, R);
38. Swap (U, V);
39. end Programming_Ada_2012;

Ma question est que comme la primitive Swap (l8) du type O1 est
surchargée par la procédure de classe Swap (l15), que devient la
primitive héritée Swap du type C1 ?

Elle devrait exister telle que celle mise en commentaires (l26).
Pourtant, GNAT donne une erreur pour l'appel Swap (l37) avec des
instances du type O1 mais pas pour l'appel Swap (l38) avec des instances
du type C1 (hérité de O1) :
programming_ada_2012.adb:37:04: ambiguous expression (cannot resolve "Swap")

Comme expliqué par John Barnes, la primitive héritée pour le type C1 n'a
que peu d'utilité car elle ne va échanger que les champs X et Y et non
pas R, mais est-ce qu'elle n'existe alors pas pour le langage ?

Merci pour vos idées, Pascal.
J-P. Rosen
2018-11-23 06:24:48 UTC
Permalink
Post by Blady
Bonjour,
Voici un exemple inspiré de celui donné au chapitre 14.5 du livre
 1. procedure Programming_Ada_2012 is
 2.
 3.    type O1 is tagged record
 4.       X : Float := 0.0;
 5.       Y : Float := 0.0;
 6.    end record;
 7.
 8.    procedure Swap (X, Y : in out O1) is
 9.       T : O1 := X;
10.    begin
11.       X := Y;
12.       Y := T;
13.    end Swap;
14.
15.    procedure Swap (X, Y : in out O1'Class) is
16.       T : O1'Class := X;
17.    begin
18.       X := Y;
19.       Y := T;
20.    end Swap;
21.
22.    type C1 is new O1 with record
23.       R : Float;
24.    end record;
25.
26. --     procedure Swap (X, Y : in out C1) is
27. --        T : O1 := O1 (X);
28. --     begin
29. --        O1 (X) := O1 (Y);
30. --        O1 (Y) := T;
31. --     end Swap;
32.
33.    S, R : O1;
34.    U, V : C1;
35.
36. begin
37.    Swap (S, R);
38.    Swap (U, V);
39. end Programming_Ada_2012;
Ma question est que comme la primitive Swap (l8) du type O1 est
surchargée par la procédure de classe Swap (l15), que devient la
primitive héritée Swap du type C1 ?
Elle existe toujours. O1 et O1'Class sont deux types différents, donc il
y a simplement surcharge.
Post by Blady
Elle devrait exister telle que celle mise en commentaires (l26).
Pourtant, GNAT donne une erreur pour l'appel Swap (l37) avec des
instances du type O1 mais pas pour l'appel Swap (l38) avec des instances
programming_ada_2012.adb:37:04: ambiguous expression (cannot resolve "Swap")
Gnat dit bien qu'il y a /ambiguité/, c'est à dire que les deux existent
et qu'il n'est pas capable de savoir laquelle des deux choisir. En
effet, O1 est compatible avec O1 aussi bien que O1'Class
Post by Blady
Comme expliqué par John Barnes, la primitive héritée pour le type C1 n'a
que peu d'utilité car elle ne va échanger que les champs X et Y et non
pas R, mais est-ce qu'elle n'existe alors pas pour le langage ?
Est-ce que tu as bien copié l'exemple? Ici, le type C1 est déclaré dans
une /procédure/, donc la procédure Swap de O1 n'est pas primitive et
n'est pas héritée par C1. Donc, C1 n'a qu'une seule procédure Swap
applicable (celle sur O1'Class).

Rappel: une opération est primitive si elle est déclarée dans la même
/spécification de paquetage/ que le type...
--
J-P. Rosen
Adalog
2 rue du Docteur Lombard, 92441 Issy-les-Moulineaux CEDEX
Tel: +33 1 45 29 21 52, Fax: +33 1 45 29 25 00
http://www.adalog.fr
Blady
2018-11-24 12:35:37 UTC
Permalink
Post by J-P. Rosen
Post by Blady
Bonjour,
Rappel: une opération est primitive si elle est déclarée dans la même
/spécification de paquetage/ que le type...
Bin voilà, j'ai oublié cette règle, merci Jean-Pierre de l'avoir
rappelée car elle ne m'est pas intuitive.
En mettant les déclarations dans 2 paquetages extérieurs à la procédure
j'obtiens bien une ambiguïté sur les 2 appels:

1. with PA2012_02_PO1;
2. with PA2012_02_PC1;
3. procedure PA2012_02 is
4. use PA2012_02_PO1;
5. use PA2012_02_PC1;
6.
7. S, R : O1;
8. U, V : C1;
9.
10. begin
11. Swap (S, R);
12. Swap (U, V);
13. end PA2012_02;

pa2012_02.adb:11:04: ambiguous expression (cannot resolve "Swap")
pa2012_02.adb:11:04: possible interpretation at pa2012_02_po1.ads:7
pa2012_02.adb:11:04: possible interpretation at pa2012_02_po1.ads:6
pa2012_02.adb:12:04: ambiguous expression (cannot resolve "Swap")
pa2012_02.adb:12:04: possible interpretation (inherited) at
pa2012_02_pc1.ads:3
pa2012_02.adb:12:04: possible interpretation at pa2012_02_po1.ads:7

Merci, Pascal.
Blady
2018-11-25 19:27:13 UTC
Permalink
Post by Blady
Post by J-P. Rosen
Post by Blady
Bonjour,
Rappel: une opération est primitive si elle est déclarée dans la même
/spécification de paquetage/ que le type...
Bin voilà, j'ai oublié cette règle, merci Jean-Pierre de l'avoir
rappelée car elle ne m'est pas intuitive.
En mettant les déclarations dans 2 paquetages extérieurs à la procédure
 1. with PA2012_02_PO1;
 2. with PA2012_02_PC1;
 3. procedure PA2012_02 is
 4.    use PA2012_02_PO1;
 5.    use PA2012_02_PC1;
 6.
 7.    S, R : O1;
 8.    U, V : C1;
 9.
10. begin
11.    Swap (S, R);
12.    Swap (U, V);
13. end PA2012_02;
pa2012_02.adb:11:04: ambiguous expression (cannot resolve "Swap")
pa2012_02.adb:11:04: possible interpretation at pa2012_02_po1.ads:7
pa2012_02.adb:11:04: possible interpretation at pa2012_02_po1.ads:6
pa2012_02.adb:12:04: ambiguous expression (cannot resolve "Swap")
pa2012_02.adb:12:04: possible interpretation (inherited) at
pa2012_02_pc1.ads:3
pa2012_02.adb:12:04: possible interpretation at pa2012_02_po1.ads:7
Merci, Pascal.
Le paquetage C1 proposé ressemble à ceci:
with PA2012_02_PO1;
package PA2012_02_PC1 is
type C1 is new PA2012_02_PO1.O1 with record
R : Float;
end record;
function Radius (C : C1) return Float;
end PA2012_02_PC1;

Il me vient une autre question: la fonction Radius est censée retourner
la valeur R d'une instance de C1, ne faudrait-il pas mieux mettre un
paramètre de classe ?
function Radius (C : C1'Class) return Float;

Sinon quel serait ici l'intérêt de pouvoir la dériver vu que l'utilité
est seulement de retourner la valeur de R ?

Merci Pascal.
J-P. Rosen
2018-11-25 21:25:48 UTC
Permalink
Post by Blady
with PA2012_02_PO1;
package PA2012_02_PC1 is
   type C1 is new PA2012_02_PO1.O1 with record
      R : Float;
   end record;
   function Radius (C : C1) return Float;
end PA2012_02_PC1;
Il me vient une autre question: la fonction Radius est censée retourner
la valeur R d'une instance de C1, ne faudrait-il pas mieux mettre un
paramètre de classe ?
   function Radius (C : C1'Class) return Float;
Sinon quel serait ici l'intérêt de pouvoir la dériver vu que l'utilité
est seulement de retourner la valeur de R ?
C'est un exemple d'école? Vu que R est visible, la fonction Radius ne
sert à rien de toute façon. Ceci dit la question philosophique est:
est-ce une méthode (où chaque classe dérivée peut redéfinir sa façon de
faire) ou une méthode de classe (où l'on veut garantir que le résultat
est garanti identique pour toutes les classes dérivées)?
--
J-P. Rosen
Adalog
2 rue du Docteur Lombard, 92441 Issy-les-Moulineaux CEDEX
Tel: +33 1 45 29 21 52, Fax: +33 1 45 29 25 00
http://www.adalog.fr
Blady
2018-11-27 06:56:48 UTC
Permalink
Post by J-P. Rosen
Post by Blady
with PA2012_02_PO1;
package PA2012_02_PC1 is
   type C1 is new PA2012_02_PO1.O1 with record
      R : Float;
   end record;
   function Radius (C : C1) return Float;
end PA2012_02_PC1;
Il me vient une autre question: la fonction Radius est censée retourner
la valeur R d'une instance de C1, ne faudrait-il pas mieux mettre un
paramètre de classe ?
   function Radius (C : C1'Class) return Float;
Sinon quel serait ici l'intérêt de pouvoir la dériver vu que l'utilité
est seulement de retourner la valeur de R ?
C'est un exemple d'école? Vu que R est visible, la fonction Radius ne
est-ce une méthode (où chaque classe dérivée peut redéfinir sa façon de
faire) ou une méthode de classe (où l'on veut garantir que le résultat
est garanti identique pour toutes les classes dérivées)?
Oui, il s'agit d'un exemple issu du livre "Programming in Ada 2012" page
336.
John Barnes a choisi une méthode plutôt qu'une méthode de classe pour
cet exemple. Mais j'aurais pour ma part plutôt répondu à la question
posée ci-dessus avec une méthode de classe.
Est-ce correct ?

Cela montrerais ainsi qu'une méthode de classe n'est pas réservée au
types abstraits ou au "dispatching" (je n'arrive pas à trouver une
traduction satisfaisante).

Merci Pascal.
J-P. Rosen
2018-11-27 09:31:04 UTC
Permalink
Post by Blady
John Barnes a choisi une méthode plutôt qu'une méthode de classe pour
cet exemple. Mais j'aurais pour ma part plutôt répondu à la question
posée ci-dessus avec une méthode de classe.
Est-ce correct ?
Je n'aurais pas dû employer le terme "méthode de classe", puisque
précisément une opération à l'échelle de classe n'est pas une méthode...

Mon critère de choix (perso):
- Une méthode est appropriée quand c'est une opération que toute classe
dérivée doit avoir, mais dont la réalisation (la "méthode") dépend de
l'objet: tout objet graphique doit être dessiné, mais la façon dont on
dessine un cercle n'est pas la même que la façon dont on dessine un
triangle.

- Une opération à l'échelle de classe est appropriée lorsque le service
est identique pour toute la classe (et qu'on veut être sûr que personne
n'invente une autre façon de faire). Pour déplacer un objet graphique,
on l'efface, on change ses coordonnées, et on le redessine. Cet
algorithme est indépendant du type spécifique de l'objet. En général,
une opération à l'échelle de classe utilise les méthodes de l'objet
(d'où le dispatching) mais ce n'est pas fondamental.

- Il y a aussi la possibilité du redispatching: une méthode qui
convertit l'objet vers le type à l'échelle de classe pour forcer le
dispatching. Pour moi, c'est une anomalie, car ça n'entre pas dans le
beau schéma ci-dessus - ça provient des autres langages OO qui n'ont pas
la notion d'opération à l'échelle de classe et qui sont donc bien
obligés de les simuler avec des méthodes, éventuellement déclarées
"final" comme en Java.
Post by Blady
Cela montrerais ainsi qu'une méthode de classe n'est pas réservée au
types abstraits ou au "dispatching" (je n'arrive pas à trouver une
traduction satisfaisante).
En ce qui concerne ton exemple... On ne peut pas dire grand-chose, cela
dépend de ce que représente l'objet et de ce que veut faire la méthode.
C'est souvent le problème dans les exemples trop simplifiés, on n'a pas
de contexte pour évaluer la bonne solution.
--
J-P. Rosen
Adalog
2 rue du Docteur Lombard, 92441 Issy-les-Moulineaux CEDEX
Tel: +33 1 45 29 21 52, Fax: +33 1 45 29 25 00
http://www.adalog.fr
Blady
2018-11-29 07:20:45 UTC
Permalink
Post by J-P. Rosen
Post by Blady
John Barnes a choisi une méthode plutôt qu'une méthode de classe pour
cet exemple. Mais j'aurais pour ma part plutôt répondu à la question
posée ci-dessus avec une méthode de classe.
Est-ce correct ?
Je n'aurais pas dû employer le terme "méthode de classe", puisque
précisément une opération à l'échelle de classe n'est pas une méthode...
- Une méthode est appropriée quand c'est une opération que toute classe
dérivée doit avoir, mais dont la réalisation (la "méthode") dépend de
l'objet: tout objet graphique doit être dessiné, mais la façon dont on
dessine un cercle n'est pas la même que la façon dont on dessine un
triangle.
- Une opération à l'échelle de classe est appropriée lorsque le service
est identique pour toute la classe (et qu'on veut être sûr que personne
n'invente une autre façon de faire). Pour déplacer un objet graphique,
on l'efface, on change ses coordonnées, et on le redessine. Cet
algorithme est indépendant du type spécifique de l'objet. En général,
une opération à l'échelle de classe utilise les méthodes de l'objet
(d'où le dispatching) mais ce n'est pas fondamental.
Merci Jean-Pierre pour ces explications simples et "éclairantes", Pascal.
Continuer la lecture sur narkive:
Loading...