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

A generic way to dynamically store and pass Delphi-Events of different types

1,164 views
Skip to first unread message

Daniel V. Oppenheim

unread,
Jan 8, 1998, 3:00:00 AM1/8/98
to

I am trying to build a notification system that allows observers to
register an event to be called when updating. The TNotifier would hold a
Tlist of events that during an update will be enumerated and triggered.
I would like my TNotifier to be general and not care about different
event types (i.e. with different signatures). I don’t want to create
subclasses of specialized objets each time I use different types of
events. Delphi events are not objects, but an 8 byte field with the
address of an instance (4 bytes) and adress of a method (4 bytes).

1. How can I dynamically store events – they can not be added to a
Tlist.
2. How can I pass events as arguments, again generically – I would like
my Tnotifier to have a function such as
procedure TNotifier.RegisterEvent (aDelphiEvent :
IWhishIKnewWhatTypeToPutHere);

Since events are types, and not objects, I can’t use an abstract super
class approach (i.e. TObject) that will allow me to pass events as
arguments or to assign events to variables (and typecast them before
the actual usage).

Any ideas about how to approach this?


Daniel Oppenheim
mu...@watson.ibm.com


Roman Krejci

unread,
Jan 8, 1998, 3:00:00 AM1/8/98
to

Hi Daniel,
You can typecast events to tMethod

..........


>1. How can I dynamically store events – they can not be added to a
>Tlist.

You can store the adresses of dymacilally created tmethod structures.

>2. How can I pass events as arguments, again generically – I would like
>my Tnotifier to have a function such as
> procedure TNotifier.RegisterEvent (aDelphiEvent :
>IWhishIKnewWhatTypeToPutHere);

TNotifier.RegisterEvent (aDelphiEvent : tMethod);

Roman
KRE...@mbox.cesnet.cz
(please remove STOPSPAM. in header]

Claire Humphrey

unread,
Jan 9, 1998, 3:00:00 AM1/9/98
to

type pNotifyEvent = ^tnotifyevent;

var
List : TList;
n1 : pNotifyEvent;

procedure TForm1.FormCreate(Sender: TObject);
var
n:integer;
begin
List := tlist.create;
try
getmem(n1,sizeof(tnotifyevent));
n1^ := button1Click;
list.add(n1);
getmem(n1,sizeof(tnotifyevent));
n1^ := form2.button1click;
list.add(n1);
getmem(n1,sizeof(tnotifyevent));
n1^ := form3.button1click;
list.add(n1);

for n := 0 to 2 do begin
n1 := list[n];
n1^(self);
end;

for n := 2 downto 0 do begin
freemem(list[n],sizeof(TNotifyEvent));
list.delete(n);
end;

finally
list.free;
end;

end;

How you'd keep watch on the type of function you'd add to the list may
require a little more thought.
Kind Regards
Claire, c...@nospam.HallworthHome.demon.co.uk

Peter Below

unread,
Jan 9, 1998, 3:00:00 AM1/9/98
to

In article <34B50FEF...@watson.ibm.com>, Daniel V. Oppenheim wrote:
> I am trying to build a notification system that allows observers to
> register an event to be called when updating. The TNotifier would hold a
> Tlist of events that during an update will be enumerated and triggered.
> I would like my TNotifier to be general and not care about different
> event types (i.e. with different signatures). I don’t want to create
> subclasses of specialized objets each time I use different types of
> events.
>
You haven't thought through this problem properly, i'm afraid. The caller
of the method absolutely has to know what parameters the method requires
and has to provide these parameters as well. This is required by the way
Delphi (and other compiled languages) call functions and methods, passing
parameters in registers and on the stack. So you have two options, as i see
it. Which to choose depends on your requirements.

1. Each TNotifier instance will only hold method pointers of one type.

In this case you could derive a new TNotifier descendent from a common base
class for each new method type you need to handle. The internal list of the
notifier class would store the method pointers themselves, which is generic
and can be implemented in the base class. If you use a TList you could
simply use two slots of the TList per method, one for the code part, one
for the object address. This avoids usage of a wrapper class just to store
the method pointers.

Function TMethodList.AddMethod( aMethod: TMethod ): Integer;
Begin
Result := Add( aMethod.Code ) div 2;
Add( aMethod.Data );
End;

This way the code pointers would always be stored at even indices, the
object reference at odd indices (from the view of TList, for the
TMethodLIst the indices would go from 0 to (count div 2)-1). To get methods
back you would have a

Function TMethodLIst.GetMethod( index: Integer ): TMethod;
Begin
Result.Code := Items[ index * 2 ];
Result.Data := Items[ index * 2 + 1];
End;

A MethodCount property could be implemented to hide the fact that each
entry consumes 8 bytes.

This takes care of storing the method references, calling the methods would
be via a method of the appropriate TMethodList descendent. You cannot
easily use a virtual method and polymorphism here (but it would be
possible, in principle, using an array of const as parameter passing
mechanism). The code that uses the TMethodList instance would need to now
the actual class of the instance, to be able to call this method and
provide the correct parameters to pass to each method. E.g.

Type
TNotificationList = Class( TMethodList )
public
Procedure FireEvents( aSender: TObject );
end;

Procedure TNotificationList.FireEvents( aSender: TObject );
Var
i: Integer;
m: TMethod;
Begin
For i:= 0 To (count div 2) - 1 Do Begin
m:= GetMethod( i );
If Assigned(m) Then
TNotifyEvent(m)(aSender);
End;
End;

2. Any TNotifier instance has to handle different method types.

In this case you need wrapper objects for each method pointer type, the
wrapper class is the one that knows the method type and stores the
parameters for the method call. The base class would have a virtual
FireEvent method that each descendent overrides to handle a specific method
type. A client would create an instance of the proper class for its event
and register this event with the notifier by adding the object to the
notifiers list (which can now be a simple TList). It could set default
parameters for the method call, the parameters would be stored in fields of
the object. To fire the events in the list the notifier would just call the
FireEvent method of each object it stores. The code triggering the events
would have the option of looking through the list of event objects and set
parameters before the call (it would have to know which event object types
to look for). The event objects could have ParambyName and HasParam virtual
methods that would allow an interesting party to inquire about the
existence of a parameter by name.

Peter Below (TeamB) 10011...@compuserve.com)


0 new messages