i need a function like the code below (not working! its just example)
type
TCallBack = procedure( x:String) of object;
procedur procA(CallBack : TCallBack);
begin
CallBack('hello world')
end;
and then fire the procA an pass a procedure as parameter:
procA( procedure (res : string)
begin
ShowMessage(res);
end);
From your syntax at the CALLING site, it seems like you want to declare the procedure to be called inline at the call site.
If so, you shouldn't use OF OBJECT definition but REFERENCE TO:
type
TCallBack = reference to procedure(x : String);
Then you can use your code:
procedure procA(CallBack : TCallBack);
begin
CallBack('hello world')
end;
procA(procedure (res : string)
begin
ShowMessage(res);
end);
EDIT: Sample code
type
TCallBack = reference to procedure(x : String);
procedure procA(CallBack : TCallBack);
begin
CallBack('hello world')
end;
procedure TForm59.FormCreate(Sender: TObject);
begin
procA(procedure (res : string)
begin
ShowMessage(res);
end);
end;
EDIT: Using the CallBack event from within a thread:
PROCEDURE TWebThread.Execute;
BEGIN
.
.
.
Synchronize(PROCEDURE
BEGIN
CallBack('Hello World')
END)
END;
But then you have to ensure that any variables you access from within the inline-defined procedure is still valid at the time the thread calls the callback, so don't use local variables, as they may very well have run out of scope. Also, make sure that any CLASS instances you use in the inline-defined procedure hasn't been Free'd or otherwise made invalid between the time you create the thread and the time the callback event is executed.
Related
I have thoses objects :
TmyObject1=class
public
Status: integer;
end;
TmyObject2=class
public
StatusA: integer;
StatusB: integer;
end;
I have many objects like those one with many status fields declared. I would like to call a function and let the function update the value of one of the status field. quite easy I declare the function like this :
function MyFunction(var AStatus: Integer);
and I call it like this
MyFunction(myObject1.Status);
or for example
MyFunction(myObject2.StatusB);
that good but now my problem arrive, in MyFunction I create a thread and I want to let the possibility to the thread to update also the value of Status. something like this :
function MyFunction(var AStatus: Integer);
begin
MyTread.??ObjectStatus?? := AStatus;
MyTread.start;
end;
procedure TmyThread.execute;
begin
...
??ObjectStatus?? := NewStatus
...
end;
How can I do ? what type must be the TmyThread.??ObjectStatus??. I was thinking to gave to MyTread a pointer address but I m afraid that if memory relocation between the start and the end of the thread that the pointer address could become wrong (Code must work on ios/android/windows/etc.). any other options to solve my problem ?
Instead of a var parameter you can provide getter and setter to your function:
function MyFunction(GetStatus: TFunc<Integer>; SetStatus: TProc<Integer>);
begin
MyThread.GetStatus := GetStatus;
MyThread.SetStatus := SetStatus;
MyThread.start;
end;
procedure TmyThread.execute;
begin
...
OldStatus := GetStatus;
SetStaus(NewStatus);
...
end;
Calling this function requires some anonymous methods now:
MyFunction(
function: Integer
begin
result := myObject1.Status;
end,
procedure(Arg: Integer)
begin
myObject1.Status := Arg
end);
Of course you have to make sure that myObject1 is available during the thread execution.
In our application framework we have some kind of instance handler class that in resume it's responsible for capture the instances create by our others controllers/components/forms/etc.
Here's the declaration:
TInstanceHandler = class(TFrameworkClass)
strict private
FInstances : TList<TObject>;
procedure FreeInstances();
protected
procedure Initialize(); override;
procedure Finalize(); override;
public
function Delegate<T : class>(const AInstance : T) : T;
end;
And the implementation:
procedure TInstanceHandler.FreeInstances();
var AInstance : TObject;
begin
for AInstance in FInstances do
if(Assigned(AInstance)) then AInstance.Free();
FInstances.Free();
end;
procedure TInstanceHandler.Initialize();
begin
inherited;
FInstances := TList<TObject>.Create();
end;
procedure TInstanceHandler.Finalize();
begin
FreeInstances();
inherited;
end;
function TInstanceHandler.Delegate<T>(const AInstance : T) : T;
begin
FInstances.Add(AInstance);
end;
what happen sometimes is that our programmers forgot the existence of this class or his purpose and they free their instances.
Like this:
with InstanceHandler.Delegate(TStringList.Create()) do
try
//...
finally
Free();
end;
what happens next is that when TInstanceHandler is finalized it will try to free the delegated instance again and this will lead to a error.
I know the season why Assigned fail in this case and as far i can see i cant use FreeAndNil.
so the question is: how i can correctly check if the reference was already freed?
How I can correctly check if the reference was already freed?
You cannot.
Delphi XE6 - I have a Unit (EMAIL1.pas) which does related processing. This is meant to be a standalone unit I can incorporate into multiple programs. My initial procedure is called GetDetailsFromEmailAddress. It has two parameters, an email address which I lookup and a "group of data" which will get updated, currently defined as a var. This can be a record or a class, I don't really care. It is just a group of related strings (firstname, last name, city, etc). Let's call this EmpRec.
My challenge is that this procedure creates an instance of a class (JEDI VCL HTMLParser) which uses a method pointer to call a method (TableKeyFound). This routine needs to update EmpRec. I do not want to change this code (HTMLPArser routine) to add additional parameters. There are several other procedures that my UNIT creates. All of them need to read/update EmpRec. How do I do this?
I need a way to "promote" the variable EmpRec which is passed in this one routine (GetDetailsFromEmailAddress) to be GLOBAL within this UNIT so that all the routines can access or change the various elements. How do I go about this? I do NOT really want to have to define this as a GLOBAL / Application wide variable.
Code sample below. So.. How does the routine TableKeyFoundEx get access to the EmpRec variable?
procedure GetDetailsFromEmailAddress(Email: string; var EmpRec: TEmpRec);
begin
...
// Now create the HTML Parser...
JvHtmlParser1 := TJvHTMLParser.Create(nil);
// On event KeyFoundEx, call Parsehandlers.TableKeyFoundEx;
JvHtmlParser1.OnKeyFoundEx := ParseHandlers.TableKeyFoundEx;
...
end.
procedure TParseHandlers.TableKeyFoundEx(Sender: TObject; Key, Results, OriginalLine: String; TagInfo: TTagInfo;
Attributes: TStrings);
begin
..
// NEED ACCESS to EmpRec here, but can't change procedure definition
end;
There are two different ways I would approach this:
use the parser's Tag property:
procedure GetDetailsFromEmailAddress(Email: string; var EmpRec: TEmpRec);
begin
...
JvHtmlParser1 := TJvHTMLParser.Create(nil);
JvHtmlParser1.OnKeyFoundEx := ParseHandlers.TableKeyFoundEx;
JvHtmlParser1.Tag := NativeInt(#EmpRec);
...
end;
procedure TParseHandlers.TableKeyFoundEx(Sender: TObject; Key, Results, OriginalLine: String; TagInfo: TTagInfo; Attributes: TStrings);
var
EmpRec: PEmpRec; // assuming PEmpRec = ^TEmpRec
begin
EmpRec := PEmpRec(TJvHTMLParser(Sender).Tag);
...
end;
use a little TMethod hack to pass the record DIRECTLY to the event handler:
// Note: this is declared as a STANDALONE procedure instead of a class method.
// The extra DATA parameter is where a method would normally pass its 'Self' pointer...
procedure TableKeyFoundEx(Data: Pointer: Sender: TObject; Key, Results, OriginalLine: String; TagInfo: TTagInfo; Attributes: TStrings);
var
EmpRec: PEmpRec; // assuming PEmpRec = ^TEmpRec
begin
EmpRec := PEmpRec(Data);
...
end;
procedure GetDetailsFromEmailAddress(Email: string; var EmpRec: TEmpRec);
var
M: TMethod;
begin
...
JvHtmlParser1 := TJvHTMLParser.Create(nil);
M.Code := #TableKeyFoundEx;
M.Data := #EmpRec;
JvHtmlParser1.OnKeyFoundEx := TJvKeyFoundExEvent(M);
...
end;
In addition to the two options that Remy offers, you could derive a sub-class of TJvHTMLParser.
type
PEmpRec = ^TEmpRec;
TMyJvHTMLParser = class(TJvHTMLParser)
private
FEmpRec: PEmpRec;
public
constructor Create(EmpRec: PEmpRec);
end;
....
constructor TMyJvHTMLParser.Create(EmpRec: PEmpRec);
begin
inherited Create(nil);
FEmpRec := EmpRec;
end;
When you create the parser, do so like this:
procedure GetDetailsFromEmailAddress(Email: string; var EmpRec: TEmpRec);
var
Parser: TMyJvHTMLParser;
begin
Parser := TMyJvHTMLParser.Create(#EmpRec);
try
Parser.OnKeyFoundEx := ParseHandlers.TableKeyFoundEx;
....
finally
Parser.Free;
end;
end.
And in your OnKeyFoundEx you cast Sender back to the parser type to gain access to the record:
procedure TParseHandlers.TableKeyFoundEx(Sender: TObject; ...);
var
EmpRec: PEmpRec;
begin
EmpRec := (Sender as TMyJvHTMLParser).FEmpRec;
....
end;
I want a procedure to be executed when an event is happened. But that procedure is set by another procedure(SetNotifierProc).
Firstly I run this:
SetNotifierProc(Proc1);
And then Proc1 is executed whenever event triggered.
How could I code SetNotifierProc to get a procedure as an argument and how to inform event handler to execute that procedure?
Problem: I have a TCPServerExecute and want to run a procedure to show received data. But because I have multiple forms I want to set a procedure that handle received data.
Thanks
If your procedure is an ordinary procedure without arguments:
Type
TForm1 = Class(TForm)
..
private
FMyProc : TProcedure;
public
procedure SetEventProc(aProc : TProcedure);
procedure TheEvent( Sender : TObject);
end;
procedure Test;
begin
// Do something
end;
procedure TForm1.SetEventProc(aProc: TProcedure);
begin
Self.FMyProc := aProc;
end;
procedure TForm1.TheEvent(Sender: TObject);
begin
if Assigned(FMyProc) then
FMyProc;
end;
// to set the callback to procedure "Test"
Form1.SetEventProc(Test);
If your procedure has arguments, declare a procedure type:
Type
MyProcedure = procedure( aString : String);
And if your procedure is a method :
Type
MyMethod = procedure( aString : String) of Object;
See also documentation about Procedural types.
This should do the trick :-
Type
TTCPNotifyProc = Procedure(pData : String) Of Object;
TMyTCPServer = Class
Private
FNotifyProc : TTCPNotifyProc;
..
Public
Procedure SetNotifier(pProc : TTCPNotifyProc);
End;
Procedure TMyTCPServer.SetNotifier(pProc : TTCPNotifyProc);
Begin
FNotifyProc := pProc;
End;
Then whenever you need to call the procedure within your server class just call :-
If Assigned(FNotifyProc) Then
FNotifyProc(DataStringReceived);
If I try to use a closure on an event handler the compiler complains with :
Incompatible types: "method pointer and regular procedure"
which I understand.. but is there a way to use a clouser on method pointers? and how to define if can?
eg :
Button1.Onclick = procedure( sender : tobject ) begin ... end;
Thanks!
#Button1.OnClick := pPointer(Cardinal(pPointer( procedure (sender: tObject)
begin
((sender as TButton).Owner as TForm).Caption := 'Freedom to anonymous methods!'
end )^ ) + $0C)^;
works in Delphi 2010
An excellent question.
As far as I know, it's not possible to do in current version of Delphi. This is much unfortunate since those anonymous procedures would be great to have for quickly setting up an object's event handlers, for example when setting up test fixtures in a xUnit kind of automatic testing framework.
There should be two ways for CodeGear to implement this feature:
1: Allow for creation of anonymous methods. Something like this:
Button1.OnClick := procedure( sender : tobject ) of object begin
...
end;
The problem here is what to put as the self pointer for the anonymous method. One might use the self pointer of the object from which the anonymous method was created, but then one can only create anonymous methods from an object context. A better idea might be to simply create a dummy object behind the scenes to contain the anonymous method.
2: Alternatively, one could allow Event types to accept both methods and procedures, as long as they share the defined signature. In that way you could create the event handler the way you want:
Button1.OnClick := procedure( sender : tobject ) begin
...
end;
In my eyes this is the best solution.
In previous Delphi versions you could use a regular procedure as event handler by adding the hidden self pointer to the parameters and hard typecast it:
procedure MyFakeMethod(_self: pointer; _Sender: TObject);
begin
// do not access _self here! It is not valid
...
end;
...
var
Meth: TMethod;
begin
Meth.Data := nil;
Meth.Code := #MyFakeMethod;
Button1.OnClick := TNotifyEvent(Meth);
end;
I am not sure the above really compiles but it should give you the general idea. I have done this previously and it worked for regular procedures. Since I don't know what code the compiler generates for closures, I cannot say whether this will work for them.
Its easy to extend the below to handle more form event types.
Usage
procedure TForm36.Button2Click(Sender: TObject);
var
Win: TForm;
begin
Win:= TForm.Create(Self);
Win.OnClick:= TEventComponent.NotifyEvent(Win, procedure begin ShowMessage('Hello'); Win.Free; end);
Win.Show;
end;
Code
unit AnonEvents;
interface
uses
SysUtils, Classes;
type
TEventComponent = class(TComponent)
protected
FAnon: TProc;
procedure Notify(Sender: TObject);
class function MakeComponent(const AOwner: TComponent; const AProc: TProc): TEventComponent;
public
class function NotifyEvent(const AOwner: TComponent; const AProc: TProc): TNotifyEvent;
end;
implementation
{ TEventComponent }
class function TEventComponent.MakeComponent(const AOwner: TComponent;
const AProc: TProc): TEventComponent;
begin
Result:= TEventComponent.Create(AOwner);
Result.FAnon:= AProc;
end;
procedure TEventComponent.Notify(Sender: TObject);
begin
FAnon();
end;
class function TEventComponent.NotifyEvent(const AOwner: TComponent;
const AProc: TProc): TNotifyEvent;
begin
Result:= MakeComponent(AOwner, AProc).Notify;
end;
end.