Delphi> Please explain this: type... procedure of object - delphi

I've encountered some code that's new to me...
I've never really seen a type declaration of a procedure of object, and I just don't
see the point.
Why couldn't the developer simply keep a field of type Boolean?
interface
type
TFinishedCaptureEvent = procedure(AFinished: Boolean) of object;
TFrameCard = class(TFrame)
...
private
FOnFinishedCapture: TFinishedCaptureEvent;
procedure DoUpdateMessage(AMessageType: TMessageType);
public
property OnFinishedCapture: TFinishedCaptureEvent read FOnFinishedCapture write FOnFinishedCapture;
end;
implementation
...
procedure TFrameCard.DoUpdateMessage(AMessageType: TMessageType);
begin
if Assigned(FOnFinishedCapture) then
FOnFinishedCapture(False);
...
end;
end.

Let's break this down into two parts to be easier to understand. First, procedure(AFinished: Boolean) isn't a boolean variable, it's a reference to a procedure that takes a boolean as a parameter. It's basically a procedure header, except without the procedure name because this is just a type definition. Any procedure that matches this signature can be assigned to this variable.
The of object part means that this isn't just a procedure reference, but a method reference; it has to belong to an object. The compiler needs to know the difference so that it can store the self pointer for the object together with the procedure pointer so it can be invoked properly, as the other posters have pointed out.
Basically, this is declaring a new event handler, and it's a pretty common pattern in Delphi. It's the same thing that the VCL does all over the place. When you create a button and assign an OnClick handler, it has to be a procedure (Sender: TObject) of object;. Your form gives the button a method reference referring to itself and the event handler procedure, and then the button uses that information to invoke the handler on the form when someone clicks it.
This code is doing the same thing. It's providing a way for some external object to be notified when DoUpdateMessage runs, using the standard Delphi idiom for event notification.

A procedure of object is a procedure reference for procedures contained in class instances. When calling procedures that are members of a class, the implict Self reference must be passed with the other parameters. Using procedure of object tells the compiler to store the Self reference with the procedure address inside the procedure reference, so that when the procedure is called via the procedure reference, the Self reference will be automatically passed.
In the code snippet you provided, TFinishedCaptureEvent is defined as a procedure of object, meaning that any variables created of its type will contain 2 values: the Self value and the procedure address. When this variable is assigned to, in particular when the assignment is inside a class, the compiler will automatically set the Self value inside this variable to the instance of the class that contains the procedure being assigned to the variable. When the variable is called (FOnFinishedCapture(False)), the compiler automatically passes the correct Self value back to the procedure that was assigned to this variable.

I don't understand how you relate this to a field of boolean.
But TFinishedCaptureEvent = procedure(AFinished: Boolean) of object declares a delegate/method pointer type, which is used for events. It's a record which contains a self pointer and a function pointer. When you call the delegate, the function is called with the self passed as a parameter to the function.

Related

Are open array parameters, classes and strings passed by reference?

I have read on SO that open array parameters are passed by reference automatically. Is this the same thing for classes and strings or I have to use the var?
I refer to the fact that I want to pass to a function (or procedure) a parameter by reference. Like in C++ I always pass std::string& for example, it is explicit otherwise I pass a copy
Your statement about open arrays is not correct. There are three forms of open array parameter:
procedure ByValue(arr: array of Integer);
procedure ConstParam(const arr: array of Integer);
procedure VarParam(var arr: array of Integer);
The final two forms, var and const, pass a reference to the array. However, the first, passing by value, passes a copy. If you want to avoid making a copy, then you should use either a var or const parameter. Choose between these depending on whether or not you wish the caller's array to be modified.
As for classes and strings, these types are reference types. Since they are already references, when you pass one to a procedure, you are passing the reference.
Let's look at the various possibilities for classes:
procedure ByValue(obj: TObject);
procedure ConstParam(const obj: TObject);
procedure VarParam(var obj: TObject);
procedure OutParam(out obj: TObject);
For ByValue and ConstParam, the reference is passed directly. The difference between the two is that in the implementation of ConstParam, the reference cannot be modified. It can in ByValue. The caller cannot see that modification. Of course, you can always call methods on obj that modify the state of the object. In practice there's little point in using const for a reference type.
For VarParam and OutParam, the address of the reference is passed. Thus the procedure receives a pointer to a pointer to the object. All of the pointer handling is hidden from you. But this is what allows you to modify the reference and have the caller see that modification.
Strings are also reference types, and so are handled similarly.

Delphi Use calling objects properties in added class method

I want to add a method to an existing Delphi class. I think basic class framework OK but need to access some properties of the object that called my method in my method. I can't seem to get anything to work.
old class TStringGrid
new class OptStringGrid where myNewMethod is referenced
//example of new class method
procedure myNewMethod (const Name: string);
begin
//Here is my question location.
// I would like to access properties of the calling object in this case
// testgrid. Like... i:= testgrid.rowcount;
end
// Unit Calling statements
var
testGrid : OptStringGrid;
i: integer;
begin
i := testgrid.myNewMethod(strName);
end;
New to Delphi, forgive my terminology if wrong please. I know example code not compilable. I'm looking for techniques to access the properties as described.
To access members of the object whose method is executing, you can use the Self variable. It's automatically declared and assigned inside any method body. In fact, its use is usually implicit — any members of the object are automatically in scope inside the method body. You generally only need to qualify member access with Self when there is already some other variable in the method that has the same name as the member you wish to use.
The key thing about implementing methods is that you need to make sure they're actually methods. The code shown in the question does not define myNewMethod as a method. Rather, it's a standalone subroutine. Only methods can be called on objects, and therefore only methods can have access to the objects they're called on.
A method declaration appears inside a class declaration. Yours might look something like this:
type
TOptStringGrid = class(TStringGrid)
public
function myNewMethod(const Name: string): Integer;
end;
A method definition appears in the implementation section of your unit, along with all your other subroutine bodies, just like all the event-handler implementations the IDE creates for you when you double-click events in the Object Inspector. Those are just ordinary methods.
What distinguishes a method implementation from an implementation of some other kind of subroutine is that the method name includes the name of the class it belongs to:
function TOptStringGrid.myNewMethod(const Name: string): Integer;
begin
// ...
end;
Observe the TOptStringGrid. portion in the code above. That's how the compiler knows that the method body belongs to that class and not anything else named myNewMethod.
Within that method body, you can access all the published, public, and protected members of the ancestor class, TStringGrid, including the RowCount property.

How to free an object which is in a record?

Here I have a tricky situation, I guess. I need to be able to free an object which is a field of a record. I would normally write the cleanup code in the destructor, if it was a class. But since record types can't introduce a "destructor", how would it be possible to call TObject(Field).Free; ?
There'll be two types of usage I predict:
Replacing the record with a new one.
I think this usage would be easy to implement. Since records are value types and so they are copied on assignment, I can overload the assigning operator and free the objects owned by old record.
( Edit: Assignment overloading wasn't able. That's a new info to me.. )
Exiting the scope where record variable defined.
I can think of a private method that frees the objects and this method could be called on scope excitation manually. BUT, here is the same question: How to make it more recordly? This behaviour kind of feels like a class...
Here is a sample (and obviously not the intended usage):
TProperties = record
... some other spesific typed fields: Integers, pointers etc..
FBaseData: Pointer;
FAdditionalData: TList<Pointer>;
//FAdditionalData: array of Pointer; this was the first intended definition
end;
Assume,
FAdditionalData:=TList<Pointer>.Crete;
called in record constructor or manually in record variable scope by accessing the field publicly like
procedure TFormX.ButtonXClick(Sender: TObject);
var
rec: TProperties;
begin
//rec:=TProperties.Create(with some parameters);
rec.FAdditionalData:=TList<Pointer>.Create;
//do some work with rec
end;
After exiting the ButtonClick scope the rec is no more but a TList still keeps its existance which causes to memory leaks...
If all you have in the record is an object reference, then you can't get the compiler to help you. You are in sole charge of the lifetime of that object. You cannot overload the assignment operator, and you don't get any notification of scope finalisation.
What you can do though is to add a guard interface that will manage the lifetime of the object.
TMyRecord = record
obj: TMyObject;
guard: IInterface;
end;
You need to make sure that TMyObject manages its lifetime by reference counting. For example by deriving from TInterfacedObject.
When you initialise the record you do this:
rec.obj := TMyObject.Create;
rec.guard := rec.obj;
At this point, the guard field of the record will now manage your object's lifetime.
In fact, if you want to push this idea further, you can build a dedicated class to guard the lifetime of objects. That then no longer constrains you to implement IInterface on your class. There are plenty of examples on the web that illustrate the technique. For example I offer Jarrod Hollingworth's article titled Smart Pointers, and Barry Kelly's titled Reference-counted pointers, revisited. There are many more out there. It's an old trick!
Note however, that what you have here is a strange hybrid of value type and reference type. On the face of it, records are value types. However, this one acts like a reference type. If you have other fields in the record that are value types then that would be even more confusing. You'll need to be very aware of this issue when you work with such a record.
On the face of it, without knowing more about your design, I'd be inclined to advise you not to put object references in records. They fit better inside reference types, i.e. classes.
I remember that someone created a class named TLifetimeWatcher. Basically, it looks like:
TLifetimeWatcher = class(TInterfacedObject)
private
fInstance: TObject;
fProc: TProc;
public
constructor Create(instance: TObject); overload;
constructor Create(instance: TObject; proc: TProc); overload;
destructor Destroy; override;
end;
// The (cleanup) proc will be executed in the destructor if assigned, otherwise the instance will be freed by invoking the Free method.
https://docwiki.embarcadero.com/RADStudio/Sydney/en/Custom_Managed_Records
THeaderStruct = record
private
public
class operator Initialize (out Header: THeaderStruct);
class operator Finalize (var Header: THeaderStruct);
end;

Passing methods as parameters on a deserialized form with no ClassType

I'm effectively trying to deserialize a form.
One of the objects on the serialized form has a method which takes a series of events as parameters.
Now since I don't have the class type of the object when I'm deserializing, I have a method on the object doing the deserialization called AddMethod which is declared like this:
procedure TMyDeserializer.AddMethod(ControlName, EventName: String;
MethodAddr: Pointer);
var
TargetControl : TControl;
Method : TMethod;
begin
if Not Assigned(TempForm) then
Exit;
if TempForm.Name = ControlName then
TargetControl := TempForm
else
TargetControl := TempForm.FindChildControl(ControlName);
if Assigned(TargetControl) then
begin
Method.Code := MethodAddr;
Method.Data := TargetControl;
SetMethodProp(TargetControl, EventName, Method);
end;
end;
So that I can poke subroutines into the various controls as I deserialize them, The problem is I need to add events as a list of parameters (not to a control). e.g.
SetUpEvents(EventHandler1:TNotifyEvent;EventHandler2:TNotifyEvent);
Where EventHandler1 and EventHandler2 are defined somewhere in code as
Procedure EventHandler1(Sender:TNotifyEvent);
begin
// Do something
end;
These are not methods but stand alone subroutines.
When I'm assigning these to objects the subroutine doesn't need to be part of an object as the AddMethod procedure handles it with a call like
MyDeserializerInstance.AddMethod('Button1','OnClick',#EventHandler1);
This works for standard event handlers, such as Button1.OnClick but not if I want to do
Procedure SetUpButton1Click(Method: TNotifyEvent)
begin
TButton(MyDeserializerInstance.TempForm.FindChildControl('Button1')).OnClick = Method;
end;
The problem is I can't pass the subroutine as a method to the example Set Up Procedure.
The form being created isn't declared in an interface and is entirely defined by the file it is read from as well as a few stand alone routines in code.
So I suppose the question is how do turn a subroutine into a method at run time (after creating the object it is supposed to be part of), and if I can't do that how do I pass the subroutines in code as parameters in another method?
So far I've tried casting a TMethod as the correct event type and filling in the .Data as the TempForm. It called the correct method but it scrambled the parameters.
Delphi version is 2007
Non-static class methods have a hidden Self input parameter that is filled in when the method is called. That is what the TMethod.Data field corresponds to. In order to use a standalone procedure as a handler for an event that expects a class method, the procedure must have an extra parameter defined to represent the Self parameter so the value of TMethod.Data has somewhere to go, ie:
procedure Button1ClickHandler(Self: Pointer; Sender: TObject);
begin
// Do something
end;
MyDeserializerInstance.AddMethod('Button1', 'OnClick', #Button1ClickHandler);
Your AddMethod() implementation is assigning the TargetControl as the TMethod.Data value, so the Self and Sender parameters above will end up pointing at the same object at runtime, but that is OK.
Without the explicit Self parameter defined, that explains why your parameters are getting "scrambled" when the procedure called at runtime. The hidden Self value is being assigned to the Sender parameter, and the real Sender value is being ignored.
I'm sure someone will correct me if I'm wrong but I don't believe there is a way to create a type definition at runtime in native Delphi. Delphi's RTTI just doesn't handle this yet.
The two scenarios that come to mind for object serialization are persistence and IPC. (There may be more that I haven't thought of).
Delphi's DFM serialization would be an example of persistence. If you look at a dfm you'll notice it isn't defining methods at all. It's simply assigning event handlers to properties expecting an event handler. Both the handlers and the properties are defined at design time using normal type definitions.
If your intent is IPC(whether on the same machine or a remote one) there are already existing frameworks for accomplishing this. (RemObjects comes to mind)
You don't "make a method" at run time. That would amount to compiling new code. The methods that you assign to various event properties need to already exist.
Furthermore, you can't "add events." The object you're deserializing already has events. You defined them when you wrote the class declaration in your Delphi code. You can't add new event properties to a class after it's been compiled.
It appears that what you're really saying is that you have a standalone procedure that you happen to have named Method1, and you want to pass it as a TNotifyEvent parameter when you call SetUpMethods.
That's the wrong way to go. That Method1 procedure isn't a method, despite its name, so you mustn't use it where a method is required. Change that declaration so it belongs to a class, and then it will be a method.
If you don't want to have to instantiate the class that the method belongs to, that's fine — you can declare it as a class method instead:
class procedure TSomeClass.Method1(Sender: TNotifyEvent);
I encourage you to change the declaration of AddMethod so that the last parameter is of type TMethod. Then you're sure to have both the code and data portions of the method pointer. Right now, you're assigning the data portion based on the object whose event property you're assigning, but as I mentioned in my comment, it's rare for that relationship to exist, especially now that the method belongs to an entirely unrelated class (TSomeClass in my example). The value of the TMethod.Data field becomes the Self value when the method gets called. It's your responsibility to ensure that the value you store in that field is of a compatible type for the class the code belongs to.

how to safely bypass Delphi Error: "types of formal and actual parameters must be identical"

I need a way to write a generic procedure to act upon an object type or any of its descendants.
My first attempt was to declare
procedure TotalDestroy(var obj:TMyObject);
but when using it with a descendant object
type TMyNewerObject = class(TMyObject);
var someNewerObject: TMyNewerObject;
TotalDestroy(someNewerObject);
I get the infamous error "types of formal and actual parameters must be identical"
So, while strugling to find a solution, I looked at the source code of Delphi system FreeAndNil procedure. And I found this awesome declaration, along with this astonishing comment
{ FreeAndNil frees the given TObject instance and
sets the variable reference to nil.
Be careful to only pass TObjects to this routine. }
procedure FreeAndNil(var Obj);
It avoids the type checking error, but it uses no safety net.
My question is ... is there any safe way to check the type of an untyped var parameter?
or in other words, can you improve this Delphi source code so that the warning would not be needed?
procedure FreeAndNil(var Obj);
var
Temp: TObject;
begin
Temp := TObject(Obj);
Pointer(Obj) := nil;
Temp.Free;
end;
Let's examine what you want to do.
You want to call a method that takes X, passing in an object of type Y, where Y is a descendant of X. The snag, the parameter is a "var" parameter.
Let's analyze what you could do if that was possible.
type
TBase = class
end;
TDescendant = class(TBase)
end;
procedure Fiddle(var x: TBase);
begin
x := TDescendant.Create;
end;
type
TOtherDescendant = class(TBase)
end;
var a: TOtherDescendant;
a := TOtherDescendant.Create;
Fiddle(a);
Uh-oh, now a no longer contains an instance of TOtherDescendant, it contains an instance of TDescendant. That probably comes as a surprise to the code that follows the call.
You must not only consider what you intend to do with the syntax you propose, but effectively what you could do with the syntax.
You should read Eric Lipperts excellent blog post about similar issues in .NET, found here: Why do ref and out parameters not allow type variation?.
I've written about this before, using an example very similar to Lasse's:
Delphi Q&A: Why must the types of actual and formal var parameters be identical?
Unless you're writing an assignment statement to change the value of the input parameter itself, and not just one of its properties, you shouldn't pass a parameter by reference in the first place.
If you are writing an assignment statement to change the parameter's value, then the compiler message really is true, and you should heed it.
One reason for needing to by-pass the error is when you're writing a function like TApplication.CreateForm. Its job is to change the input parameter's value, and the type of the new value varies and cannot be determined at compile time. If you're writing such a function, then your only option with Delphi is to use an untyped var parameter, and then there is extra burden on both the caller and the receiver to make sure everything goes right. The caller needs to make sure it passes a variable that is capable of holding values of whatever type the function will put in it, and the function needs to make sure it stores a value of a type compatible with what the caller requested.
In the case of CreateForm, the caller passes in a class-reference literal and a variable of that class type. The function instantiates the class and stores the reference in the variable.
I don't think very highly of either CreateForm or FreeAndNil, largely because of the way their untyped parameters sacrifice type safety in return for comparatively little extra convenience. You haven't shown the implementation of your TotalDestroy function, but I suspect its var parameter will ultimately provide the same low utility as in those other two functions. See my articles on both:
When should I use FreeAndNil?
Why shouldn't I call Application.CreateForm?
In addition to what Lasse wrote, which is quite correct, most of the time you don't want to pass an object to a var parameter anyway.
An object is a reference type. What you see as the object is actually a reference to it. You would only want to pass an object reference to a var parameter if you wanted to change your object out for a new object. If you just want to be able to modify the members of the object, then you can do that by simply passing it to a normal parameter. Make method call take a TMyObject parameter instead of a var TMyObject parameter and it should work.
Of course, if you really are replacing the object, then feel free to disregard all this, and see Lasse's answer.
can you improve this Delphi source code so that the warning would not be needed?
Yes, you can get a type safe way to avoid the compiler error.
In the newest Delphi 10.4 Sidney, the FreeAndNil procedure has been changed into this:
procedure FreeAndNil(const [ref] Obj: TObject);
var
Temp: TObject;
begin
Temp := Obj;
TObject(Pointer(#Obj)^) := nil;
Temp.Free;
end;
It is type safe for objects and will catch errors when passing an interface reference for example.
The way to pass a parameter by const [ref] means that the parameter is passed by reference. Without the [ref] attribute, parameters with size equal and smaller than a pointer would otherwise be passed by value.
Here, even though the object is passed as a constant, the reference will be modified.
In that sense, it is not a perfect declaration, but will do its job better than the former implementation.
From New features in Delphi 10.4:
This means that incorrect usage of FreeAndNil will now cause a compiler error. In the past, incorrect usage would not be caught, leading to difficult bugs. Note that although the parameter is declared as const, the by-reference variable is indeed modified.
A new, but ‘not as bad’, class of incorrect calling is possible with this declaration of FreeAndNil: the method can be called passing in properties or a method result, as well as cast expressions, a type’s implicit conversion to TObject, etc. The nil-ed value will then be the temporary variable in the expression.

Resources