Array of procedure, c'est possible ?

Messages postés
459
Date d'inscription
lundi 19 avril 2004
Statut
Membre
Dernière intervention
8 avril 2009
- - Dernière réponse : cs_Forman
Messages postés
663
Date d'inscription
samedi 8 juin 2002
Statut
Membre
Dernière intervention
6 avril 2010
- 30 juil. 2006 à 19:59
Yo,bande de coderz,

Je me pose  un question : j'ai un prog qui utilisele TcyPictureArea de Mauricio, et pour résumer (pour ceux qui connaissent pas), ce composant permetde définir des zones clickables dans une image. Si je clique sur une de ces  zones, le compo me retourne l'index de la zone cliquée (via la fonction areaclick). Evidement derriere, j'ai un case AreaIndex of..... et c'est  là que ca me turlupine.

Plutot que ce case of ne pourrais-je pas utiliser un array of procedure  et utiliser AreaIndex pour exécuter directement telle procedure ?

Je rame un peu pour mettre cette idée en pratique.... z'auriez des idées / bout_de_codes / exemples ?

Merci d'avance.

DFX
Afficher la suite 

10 réponses

Messages postés
663
Date d'inscription
samedi 8 juin 2002
Statut
Membre
Dernière intervention
6 avril 2010
1
0
Merci
Pour compléter ce que vient d'expliquer CptPingu, je rajoutterais:

il y a 2 "sortes" de types pour les procédures/fonctions:
* Ceux décrits précédemment (c'est à dire les procédures et fonctions classiques, qui ne sont pas des méthodes de classes)
* Les méthodes d'objets. La déclaration de leur type est suivie de la directive "of object", par exemple:

var
OnClick : Procedure(Sender: TObject) of object;

ceci signifiera que la variable OnClick pourra recevoir une méthode d'un objet qui a les même paramètres. Par exemple:

type
TStupidClass=class
procedure ClickMe(Sender:TObject);
end;

var
StupidInstance:TStupidClass;

StupidInstance:=TStupidClass.Create;
OnClick:=StupidInstance.ClickMe;

A noter que les procédures "standards" et les procédures "of object" ont des types qui sont incompatibles entre eux, même si la liste des paramètres est la même. La taille mémoire d'une procédure standard est 32 bits (tout simplement la taille d'un pointeur) alors qu'une procédure "of object" occupe 64 bits: 32 bits pour un pointeur vers l'instance de l'objet qui possède la méthode et 32 bits pour le pointeur vers l'adresse de la procédure.

Concrètement, une procédure "of object" a la déclaration équivalente au type TMethod (qui d'ailleurs est déclaré dans Sysstem.pas):

type
TMethod = record
Code, Data: Pointer;
end;

Code est le pointeur vers la procédure et Data doit contenir une instance de l'objet qui possède la procédure.

Les autres directives dans la déclaration d'une procédure/fonction telles que stdcall, cdecl et autres sont aussi prises en compte dans la compatibilité des types fonctionnels entre eux. Par contre, le nom des paramètres n'est pas important, à condition que les types soient les même. Par exemple:

les 3 types suivants ne sont pas équivalents:

type
TIdioticType1=function(x,y:Integer):Integer;
TIdioticType2=function(x,y:Integer):Integer;stdcall;
TIdioticType3=function(x,y:Integer):Integer of object;

les 2 types suivants sont équivalents:

type
TSmartType1=procedure(p:Pointer);
TSmartType2=procedure(q:Pointer);
Commenter la réponse de cs_Forman
Messages postés
459
Date d'inscription
lundi 19 avril 2004
Statut
Membre
Dernière intervention
8 avril 2009
1
0
Merci
Procedure(Sender: TObject) of object; C'est un TNotifyEvent ca non ?
Commenter la réponse de DeltaFX
Messages postés
663
Date d'inscription
samedi 8 juin 2002
Statut
Membre
Dernière intervention
6 avril 2010
1
0
Merci
Oui, c'est le même type.
Commenter la réponse de cs_Forman
Messages postés
459
Date d'inscription
lundi 19 avril 2004
Statut
Membre
Dernière intervention
8 avril 2009
1
0
Merci
Re question sur le meme sujet: j'ai bien capté je crois, mais héhéhé ca m'arrange pas trop d'avoir a typer fortement. Je m'explique, vu qu'il s'agit d'une interface utilisateur certains boutons appelle des procedures avec ou sans paramètres, d'autres des functions.....

Donc y a t'il moyens de déclarer un "Array of procedure or function" en restant "générique"  sans déclarer un type particulier ?
Commenter la réponse de DeltaFX
Messages postés
3821
Date d'inscription
dimanche 12 décembre 2004
Statut
Modérateur
Dernière intervention
28 août 2019
85
0
Merci
Non, ca reviendrait à créer un tableau contenant différent type de donnée,ce qui n'est pas possible. Il te reste la solution de faire un "array of pointer", mais non seulement tu devra transtyper à chaque fois ce pointeur, mais en plus tu devra trouver un moyen de récupérer le bon type à chaque fois...

Derniere solution, pas très élégante:
Tu ne créer que des fonctions, ayant toute le meme nombres de parametres. Certaines fonctions auront donc des parametres inutiles, et pour les procedures il suffira d'utiliser les fonctions sans récupérer le résultat. C'est bien moche, mais ca pourrait marcher.
Commenter la réponse de cptpingu
Messages postés
459
Date d'inscription
lundi 19 avril 2004
Statut
Membre
Dernière intervention
8 avril 2009
1
0
Merci
Ouaip..... ou alors je crée une fonction bidon qui englobe le truc à appeler avec les paramètres adéquats, et vlan, affaire réglée.....

Je testerai.
Commenter la réponse de DeltaFX
Messages postés
663
Date d'inscription
samedi 8 juin 2002
Statut
Membre
Dernière intervention
6 avril 2010
1
0
Merci
Il y a peut-être une autre solution, peut-être plus élégante. Je ne sais pas exactement ce que tu comptes faire dans ton programme, mais pense à utiliser les messages. Je m'explique:

tout objet possède la méthode suivante:

TObject=class
...
procedure TObject.Dispatch(var Message);
...
end;

Imagines que tu crées une classe ainsi (avec les déclarations de types/constantes préalables):

const
MsgID1=$01;
MsgID2=$02;

type
TMsg1=record
Msg:Cardinal;
Text:string;
end;

TMsg2=record
Msg:Cardinal;
x,y,Result:Integer;
end;

TMsgClass=class
private
procedure HandleMsg1(var Message:TMsg1);message MsgID1;
procedure HandleMsg2(var Message:TMsg2);message MsgID2;
end;

implementation

procedure TMsgClass.HandleMsg1(var Message:TMsg1);
begin
ShowMessage(Message.Text);
end;

procedure TMsgClass.HandleMsg2(var Message:TMsg2);
begin
Message.Result:=Message.x+Message.y;
end;

Une fois que tu as écrit ça, tu peux faire les appels suivants:

var
Msg1:TMsg1;
Msg2:TMsg2;
MsgClass:TMsgClass;
begin
MsgClass:=TMsgClass.Create;
Msg1.Msg:=MsgID1;
Msg2.Text:='Salut monde';
MsgClass.Perform(Msg1); //Appelle "HandleMsg1"
Msg2.Msg:=MsgID2;
Msg2.x:=1;
Msg2.y:=2;
MsgClass.Perform(Msg2); //Appelle "HandleMsg2" et calcule x+y et le stocke dans Result
end;

La méthode Perform se charge de "distribuer" le message à la bonne méthode (en fonction de la directive "message xxxxxx" dans les déclarations). Les 4 premiers octets de la structure passée en paramètre à Perform sont utilisés pour comparer les identifiants de messages. Cette méthode permet de passer des paramètres quelconques à un ensemble de méthodes.

Il faut toutefois noter que les contrôles Delphi ont déjà des messages réservés à des usages bien précis. Si tu comptes utiliser des messages avec des descendants de TControl, il ne faut pas utiliser les identifiants compris entre 0 et WM_USER (constante déclarée dans Messages.pas)
Commenter la réponse de cs_Forman
Messages postés
663
Date d'inscription
samedi 8 juin 2002
Statut
Membre
Dernière intervention
6 avril 2010
1
0
Merci
Correction: il faut remplacer "Perform" par "Dispatch" dans tout ce que j'ai écrit précédemment
Commenter la réponse de cs_Forman
Messages postés
459
Date d'inscription
lundi 19 avril 2004
Statut
Membre
Dernière intervention
8 avril 2009
1
0
Merci
Le compo de mauricio TcyPictureArea permet de découper une image en zones cliquables, mais ne crée pas d'evenement OnClick à chaque zone. Via Le Onclick du composant, on a AreaIndex qui renvoie l'index de la zone cliquée, charge au concepteur de faire un Case AreaIndex of pour savoir quoi faire dans tel cas.

Le but est de voir si  "MyProcedureArray[AreaIndex]" est plus rapide que ce "case of".

Je suis adepte du "pourquoi faire simple quand on peut faire compliqué", mais j'aime pas multiplier les entités ad lib pour le plaisir.
Commenter la réponse de DeltaFX
Messages postés
663
Date d'inscription
samedi 8 juin 2002
Statut
Membre
Dernière intervention
6 avril 2010
1
0
Merci
Les méthodes avec messages sont très rapides en tout cas, la routine Dispatch est programmée en assembleur pour appeler le bon "handler" en fonction du message, et on peut voir son code dans System.pas. Je pense qu'elle a une rapidité équivalente à case...of.

Je pense que pour un nombre réduit de cas possibles, case of est le plus rapide, et s'il y a beaucoup de procédures différentes alors MyProcedureArray[AreaIndex] sera le plus rapide.

En effet, au niveau du code machine généré, case of ... end teste toutes les possibilités les unes après les autres, et va directement à l'adresse du code concerné dès qu'il a trouvé une valeur qui correspond (cette adresse n'ayant pas à être calculée puisqu'elle est définie statiquement dans le programme).

Avec un tableau de procédures, le code doit déjà lire une adresse dans une case indicée du tableau (ce qui n'est pas très rapide par rapport à un test par exemple, en effet, il doit y avoir un accès à la RAM pour lire la valeur et l'amener dans les registres du processeur) et aller à cette adresse. Cette méthode devient plus rapide que la précédante lorsque le nombre de cas possible à tester dans le case ... of donne un nombre de tests assez grand pour que ça prenne plus de temps que de lire l'adresse dans le tableau.

On pourrait imaginer une méthode de case...of améliorée en faisant une dichotomie (il faudrait la programmer en assembleur). Par exemple, s'il y a 8 zones dans l'image (numérotées de 0 à 7):

if AreaIndex>=4 then begin
if AreaIndex>=6 then begin
if AreaIndex=7 then
Procedure7
else
Procedure6
end else begin
if AreaIndex=5 then
Procedure5
else
Procedure4
end;
end else begin
if AreaIndex>=2 then begin
if AreaIndex=3 then
Procedure3
else
Procedure2
end else begin
if AreaIndex=1 then
Procedure1
else
Procedure0
end;
end;

Je pense qu'il est évident que cette méthode peut être généralisée à d'autres cas.

Avec cette méthode, il y a Log(n)/Log(2) (ici 3) tests consécutifs pour appeler la bonne procédure, avec un case...of il y a en moyenne n/2, si on appelle n le nombre de valeurs possibles de AreaIndex. Et pour n grand, Log(n)/Log(2) est très petit par rapport à n/2...
Commenter la réponse de cs_Forman