Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Méthodes d'instance et méthodes de classe.

21 views
Skip to first unread message

Blady

unread,
Apr 14, 2013, 4:57:16 AM4/14/13
to
Bonjour,

j'ai le projet de traduire la m�canique objet du langage Objective-C en Ada.
L'Objective-C a la particularit� de comporter des m�thodes d'instance et
des m�thodes de classe :

@interface PA
// m�thode de classe
+ (id) alloc;
// m�thode d'instance
- (id) init;
@end

L'emploi est le suivant :

PA* toto;
[PA alloc]; // appel m�thode de classe
[toto init]; // appel m�thode d'instance

Pour diff�rencier les deux m�canismes, j'ai traduit avec deux types Ada
tagged diff�rents : TAI pour la partie instance et TAC pour la partie
classe.
Voir sources en fin de message.

C'est pas trop mal pour la classe PA, par contre �a se g�te avec une
classe fille :
Je d�finie une classe fille PB, la m�thode d'instance init est red�finie
par contre pas la m�thode de classe alloc.

Il y a un CE lors de l'appel � alloc :
./testab
Execution terminated by unhandled exception
Exception name: CONSTRAINT_ERROR
Message: testab.adb:11 tag check failed

Je m'en doutait un peu vu que j'ai du forcer le type r�sultant.
Auriez-vous autre une id�e en Ada pour les m�thodes de classe ?
J'ai essay� plusieurs formes sans succ�s.

Merci par avance, Pascal.
blady.pagesperso-orange.fr

Les sources :
package PA is
type TAI is tagged record
Name : String (1..10);
end record;
function init (Self : TAI) return TAI;
type TAC is tagged record
Name : String (1..10);
end record;
function alloc (Self : TAC) return TAI'Class;
PAClass : constant TAC := (name => "Classe PA.");
end;
package body PA is
function init (Self : TAI) return TAI is
begin
return O : TAI do
O.Name := "Init PA.";
end return;
end init;
function alloc (Self : TAC) return TAI'Class is
begin
return O : TAI do
O.Name := Self.Name;
end return;
end alloc;
end PA;
with PA; use PA;
package PB is
type TBI is new TAI with null record;
function init (Self : TBI) return TBI;
type TBC is new TAC with null record;
PBClass : constant TBC := (name => "Classe PB.");
end;
package body PB is
function init (Self : TBI) return TBI is
begin
return O : TBI do
O.Name := "Init PB.";
end return;
end init;
end PB;
with PA;use PA;
with PB;use PB;
procedure testab is
OA : TAI;
OB : TBI;
begin
OA := TAI(alloc(PAClass));
OA := init (OA);
OB := TBI(alloc(PBClass)); -- line 11
OB := init (OB);
end;

Pascal Obry

unread,
Apr 14, 2013, 5:36:32 AM4/14/13
to

Pascal,

> Pour diff�rencier les deux m�canismes, j'ai traduit avec deux types Ada
> tagged diff�rents : TAI pour la partie instance et TAC pour la partie
> classe.

J'aurais tout simplement fait un type tagg� pour les m�thodes d'instance
et des routines dans le m�me paquetage pour les m�thodes de classe, non?

--
Pascal Obry / Magny Les Hameaux (78)

The best way to travel is by means of imagination

http://v2p.fr.eu.org
http://www.obry.net

gpg --keyserver keys.gnupg.net --recv-key F949BD3B

Ludovic Brenta

unread,
Apr 14, 2013, 5:53:11 AM4/14/13
to
Pascal Obry <pas...@obry.net> writes:
> Pascal,
>
>> Pour différencier les deux mécanismes, j'ai traduit avec deux types
>> Ada tagged différents : TAI pour la partie instance et TAC pour la
>> partie classe.
>
> J'aurais tout simplement fait un type taggé pour les méthodes
> d'instance et des routines dans le même paquetage pour les méthodes de
> classe, non?

Effectivement, voir le chapitre sur la programmation orientée objet en
Ada sur Wikibooks. J'ai écrit la partie qui explique les mécanismes en
des termes compréhensibles par les programmeurs C++:

http://en.wikibooks.org/wiki/Ada_Programming/Object_Orientation#Object-Oriented_Ada_for_C.2B.2B_programmers

Par curiosité, pourquoi essaies-tu de "traduire la mécanique objet du
langage Objective-C en Ada"? Pour avoir l'introspection?

--
Ludovic Brenta.

Blady

unread,
Apr 14, 2013, 12:42:33 PM4/14/13
to
Bonjour Pascal et Ludovic, oui, j'ai commencé par là mais je voulais
plus ;-) je m'explique:

En Objective-C, je déclare :
@interface PA
// méthode de classe
+ (id) alloc;
// méthode d'instance
- (id) init;
@end

Et une classe fille héritant de la précédente :
@interface PB : PA
// méthode d'instance
- (id) init;
@end

Que j'utilise avec :
PA* toto;
[PA alloc]; // appel méthode de classe
[toto init]; // appel méthode d'instance
PB* titi;
[PB alloc]; // appel méthode de classe
[titi init]; // appel méthode d'instance

La méthode alloc n'est pas redéfinie dans la classe PB.
Pourtant son appel par PB va bien allouer une instance de PB !

Voilà ce que j'avais essayé:
package PA is
type TAI is tagged record
Name : String (1..10);
end record;
function init (Self : TAI) return TAI;
type TAC is record
Name : String (1..10);
end record;
function alloc (Self : TAC) return TAI;
PAClass : constant TAC := (name => "Classe PA.");
end;
with PA; use PA;
package PB is
type TBI is new TAI with null record;
function init (Self : TBI) return TBI;
type TBC is new TAC;
PBClass : constant TBC := (name => "Classe PB.");
end;
with PA;use PA;
with PB;use PB;
with Ada.Text_IO; use Ada.text_IO;
procedure testab is
OA : TAI;
OB : TBI;
begin
OA := PA.alloc(PAClass);
put_line(OA.name);
OA := init (OA);
put_line(OA.name);
OB := PB.alloc(PBClass); -- line 14
put_line(OB.name);
OB := init (OB);
put_line(OB.name);
end;

Évidemment, une erreur de compilation est remontée car alloc dérivée
dans PB ne retourne pas le bon type TBI :
testab.adb:14:12: expected type "TBI" defined at pb.ads:3
testab.adb:14:12: found type "TAI" defined at pa.ads:2

N'a-je alors pas d'autre choix que de dupliquer le code d'alloc dans PB ?
function alloc (Self : TBC) return TBI;

Dans l'article Ada_Programming, ne connaissant pas bien le C++, je
comprends que u() est une méthode de classe, appelée pas C::u().
En Objective-C les méthodes de classes sont un peu plus:
"En objective-C l’objet self dans une méthode de classe désigne bien
l’objet courant, à savoir une instance de « Class »."
"Pour faire court, en Objective-C, une méthode de classe est un message
qui est pris en charge par l’objet de la classe plutôt que par une
instance de la classe."
http://sgamel.free.fr/spip.php?article123

J'essayai avec un deuxième type tagged de simuler ce comportement...
Est-ce possible ?

Merci, Pascal.
blady.pagesperso-orange.fr

Le 14/04/13 11:53, Ludovic Brenta a écrit :

Ludovic Brenta

unread,
Apr 14, 2013, 1:34:32 PM4/14/13
to
Blady <p....@orange.fr> writes:
> "Pour faire court, en Objective-C, une méthode de classe est un
> message qui est pris en charge par l’objet de la classe plutôt que par
> une instance de la classe."
> http://sgamel.free.fr/spip.php?article123
>
> J'essayai avec un deuxième type tagged de simuler ce comportement...
> Est-ce possible ?

Pas directement car Ada n'a pas d'introspection. Il est sans doute
possible de le réimplémenter mais *pourquoi*?

Note que Ada est basé sur le typage statique alors qu'Objective-C
utilise le typage dynamique, basé sur l'introspection. Réconcilier les
deux est quasiment impossible. Si tu veux écrire de l'Objectivec-C en
Ada ("Objective-Ada"), il faut soit modifier le compilateur Ada, soit
accepter de mélanger le typage dynamique pour quelques types,
laborieusement décrits en termes Ada, et le typage statique pour tous
les autres (Integer, String, etc.).

Pour résumer: si tu aimes le typage dynamique tant que ça... pourquoi
écrire en Ada? Note que, outre Objective-C, d'autres langages utilisent
le même principe, à commencer par le premier langage orienté objet,
Smalltalk.

> package PA is
> type TAI is tagged record
> Name : String (1..10);
> end record;
> function init (Self : TAI) return TAI;
> type TAC is record
> Name : String (1..10);
> end record;
> function alloc (Self : TAC) return TAI;
> PAClass : constant TAC := (name => "Classe PA.");
> end;
> with PA; use PA;
> package PB is
> type TBI is new TAI with null record;
> function init (Self : TBI) return TBI;
> type TBC is new TAC;

Ici il y a une erreur: si le type TBC doit être capable de créer un
objet de type TBI, alors il doit obligatoirement redéfinir alloc. En
effet:
* TBI peut avoir des composants supplémentaires par rapport à TAI
* la nouvelle version de alloc doit retourner un TBI.

> PBClass : constant TBC := (name => "Classe PB.");
> end;

* et la nouvelle version de alloc doit indiquer à l'objet qu'elle crée
qu'elle appartient au type TBI et non TAI.

L'idiome accepté pour créer un objet de type arbitraire à l'intérieur
d'une classe de dérivation est:

package P is
type T (<>) is abstract tagged private;
function Alloc (parameters...) return T'Class;
private
type T is tagged null record;
end P;

Les discriminants inconnus (<>) obligent tout autre package à appeler
Alloc pour créer un objet de type T ou dérivé de T. Sans eux,
n'importe qui peut simplement écrire:

My_Object : P.T;

sans appeler Alloc. Avec les discriminants inconnus, il est obligé
d'écrire:

My_Object : P.T'Class := P.Alloc (parameters...);

et être préparé à ce que My_Object soit de n'importe quel type dérivé de
P.T. Bien entendu, ce sont les paramètres de Alloc qui déterminent le
type exact de l'objet créé. Et bien entendu, ce sont ces mêmes
paramètres qui rendent dynamique le type de l'objet créé.

--
Ludovic Brenta.

Blady

unread,
Apr 14, 2013, 1:55:37 PM4/14/13
to
Le 14/04/13 19:34, Ludovic Brenta a écrit :
> Blady writes:
>> "Pour faire court, en Objective-C, une méthode de classe est un
>> message qui est pris en charge par l’objet de la classe plutôt que par
>> une instance de la classe."
>> http://sgamel.free.fr/spip.php?article123
>>
>> J'essayai avec un deuxième type tagged de simuler ce comportement...
>> Est-ce possible ?
>
> Pas directement car Ada n'a pas d'introspection. Il est sans doute
> possible de le réimplémenter mais *pourquoi*?

Bin, sur MacOS, j'ai réussi à appeler le runtime d'Objective-C avec du
code Ada.
https://hermes.gwu.edu/cgi-bin/wa?A2=ind1303&L=gnat-osx&F=&S=&P=68
Je me suis donc lancé dans la traduction de la bibliothèque Cocoa en Ada.

<...>

>> with PA; use PA;
>> package PB is
>> type TBI is new TAI with null record;
>> function init (Self : TBI) return TBI;
>> type TBC is new TAC;
>
> Ici il y a une erreur: si le type TBC doit être capable de créer un
> objet de type TBI, alors il doit obligatoirement redéfinir alloc. En
> effet:
> * TBI peut avoir des composants supplémentaires par rapport à TAI
> * la nouvelle version de alloc doit retourner un TBI.

Oui, mais sans importance pour moi car le contenu ne sera toujours qu'un
pointeur sur l'objet Objective-C et donc alloué correctement par le runtime.
J'ai "juste" le pb du type du retour Ada :-(
<...>

> L'idiome accepté pour créer un objet de type arbitraire à l'intérieur
> d'une classe de dérivation est:
>
> package P is
> type T (<>) is abstract tagged private;
> function Alloc (parameters...) return T'Class;
> private
> type T is tagged null record;
> end P;
>
> Les discriminants inconnus (<>) obligent tout autre package à appeler
> Alloc pour créer un objet de type T ou dérivé de T. Sans eux,
> n'importe qui peut simplement écrire:
>
> My_Object : P.T;
>
> sans appeler Alloc. Avec les discriminants inconnus, il est obligé
> d'écrire:
>
> My_Object : P.T'Class := P.Alloc (parameters...);
>
> et être préparé à ce que My_Object soit de n'importe quel type dérivé de
> P.T. Bien entendu, ce sont les paramètres de Alloc qui déterminent le
> type exact de l'objet créé. Et bien entendu, ce sont ces mêmes
> paramètres qui rendent dynamique le type de l'objet créé.
>
Je vais essayer, merci, Pascal.
blady.pagesperso-orange.fr

Blady

unread,
Apr 20, 2013, 3:11:09 AM4/20/13
to
Bonjour, j'ai finalement trouvé une formulation qui me convient.

Dans pa.ads:
type TAI is tagged record
Name : Str10;
end record;
function init (Self : TAI) return TAI;
function alloc (Class : Str10) return TAI;

Dans pb.ads :
type TBI is new TAI with null record;
function init (Self : TBI) return TBI;

Dans testab.adb :
OA : TAI := alloc ("Classe PA1");
OB : TBI := alloc ("Classe PB1");
...
OA := init (OA);
OB := init (OB);

alloc est bien dérivée pour retourner le type TBI mais allouera la bonne
classe passée en paramètre.
Cela correspond à la formulation Objective-C:
PB *OB = [[PB alloc] init];

Merci pour vos conseils, je poursuis mon projet de traduction, Pascal.
blady.pagesperso-orange.fr

Le 14/04/13 19:55, Blady a écrit :
> Le 14/04/13 19:34, Ludovic Brenta a écrit :

Ludovic Brenta

unread,
Apr 23, 2013, 3:25:13 PM4/23/13
to
Blady writes:
> Bonjour, j'ai finalement trouvé une formulation qui me convient.
>
> Dans pa.ads:
> type TAI is tagged record
> Name : Str10;
> end record;
> function init (Self : TAI) return TAI;
> function alloc (Class : Str10) return TAI;
>
> Dans pb.ads :
> type TBI is new TAI with null record;
> function init (Self : TBI) return TBI;
>
> Dans testab.adb :
> OA : TAI := alloc ("Classe PA1");
> OB : TBI := alloc ("Classe PB1");
> ...
> OA := init (OA);
> OB := init (OB);
>
> alloc est bien dérivée pour retourner le type TBI mais allouera la
> bonne classe passée en paramètre.
> Cela correspond à la formulation Objective-C:
> PB *OB = [[PB alloc] init];
>
> Merci pour vos conseils, je poursuis mon projet de traduction, Pascal.
> blady.pagesperso-orange.fr

Effectivement, ça marche car la fonction alloc est primitive du type TAI
et donc héritée par TBI.

Cependant, tout est statique dans le programme. Il n'y a aucun
dispatching dynamique. En Objective-C, [[PB alloc] init] fait deux
dispatching dynamiques, assortis d'appels à l'introspection pour
découvrir si chaque classe accepte le message. Le progamme Ada est donc
plus rapide :)

Et comme tout est statique, les chaînes de caratères "Classe PA1" et
"Classe PA2" ne servent pas à faire du dynamique et peuvent contenir
n'importe quoi et pas nécessairement le nom d'une classe.

L'autre différence entre la version Objective-C et la version Ada est
que les objets "alloués" sont sur la pile et disparaissent dès que le
bloc qui les contient finit. La version Objective-C alloue sur le tas,
retourne un pointeur et laisse le programmeur s'occuper de la
destruction des objets.

--
Ludovic Brenta.
0 new messages