RIEN RIEN RIEN RIEN RIEN RIEN RIEN RIEN RIEN RIEN RIEN RIEN RIEN RIEN RIEN RIEN

florenth - 2 déc. 2006 à 20:58
 florenth - 5 déc. 2006 à 17:32
Bien vu rt15 mais ta méthode (bien que nettement plus courte) présente quelques problèmes :

- Comment, si tu as plusieurs objets TForm1 (ou d'autre classes d'ailleurs), choisir lequel recrevra le callback ? Il faudrait donc transmettre la référence de l'objet pour le paramètre Self, ce qui entraine tout un bazar pour le trainer jusqu'a son utilisation.

- Si tu souhaites avoir un callback différent entre la version objet et l'autre, (par ex, rajouter un paramètre UserData dans la non objet), ta méthode ne fonctionne plus car il faudrait aussi le mettre dans l'autre.

- De plus, tu obliges à avoir un paramètre de 32 bits comme premier param de la méthode non objet (qui compense la référence à l'objet ajoutée automatiquement par Delphi dans l'autre) et force donc l'utilisation de la convention d'appel par défaut (register) (bon c'est pas le plus génant). Et avec des méthode virtuelles, ou dynamiques, je n'ai pas testé, je me demande bien si ça marche ou pas ...

Malgré tout, je trouve que ta méthode a pas mal de "charme" si j'ose le dire ainsi et je la garde ranfée quelque part au cas où, même si je préfère la mienne, plus sûre. Parce que ta méthode, c'est un peu de la bidouille quand même, même si ça marche pour des cas simples comme le ShowMessage().
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
5 déc. 2006 à 13:19
Désolé pour le double post (Et le code moche), mais voici une version sans classe nettement plus simple.

type TCallBack = procedure (Self: Integer; sTitle: String);
type TCallBackObj = procedure (sTitle: String) of object;

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
procedure MsgBoxObj(sTitle: String);
end;

procedure Run(uCallBack: TCallBack);

var
Form1: TForm1;

procedure MsgBox(Self: Integer; sTitle: String);

implementation

{$R *.dfm}

procedure TForm1.MsgBoxObj(sTitle: String);
begin
Application.MessageBox('Méthode', PChar(sTitle));
end;

procedure MsgBox(Self: Integer; sTitle: String);
begin
Application.MessageBox('Classique', PChar(sTitle));
end;

procedure Run(uCallBack: TCallBack);
begin
uCallBack(0, 'Le titre');
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
Run(@TForm1.MsgBoxObj);
Run(@MsgBox);
end;
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
5 déc. 2006 à 12:26
Salut,

Certains progs C surchargent les fonction d'allocation pendant le débugage. De cette manière, ils détectent toute fuite de mémoire pendant le développement. Cela leur permet de se passer de garbage (Qui consomme des ressources) par la suite avec une certaines assurance.

Pour ce qui est de trouver un autre moyen de faire des callback objet ou pas, y a un bricolage. Du style de bricolage pas possible en DOTNET je suppose d'ailleurs.

C'est juste basé sur le fait que le Self caché des méthodes fait 32 bits et est placé à gauche.

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type TCallBack = procedure (Self: Integer; sTitle: String);
type TCallBackObj = procedure (sTitle: String) of object;

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
procedure MsgBoxObj(sTitle: String);
end;

type TCallCallBack = class(TObject)
public
constructor Create(uCallBack: TCallBack); overload;
constructor Create(uCallBack: TCallBackObj); overload;
procedure Run;
private
fuCallBack: TCallBack;
end;

var
Form1: TForm1;

procedure MsgBox(Self: Integer; sTitle: String);

implementation

{$R *.dfm}

procedure TForm1.MsgBoxObj(sTitle: String);
begin
Application.MessageBox('Méthode', PChar(sTitle));
end;

procedure MsgBox(Self: Integer; sTitle: String);
begin
Application.MessageBox('Classique', PChar(sTitle));
end;

constructor TCallCallBack.Create(uCallBack: TCallBackObj);
begin
inherited Create;
fuCallBack:= TCallBack(uCallBack);
end;

constructor TCallCallBack.Create(uCallBack: TCallBack);
begin
inherited Create;
fuCallBack:= uCallBack;
end;

procedure TCallCallBack.Run;
begin
fuCallBack(Integer(Self), 'Le titre');
end;

procedure TForm1.Button1Click(Sender: TObject);
var
uObj1: TCallCallBack;
uObj2: TCallCallBack;
begin
uObj1:= TCallCallBack.Create(@TForm1.MsgBoxObj);
uObj2:= TCallCallBack.Create(@MsgBox);
uObj1.Run;
uObj2.Run;
uObj1.Free;
uObj2.Free;
end;

end.
cs_Delphiprog Messages postés 4297 Date d'inscription samedi 19 janvier 2002 Statut Membre Dernière intervention 9 janvier 2013 32
3 déc. 2006 à 10:27
Pourquoi avoir record à un garbage collector ?
La solution immédiate passe par l'utilisation d'interfaces où l'objet est au-to-ma-ti-que-ment détruit lorsque le compteur de références tombe à zéro.
Du coup, pas besoin de blocs de protection ni d'appels au destructeur.

Un GC ? C'est bon pour les programmeurs négligents qui ont trop fait la java...MDR
Bonnes vieilles recettes ? Eh oui, mais n'empèche que je n'ai pas trouvé d'autre moyen de résoudre le problème (des volontaires ???).

Par contre, je ne suis pas convaincu par .Net. D'ailleurs, les delphistes en général n'ont pas trop l'air de l'être. Il suffit de regarder le nombre de sources Delphi.Net pour s'en convaincre !
En tout cas, je me souviendrai du nom de délégué, ça peut servir, qui sait ...

Alors oui, et encore oui pour le try...finally - c'est d'ailleurs une chose que je fais tout le temps. Là, je n'ai rien mis pour ne pas surcharger parce que sinon, il faudrait aussi le faire pour le BeginUpdate()...EndUpdate() du TStrings. Mais c'est vrai que c'est important.
Si Delphi-Win32 possédait un GarbageCollector, ce serait plus pratique (tout en une ligne).
cs_Delphiprog Messages postés 4297 Date d'inscription samedi 19 janvier 2002 Statut Membre Dernière intervention 9 janvier 2013 32
2 déc. 2006 à 22:02
Alors, on ressort les bonnes vieilles recettes ? :o)

Effectivement, les fonctions de rappel sont très utiles et permettent d'alléger le code tout en obtenant un couplage faible : le code qui appelle la fonction se moque totalement de savoir ce qu'elle fera des arguments qu'il lui transmet. Et c'est bien ainsi.
Ce concept n'a rien de révolutionnaire même si sous .Net on parle des délégués (ça fait bien dans les soirées mondaines...).

Là, Florenth, tu nous fais une très belle démonstration, simple et efficace qui, je l'espère, sera lue par beaucoup de monde.

Un petit bémol : pense à protéger le bloc de code entre l'appel au constructeur de TSommeCallbackLink et sa libération par Free par un try...finally.
Ah oui, un détail: l'exemple est bidon surtout que la fonction qui n'appartient pas à la classe TFrmDemo peut très bien y avoir accès (du coup, le SL passé en paramètre ne servirait plus trop)

Mais bon, le but est de montrer comment faire dans les cas où ce n'est pas possible et surtout comment s'en servir dans les cas où cela devient un poil plus complexe.

A ce sujet, j'ai remarqué que bon nombre de fonction de l'API Windows qui admettent des callbacks ont aussi un paramètre UserData, souvent Pointer ou Cardinal qui permet justement à la fonction d'avoir accès à un minimum de données.
Voir aussi l'implémentation de TThread dans l'unité Classes.pas pour ceux que ça intéresse.

N'hésitez pas à poser vos commentaires bien sûr ^^
Rejoignez-nous