What is the difference between
TFuncOfIntToString = reference to function(x: Integer): string;
and
TFuncOfIntToString = function(x: Integer): string of object;
I use the of object
Let us consider the following three type declarations:
TProcedure = procedure;
TMethod = procedure of object;
TAnonMethod = reference to procedure;
These are all very similar to each other. In terms of calling instances of each of these three types, the calling code is identical. The differences arise in what can be assigned to variables of these types.
Procedural types
TProcedure is a procedural type. You can assign to a variable of type TProcedure something of this form:
procedure MyProcedure;
begin
end;
This is a non object-oriented procedure. You cannot assign an instance or class method to a TProcedure variable. However, you can assign a static class method to a TProcedure variable.
Method pointers
TMethod is a method pointer. This is indicated by the presence of of object. When you have a variable of type TMethod you must assign either:
A instance method of an instantiated object, or
A class method.
So you can assign either of these:
procedure TMyClass.MyMethod;
begin
end;
class procedure TMyClass.MyClassMethod;
begin
end;
The big difference between a procedural type and a method pointer is that the latter contains a reference to both code and data. A method pointer is often known as a two-pointer procedural type. A variable that contains a method pointer contains references to the code and the instance/class to call it on.
Consider the following code:
var
instance1, instance2: TMyClass;
method1, method2: TMethod;
....
method1 := instance1.MyMethod;
method2 := instance2.MyMethod;
Now, although method1 and method2 refer to the same piece of code, they are associated with different object instances. So, if we call
method1();
method2();
We are invoking MyMethod on the two distinct instances. That code is equivalent to:
instance1.MyMethod();
instance2.MyMethod();
Anonymous methods
Finally we come to anonymous methods. These are even more general purpose than procedural types and method pointers. You can assign any of the following to a variable defined using the reference to syntax:
A plain non object-oriented procedure.
An instance method of an instantiated class.
A class method.
An anonymous method.
For example:
var
AnonMethod: TAnonMethod;
....
AnonMethod := MyProcedure; // item 1 above
AnonMethod := instance1.MyMethod; // item 2
AnonMethod := TMyClass.MyClassMethod; // item 3
Anonymous methods, item 4 above, are those declared in-line in your code. For example:
var
AnonMethod: TAnonMethod;
....
AnonMethod := procedure
begin
DoSomething;
end;
The biggest benefit of anonymous methods when compared to the procedural types and method pointers is that they allow for variable capture. For example consider the following short program to illustrate:
{$APPTYPE CONSOLE}
program VariableCapture;
type
TMyFunc = reference to function(X: Integer): Integer;
function MakeFunc(Y: Integer): TMyFunc;
begin
Result := function(X: Integer): Integer
begin
Result := X*Y;
end;
end;
var
func1, func2: TMyFunc;
begin
func1 := MakeFunc(3);
func2 := MakeFunc(-42);
Writeln(func1(4));
Writeln(func2(2));
Readln;
end.
This has the following output:
12
-84
The first is anonymous method, the second is ordinary method.
Related
When I'm in a TForm (say, MyForm) that has a procedure oneProc (procedure TMyForm.oneProc), if I do inside any procedure of MyForm something like TThread.Queue(nil, oneProc);, how does it end up that when oneProc() is fired that Self is well initialized to MyForm?
Can you explain why this works?
procedure TMyForm.Button1Click(Sender: TObject);
begin
ProcA(ProcB);
end;
procedure TMyForm.ProcA(const Aproc: Tproc);
begin
Aproc;
end;
procedure TMyForm.ProcB;
begin
showmessage(self.className); // << this show TTMyForm (how?)
end;
First off, TThread.Queue() is overloaded to accept both TThreadMethod (non-anonymous) and TThreadProcedure (anonymous) parameters. Your text description is for code that would call the non-anonymous version. Your code example, on the other hand, is doing something a little different than what your described TThread.Queue() code would be doing. But in any case, to answer your question -
A non-anonymous method pointer actually carries 2 values within it - one is the address of the method, and one is the value passed to the method's Self parameter (a method pointer is represented by the TMethod record).
TProc is a reference to an anonymous method, and a non-anonymous method pointer can be assigned to an anonymous method reference. The documentation even says so, in the section on "Using Anonymous Methods":
Method references can also be assigned to methods as well as anonymous methods. For example:
type
TMethRef = reference to procedure(x: Integer);
TMyClass = class
procedure Method(x: Integer);
end;
var
m: TMethRef;
i: TMyClass;
begin
// ...
m := i.Method; //assigning to method reference
end;
However, the converse is not true: you cannot assign an anonymous method to a regular method pointer. Method references are managed types, but method pointers are unmanaged types. Thus, for type-safety reasons, assigning method references to method pointers is not supported. For instance, events are method pointer-valued properties, so you cannot use an anonymous method for an event. See the section on variable binding for more information on this restriction.
Internally, an anonymous method reference is implemented as a compiler-generated reference-counted interface with a single Invoke() method. When you write an anonymous method in code, the compiler generates a hidden class that implements Invoke() with that code. Any variables that the anonymous method captures are stored as members of that class.
When assigning a non-anonymous method pointer to an anonymous method reference, the compiler generates a class that captures that method pointer and then calls it inside of the generated Invoke().
So, given the code you have shown, the compiler would translate it into something roughly like the following (I'm leaving out implementation details that are not relevant):
type
//TProc = reference to procedure;
TProc_Intf = interface
procedure Invoke;
end;
TProc_Generated = class(TInterfacedObject, TProc_Intf)
FProc: procedure of object; // type of TMyForm.ProcB()
procedure Invoke;
end;
procedure TProc_Generated.Invoke;
begin
FProc; // <-- calls FProc.Code with FProc.Data as Self!
end;
procedure TMyForm.Button1Click(Sender: TObject);
var
Intf: TProc_Intf;
begin
//ProcA(ProcB);
Intf := TProc_Generated.Create;
//TProc_Generated(Intf).FProc := #ProcB;
TMethod(TProc_Generated(Intf).FProc).Code := Addr(ProcB);
TMethod(TProc_Generated(Intf).FProc).Data := Self; // <-- Self stored here!
ProcA(Intf);
end;
procedure TMyForm.ProcA(const Aproc: {TProc}TProc_Intf);
begin
//Aproc;
Aproc.Invoke;
end;
procedure TMyForm.ProcB;
begin
ShowMessage(Self.ClassName); // <-- Self valid here!
end;
That is how the Self gets from Button1Click() to ProcB() through ProcA().
In my program I do:
var aObj: Tobject;
var aObjClassType: Tclass;
....
aObjClassType := aObj.ClassType;
....
aObj.free;
aObj := nil;
....
showmessage(aObjClassType.Classname);
this work but I m not quite sure If this is correct, especially when i read the function TObject.ClassType
function TObject.ClassType: TClass;
begin
Pointer(Result) := PPointer(Self)^;
end;
So does freeing aObj will not also free the aObjClassType ?
A TClass is a class. A TObject is an instance. So obj.ClassType returns the class, that is the type, of the instance obj.
Note that this is the runtime type of the instance rather than the type of the obj reference variable. This is relevant when using polymorphism. So if you write
var
shape: TShape;
....
shape := TSquare.Create;
Then shape.ClassType returns TSquare even though the shape variable is TShape.
So does freeing aObj will not also free the aObjClassType?
No. Classes are static and created when the module loads and destroyed when the module unloads.
For more detail read the documentation: http://docwiki.embarcadero.com/RADStudio/en/Classes_and_Objects_(Delphi)#TObject_and_TClass
I have method with a parameter as object (sniped code below):
TMyObject=class(TObject)
constructor Create();
destructor Destroy();override;
end;
implementation
function doSomething(x:TMyObject):integer;
begin
//code
end;
procedure test();
var
w:integer;
begin
w:=doSomething(TMyObject.Create);
//here: how to free the created object in line above?
end;
How destroy object created inside of called method doSomething outside of this method?
In order to free the object instance, you need to have a reference to it on which you can call Free().
Since you are creating the object instance in-place as a parameter, the only reference you will have is the one inside of the doSomething() parameter.
You either have to Free it inside of doSomething() (which is practice I would not advise doing):
function doSomething(x: TMyObject): Integer;
begin
try
//code
finally
x.Free;
end;
end;
Or, you need to create an additional variable in test(), pass it to doSomething(), and then Free it after doSomething() returns:
procedure test();
var
w: Integer;
o: TMyObject
begin
o := TMyObject.Create;
try
w := doSomething(o);
finally
o.Free;
end;
end;
While one may think that using a reference counted object would allow you to create the object in-place and let reference counting free the object, this kind of construction may not work because of the following compiler issue:
The compiler should keep a hidden reference when passing freshly created object instances directly as const interface parameters
This is confirmed by a former Embarcadero compiler engineer, Barry Kelly, in a StackOverflow answer:
Should the compiler hint/warn when passing object instances directly as const interface parameters?
I have the following construct:
program Project26;
{$APPTYPE CONSOLE}
{$R *.res}
type
TPrint_address_func = function(offset: integer; info: disassembler_info): boolean;
disassembler_info = record
data: string;
print_address_func: TPrint_address_func;
end;
begin
end.
Obvious either the record of the function-type needs to be declared in a forward declaration.
I know that I cannot declare the record as forward, but...
Is there a way to declare the procedural-variable as forward?
Or can I replace the record with an old-school object and declare that as forward?
You cannot forward declare procedural types, or records. So, the conclusion is that you have to put the type definition inside the record:
type
disassembler_info = record
type
TPrint_address_func = function(info: disassembler_info): boolean;
var
data: string;
print_address_func: TPrint_address_func;
end;
FWIW, once I start defining types inside records, I tend to start breaking the declaration up with visibility specifiers. I'd declare this type like this:
type
disassembler_info = record
public
type
TPrint_address_func = function(info: disassembler_info): boolean;
public
data: string;
print_address_func: TPrint_address_func;
end;
If you pass a record pointer, then this problem is easy to solve, even in Delphi versions that don't support nested record types. Forward-declare a record pointer type, and then declare the function type using the record pointer. Finally, declare the record:
type
PDisassembler_info = ^TDisassembler_info;
TPrint_address_func = function(offset: Integer;
info: PDisassembler_info): Boolean;
TDisassembler_info = record
data: string;
print_address_func: TPrint_address_func;
end;
You're probably going to have more than just one function pointer, and you're probably going to have more than one instance of your record, too. As you extend this pattern, you're ultimately going to re-invent classes. Consider this:
type
TDisassembler_info = class
data: string;
function print_address(offset: Integer): Boolean; virtual; abstract;
end;
Now, instead of defining a free function, you declare a descendant of your class and override the abstract method. This has a few advantages as the number of function pointers and record instances grows:
The compiler automatically fills in the function pointers with all the right values. It stores them in the class's VMT. There's no chance you'll have a null function pointer by accidentally forgetting to assign print_address_func. The compiler will warn if you attempt to instantiate a class without overriding the abstract methods.
It's impossible to accidentally pass the wrong record pointer when you call the function. In your design, calling the function will look like this:
info.print_address_func(offset, info);
It would surely be an error if the record parameter you passed differed from the record whose function you called. With an object, the redundancy and opportunity for error go away:
info.print_address(offset);
No matter how many functions you have, the size of a single instance of the class remains constant because all instances share a single VMT. In your current model, if you have 100 instances of your record, you'll have 100 copies of the same function pointer.
It is possible to solve this with a record helper.
Type
disassembler_info = record
private
FP: Pointer;
public
data: string;
end;
TPrint_address_func = function(info: disassembler_info): boolean;
disassembler_info_helper = record helper for disassembler_info
private
procedure SetAFunc(aF: TPrint_Address_Func);
function GetAFunc: TPrint_Address_Func;
public
property print_address_func: TPrint_address_func read GetAFunc write SetAFunc;
end;
function disassembler_info_helper.GetAFunc: TPrint_Address_Func;
begin
Result := TPrint_address_func(FP);
end;
procedure disassembler_info_helper.SetAFunc(aF: TPrint_Address_Func);
begin
TPrint_address_func(FP) := TPrint_address_func(aF);
end;
function MyFunc(aRec: disassembler_info): boolean;
begin
Result := true;
WriteLn('Hello from MyFunc');
end;
var
aFunc: TPrint_address_func;
aRec:disassembler_info;
begin
aRec.print_address_func := MyFunc;
aFunc := arec.print_address_func;
if aFunc(aRec) then begin
WriteLn('Voila!');
end;
ReadLn;
end.
The helper injects a property of TPrint_address_func with read and write methods that operates on a private variable declared in disassembler_info.
I'd like to pass a method of a class as callback to a WinAPI function. Is this possible and if yes, how?
Example case for setting a timer:
TMyClass = class
public
procedure TimerProc(Wnd:HWND; uMsg:DWORD; idEvent:PDWORD; dwTime:DWORD);
procedure DoIt;
end;
[...]
procedure TMyClass.DoIt;
begin
SetTimer(0, 0, 8, #TimerProc); // <-???- that's what I want to do (last param)
end;
Thanks for your help!
Edit: The goal is to specify a method of this class as callback. No procedure outside the class.
Edit2: I appreciate all your help but as long as the method has no "TMyClass." in front of its name it is not what I am searching for. I used to do it this way but wondered if could stay fully in the object oriented world. Pointer magic welcome.
Madshi has a MethodToProcedure procedure. It's in the "madTools.pas" which is in the "madBasic" package. If you use it, you should change the calling convention for "TimerProc" to stdcall and DoIt procedure would become,
TMyClass = class
private
Timer: UINT;
SetTimerProc: Pointer;
[...]
procedure TMyClass.DoIt;
begin
SetTimerProc := MethodToProcedure(Self, #TMyClass.TimerProc);
Timer := SetTimer(0, 0, 8, SetTimerProc);
end;
// After "KillTimer(0, Timer)" is called call:
// VirtualFree(SetTimerProc, 0, MEM_RELEASE);
I've never tried but I think one could also try to duplicate the code in the "classses.MakeObjectInstance" for passing other procedure types than TWndMethod.
Which version of Delphi are you using?
In recent ones you can use static class methods for this:
TMyClass = class
public
class procedure TimerProc(Wnd:HWND; uMsg:DWORD; idEvent:PDWORD; dwTime:DWORD); stdcall; static;
procedure DoIt;
end;
[...]
procedure TMyClass.DoIt;
begin
SetTimer(0, 0, 8, #TimerProc);
end;
The TimerProc procedure should be a standard procedure, not a method pointer.
A method pointer is really a pair of
pointers; the first stores the address
of a method, and the second stores a
reference to the object the method
belongs to
Edit
This might be as much OOP as you are going to get it. All the nasty stuff is hidden from anyone using your TMyClass.
unit Unit2;
interface
type
TMyClass = class
private
FTimerID: Integer;
FPrivateValue: Boolean;
public
constructor Create;
destructor Destroy; override;
procedure DoIt;
end;
implementation
uses
Windows, Classes;
var
ClassList: TList;
constructor TMyClass.Create;
begin
inherited Create;
ClassList.Add(Self);
end;
destructor TMyClass.Destroy;
var
I: Integer;
begin
I := ClassList.IndexOf(Self);
if I <> -1 then
ClassList.Delete(I);
inherited;
end;
procedure TimerProc(Wnd:HWND; uMsg:DWORD; idEvent:PDWORD; dwTime:DWORD); stdcall;
var
I: Integer;
myClass: TMyClass;
begin
for I := 0 to Pred(ClassList.Count) do
begin
myClass := TMyClass(ClassList[I]);
if myClass.FTimerID = Integer(idEvent) then
myClass.FPrivateValue := True;
end;
end;
procedure TMyClass.DoIt;
begin
FTimerID := SetTimer(0, 0, 8, #TimerProc); // <-???- that's what I want to do (last param)
end;
initialization
ClassList := TList.Create;
finalization
ClassList.Free;
end.
Edit: (as mentioned by glob)
Don't forget to add the stdcall calling convention.
Response to your second edit:
If you want a reply that includes a pointer to a TMyClass instance, you may be out of luck. Fundamentally, the procedure Windows will call has a certain signature and is not an object method. You cannot directly work around that, not even with __closure or procedure of object magic, except as described below and in other answers. Why?
Windows has no knowledge of it being an object method, and wants to call a procedure with a specific signature.
The pointer is no longer a simple pointer - it has two halves, the object instance and the method. It needs to save the Self, as well as the method.
By the way, I don't understand what is wrong with a short dip outside the object-oriented world. Non-OO code is not necessarily dirty if used well.
Original, pre-your-edit answer:
It's not possible exactly as you are trying to do it. The method that SetTimer wants must exactly follow the TIMERPROC signature - see the MSDN documentation. This is a simple, non-object procedure.
However, the method TMyClass.DoIt is an object method. It actually has two parts: the object on which it is called, and the method itself. In Delphi, this is a "procedure of object" or a "closure" (read about procedural types here). So, the signatures are not compatible, and you cannot store the object instance, which you need in order to call an object method. (There are also calling convention problems - standard Delphi methods are implemented using the fastcall convention, whereas TIMERPROC specifies CALLBACK which, from memory, is a macro that expands to stdcall. Read more about calling conventions and especially fastcall.)
So, what do you do? You need to map your non-object-oriented callback into object-oriented code.
There are several ways, and the simplest is this:
If you only have one timer ever, then you know that when your timer callback is called it is that specific timer that fired. Save a method pointer in a variable that is of type procedure of object with the appropriate signature. See the Embarcadero documentation link above for more details. It will probably look like:
type TMyObjectProc = procedure of object;
var pfMyProc : TMyObjectProc;
Then, initialise pfMyProc to nil. In TMyClass.DoIt, set pfMyProc to #DoIt - that is, it is now pointing at the DoIt procedure in the context of that specific TMyClass instantiation. Your callback can then call that method.
(If you're interested, class variables that are of a procedural type like this are how event handlers are stored internally. The OnFoo properties of a VCL object are pointers to object procedures.)
Unfortunately this procedural architecture is not object-oriented, but it's how it has to be done.
Here's what some full code might look like (I'm not at a compiler, so it may not work as written, but it should be close):
type TMyObjectProc = procedure of object;
var pfMyProc : TMyObjectProc;
initialization
pfMyProc = nil;
procedure MyTimerCallback(hWnd : HWND; uMsg : DWORD; idEvent : PDWORD; dwTime : DWORD); stdcall;
begin
if Assigned(pfMyProc) then begin
pfMyProc(); // Calls DoIt, for the object that set the timer
pfMyProc = nil;
end;
end;
procedure TMyClass.MyOOCallback;
begin
// Handle your callback here
end;
procedure TMyClass.DoIt;
begin
pfMyProc = #MyOOCallback;
SetTimer(0, 0, 8, # MyTimerCallback);
end;
Another way would be to take advantage of the fact your timer has a unique ID. Save a mapping between the timer ID and the the object. In the callback, convert from the ID to the pointer, and call the object's method.
Edit: I've noticed a comment to another answer suggesting using the address of your object as the timer ID. This will work, but is a potentially dangerous hack if you end up having two objects at the same address at different times, and you don't call KillTimer. I've used that method but don't personally like it - I think the extra bookkeeping of keeping a (timer ID, object pointer) map is better. It really comes down to personal style, though.
I've used MakeObjectInstance a few times to do the same.
Here's an article on the subject:
How to use a VCL class member-function as a Win32 callback
TMyClass = class
public
procedure DoIt;
procedure DoOnTimerViaMethod;
end;
var MyReceiverObject: TMyClass;
[...]
procedure TimerProc(Wnd:HWND; uMsg:DWORD; idEvent:PDWORD; dwTime:DWORD); stdcall:
begin
if Assigned(MyReceiverObject) then
MyReceiverObject.DoOnTimerViaMethod;
end;
procedure TMyClass.DoIt;
begin
MyReceiverObject := Self;
SetTimer(0, 0, 8, #TimerProc); // <-???- that's what I want to do (last param)
end;
Not perfect. Watch for the threads, variable overwriting etc. But it does the job.