Does anyone here know how Delphi represents a reference to procedure?
for example
var
proc: TProc;
...
proc = procedure begin beep end;
What do we got in "proc"?
I know that for "method variable" the memory representation is 4 bytes for the "procedure address" followed by 4 bytes for the "object address", but for "reference to procedure" is somewhat different and I cannot quite figure it out.
The reason I want this is because I have some legacy code that I want to make it work with references.
Does anyone know something about it?
Method references are implemented as a COM-style interface with a single method called Invoke, which has the same signature as the method reference.
So TProc looks like this:
type
TProc = interface(IInterface) // so inherits QI, AddRef, Release
procedure Invoke;
end;
It's a valid question to ask, as Delphi has interoperability with the C++ product. By using a pre-existing reference-counted type and idiom (COM lifetime rules), interop with C++ at the method reference level is possible.
Anonymous methods generate a hidden class which implements an interface isomorphic to the method reference interface, i.e. exactly the same shape, but not with the same symbolic identity. The hidden class doesn't implement the method reference interface directly because it may need to implement the interface multiple times (a single block may contain multiple anonymous methods all assigned to locations of the same method reference type).
Related
I am having some difficulty understanding typecasting when using a class that is a passed parameter. I tried searching for this but couldn't find other answers.
I am working with some legacy Delphi code, using Delphi 2006, which doesn't support Generics (introduced in Delphi 2009).
The code is using TLists to store pointers to instantiated classes of particular types. When clearing the list, they use this:
procedure ClearList(AList: TList);
var i: Integer;
begin
for i := 0 to AList.Count - 1 do
TObject(AList[i]).Free;
AList.Clear;
end;
And it is called like this:
ClearList(FExtraVisitTypes);
ClearList(FDiagnoses);
ClearList(FProcedures);
ClearList(FImmunizations);
ClearList(FSkinTests);
ClearList(FPatientEds);
ClearList(FHealthFactors);
ClearList(FExams);
My understanding of this may be off, but I am concerned that if the pointed-to objects are freed as TObject, that the destructor of the descendant object won't be called, potentially leading to a memory leak. (My polymorphisim kung-fu is a bit rusty, which may be causing my confusion.)
So I tried to change the clear function as below:
procedure ClearList(AList: TList; ItemClass: TPCEItemClass); //mod to add ItemClass
var i: Integer;
begin
for i := 0 to AList.Count - 1 do begin
(AList[i] as ItemClass).Free;
end;
AList.Clear;
end;
TPCEItemClass is defined like this:
TPCEItemClass = class of TPCEItem;
I then changed the clear calls like this:
ClearList(FExtraVisitTypes, TPCEProc);
ClearList(FDiagnoses, TPCEDiag);
ClearList(FProcedures, TPCEProc);
ClearList(FImmunizations, TPCEImm);
ClearList(FSkinTests, TPCESkin);
ClearList(FPatientEds, TPCEPat);
ClearList(FHealthFactors, TPCEHealth);
ClearList(FExams, TPCEExams);
But the compiler won't allow this and gives this error:
[Pascal Error] uPCE.pas(1730): E2015 Operator not applicable to this operand type
For this erroneous line:
(AList[i] as ItemClass).Free;
Questions:
Does the original way of coding, where the item is freed by simply calling the great-great-great (etc) ancestor Free method end up effecting the descendant's destructor method? As I write this, I'm now thinking that it actually does. But I don't know why. So any answers to help me keep this in my head would be great.
Why does my method of trying to typecast via the parameter which is of type class not work? Is this just not allowed? Or is my syntax wrong? Is there another way to do this?
Am I going about this all wrong? Is there a better way?
Thanks
I am concerned that if the pointed-to objects are freed as TObject, that the destructor of the descendant object won't be called, potentially leading to a memory leak.
That is not the case for classes that are properly implemented.
All classes derive from TObject. TObject.Free() calls the TObject.Destroy() destructor, which is virtual. Any descendant that requires destruction logic must override that destructor (if it doesn't, it has a flaw that needs fixing).
So, in properly written code, the original code will work perfectly fine as shown. Calling Free() on any valid and correctly implemented object will invoke its most-derived destructor.
Now, that being said, there have been plenty of cases over the years of people forgetting to override the destructor when their classes require it, thus causing the kinds of memory leaks you are worried about. So, make sure you pay attention to what your classes are doing, and you will be fine.
So I tried to change the clear function as below ... But the compiler won't allow this and gives this error
Correct, because you can't perform a type-cast on an object using a variable to a metaclass type, like you are trying to do. Type-casts require the target type to be specified at compile-time, but metaclass variables are not assigned until runtime.
Does the original way of coding, where the item is freed by simply calling the great-great-great (etc) ancestor Free method end up effecting the descendant's destructor method?
The original code will work just fine 99% of the time, yes. Most Delphi coders are good about override'ing the destructor when it is appropriate. But that other 1% is only when you are dealing with classes that are not implemented correctly, in which case it is their author's responsibility to fix them, not your responsibility to fix the code that is calling Free() on them.
As I write this, I'm now thinking that it actually does. But I don't know why.
Polymorphic dispatch of the virtual destructor, just like when calling any other virtual method.
Why does my method of trying to typecast via the parameter which is of type class not work? Is this just not allowed?
Correct. It is illegal.
Is there another way to do this?
No (well, yes, but it involves walking an object's class structure's manually at runtime, but that requires a deep understanding of how the compiler lays out objects in memory, so I'm not going to get into that here).
I created a simple class to explain my problem:
ttest =class
private
val:boolean;
published
function get:boolean;
end;
...
function ttest.get: boolean;
begin
val:=not val;
result:=val;
end;
Now if I declare a local ttest variable and call my_var.get; then everything works, but if I declare it as a global variable then it can't access the val field anymore, it shows an error message which says "Access violation...".
I read some articles about classes in Delphi but still can't find my mistake.
You've neglected to instantiate the class.
Global class-reference variables are initialized to nil, whereas local variables are not initialized at all. The local variable has a value determined by whatever happened to be on the stack at the time you called your function, and your program is interpreting that value as though it were a TTest reference even though it's really not. Your program then reads the value at that memory address to get the value that would represent the val field.
The only reason your code appears to work with a non-global variable is luck. Whether it's good luck or bad is another matter. (Good luck, since your code appeared to work, and working code is always nice. Bad luck, since you'd have been alerted to your mistake earlier if your code had crashed.)
Instantiate a class before you use references to it.
x := TTest.Create;
Now you can access fields, methods, and properties of the object via the x variable.
You should have gotten a compiler warning when you attempted to use a local variable without assigning a value to it first. Although they're just warnings, and your program will still run, never ignore a warning or even a hint. When the compiler bothers to complain about something, it's usually right.
In Delphi object variables are always pointers. Before you can use the variable you need to initialize it with a reference to an object. The most common way to do that is to create a new object of the particular class.
procedure Foo;
var
Obj: TObject;
begin
Obj := TObject.Create;
try
// Do stuff with Obj
finally
Obj.Free;
end;
end;
In this case Obj starts out as an uninitialized pointer (it will point to random memory). It is only after we assign the newly created TObject that Obj is a valid object reference.
In Delphi there is no automatic garbage collection for objects, so you always need to call free on them when you are done using them. If you declare a global or local object variable, you can initialize it the special initialization section of the unit and free the object in the finalization section.
unit myunit;
interface
var
Obj: TObject;
implementation
initialization
Obj := TObject.Create;
finalization
Obj.Free;
end.
Variables declared in the interface section are globally visible, variables declared in the implementation section are only visible inside the unit. It should be noted that declaring a global object variable means that any unit can overwrite the variable with a reference to a new object without freeing the existing object first. This would cause a memory leak as again there is no automatic garbage collection.
A delphi class is basically just a description, not the object itself. You describe the properties and methods the final object should have. And the missing piece of the puzzle is that you havent really told Delphi to create an object from your class.
This is done by calling the constructor:
mMyInstance:=TTest.Create;
The constructor takes the class description and builds an object instance for you in memory. It returns a pointer to the object which you must store in a variable (myInstance in the above example) of the same type.
Reading your question, I suspect you want to create an object that is "always there", a bit like the printer object. This is easy to do, but just like the printer object - you must include that unit before you can access the object. I think Anders E. Andersen above has shown how most people would initialize an object from a unit centric point of view.
If you want the object to be reachable from another unit, say your mainform or any other unit, first add "myunit" to the uses list. Then to make it visible you add a function, like this:
function test:ttest;
Begin
result:=obj;
end;
And remember to add "function test:TTest" to the interface section of the unit. Then you can use the object from another unit as such:
myUnit.test.get;
But be warned! This is pretty old school programming, and you run the risk of your unit being released (which calls finalization and thus destroys your object) before the other units are done with it. Thus you risk calling a function in an object which no longer exists in memory - causing a spectacular access violation when your program closes.
If you want to learn Delphi properly, head over to Delphi Basics and read up on the basic principles. It takes a while to learn a new language but you will soon get the hang of it.
Good luck!
I'm attempting to write a unit test for a simple factory class that creates one of several possible implementing objects and returns it as an interface reference.
DUnit has a built in procedure, CheckIs(AObject: TObject; AClass: TClass; msg: string), that based on its name and the parameters it accepts should fail the test if the object's class type doesn't match the expected one. The only problem is it requires an object reference not an interface reference.
So I'm trying to use CheckTrue and perform the comparison in the body of the test but I'm not as familiar with Delphi's type checking support as I am with C#'s.
I know the is operator is out of the question since it only works with object references.
CheckTrue(LMyInterfaceReference {comparison here} TMyClass);
Any suggestions?
BTW, I'm using Delphi 2009 so I don't have access to the new RTTI support added in 2010+.
I'm wondering why you MUST have to test this... maybe you really don't have to.
But if knowing the underlying object of a Interface is a must, you have two choices:
Add a method to the interface which returns the underlying object, just a TObject, and implement this in each class just by returning self.
Hack a bit, for example using this Interface to object routine.
If you don't like hacks and don't feel like upgrading to Delphi 2010+ you may use an interface like this:
IImplementingObjectInterface = interface
function GetImplementingObject: TObject;
end;
Make sure your objects also implement this interface and use it to extract the implementing object. If you need to do this for a lot of objects you can define your own TInterfacedObject derivate that already implements this so you can simply change your inheritance and be done.
Barry Kelly (one of the main Embarcadero Delphi Compiler Engineers) wrote a nice An ugly alternative to interface to object casting this week.
It answers your question.
The fun is that Hallvard Vassbotn wrote a very similar piece of code back in 2004.
From Delphi 2010 on, you can just use an is check or as cast to go back from interface references to object references.
--jeroen
I've read the official documentation and I understand what class references are but I fail to see when and why they are best solution compared to alternatives.
The example cited in the documentation is TCollection which can be instantiated with any descendant of TCollectionItem. The justification for using a class reference is that it allows you to invoke class methods whose type is unknown at compile time (I assume this is the compile time of TCollection). I'm just not seeing how using TCollectionItemClass as an argument is superior to using TCollectionItem. TCollection would still be able to hold any descendant of TCollectionItem and still be able to invoke any method declared in TCollectionItem. Wouldn't it?
Compare this with a generic collection. TObjectList appears to offer much the same functionality as TCollection with the added benefit of type safety. You are freed from the requirement to inherit from TCollectionItem in order to store your object type and you can make a collection as type specific as you want. And if you need to access item's members from within the collection you can use generic constraints. Other than the fact that class references are available to programmers prior to Delphi 2009 is there any other compelling reason to use them over generic containers?
The other example given in the documentation is passing a class reference to a function that acts as an object factory. In this case a factory for creating objects of type TControl. Its not really apparent but I'm assuming the TControl factory is invoking the constructor of the descendant type passed to it rather than the constructor of TControl. If this is the case then I'm starting to see at least some reason for using class references.
So I guess what I'm really trying to understand is when and where class references are most appropriate and what do they buy a developer?
MetaClasses and "class procedures"
MetaClasses are all about "class procedures". Starting with a basic class:
type
TAlgorithm = class
public
class procedure DoSomething;virtual;
end;
Because DoSomething is a class procedure we can call it without an instance of TAlgorithm (it behaves like any other global procedure). We can do this:
TAlgorithm.DoSomething; // this is perfectly valid and doesn't require an instance of TAlgorithm
Once we've got this setup we might create some alternative algorithms, all sharing some bits and pieces of the base algorithm. Like this:
type
TAlgorithm = class
protected
class procedure DoSomethingThatAllDescendentsNeedToDo;
public
class procedure DoSomething;virtual;
end;
TAlgorithmA = class(TAlgorithm)
public
class procedure DoSomething;override;
end;
TAlgorithmB = class(TAlgorithm)
public
class procedure DoSomething;override;
end;
We've now got one base class and two descendent classes. The following code is perfectly valid because we declared the methods as "class" methods:
TAlgorithm.DoSomething;
TAlgorithmA.DoSomething;
TAlgorithmB.DoSomething;
Let's introduce the class of type:
type
TAlgorithmClass = class of TAlgorithm;
procedure Test;
var X:TAlgorithmClass; // This holds a reference to the CLASS, not a instance of the CLASS!
begin
X := TAlgorithm; // Valid because TAlgorithmClass is supposed to be a "class of TAlgorithm"
X := TAlgorithmA; // Also valid because TAlgorithmA is an TAlgorithm!
X := TAlgorithmB;
end;
TAlgorithmClass is a data type that can be used like any other data type, it can be stored in a variable, passed as a parameter to a function. In other words we can do this:
procedure CrunchSomeData(Algo:TAlgorithmClass);
begin
Algo.DoSomething;
end;
CrunchSomeData(TAlgorithmA);
In this example the procedure CrunchSomeData can use any variation of the algorithm as long as it's an descendant of TAlgorithm.
Here's an example of how this behavior may be used in a real-world application: Imagine a payroll-type application, where some numbers need to be calculated according to an algorithm that's defined by Law. It's conceivable this algorithm will change in time, because the Law is some times changed. Our application needs to calculate salaries for both the current year (using the up-to-date calculator) and for other years, using older versions of the algorithm. Here's how things might look like:
// Algorithm definition
TTaxDeductionCalculator = class
public
class function ComputeTaxDeduction(Something, SomeOtherThing, ThisOtherThing):Currency;virtual;
end;
// Algorithm "factory"
function GetTaxDeductionCalculator(Year:Integer):TTaxDeductionCalculator;
begin
case Year of
2001: Result := TTaxDeductionCalculator_2001;
2006: Result := TTaxDeductionCalculator_2006;
else Result := TTaxDeductionCalculator_2010;
end;
end;
// And we'd use it from some other complex algorithm
procedure Compute;
begin
Taxes := (NetSalary - GetTaxDeductionCalculator(Year).ComputeTaxDeduction(...)) * 0.16;
end;
Virtual Constructors
A Delphi Constructor works just like a "class function"; If we have a metaclass, and the metaclass knows about an virtual constructor, we're able to create instances of descendant types. This is used by TCollection's IDE Editor to create new items when you hit the "Add" button. All TCollection needs to get this working is a MetaClass for a TCollectionItem.
Yes a Collection would still be able to hold any descendant of TCollectionItem and to invoke methods on it. BUT, it wouldn't be able to instantiate a new instance of any descendant of a TCollectionItem. Calling TCollectionItem.Create constructs an instance of TCollectionItem, whereas
private
FItemClass: TCollectionItemClass;
...
function AddItem: TCollectionItem;
begin
Result := FItemClass.Create;
end;
would construct an instance of whatever class of TCollectionItem descendant is held in FItemClass.
I haven't done much with generic containers, but I think that given a choice, I would opt for the generic container. But in either case I'd still have to use a metaclass if I wanted to have the list instantiate and do whatever else needs to be done when an item is added in the container and I do not know the exact class beforehand.
For example an observable TObjectList descendant (or generic container) could have something like:
function AddItem(aClass: TItemClass): TItem;
begin
Result := Add(aClass.Create);
FObservers.Notify(Result, cnNew);
...
end;
I guess in short the advantage/benefit of metaclasses is any method/class having only knowledge of
type
TMyThing = class(TObject)
end;
TMyThingClass = class of TMyThing;
is able to construct instances of any descendant of TMyThing whereever they may have been declared.
Generics are very useful, and I agree that TObjectList<T> is (usually) more useful than TCollection. But class references are more useful for different scenarios. They're really part of a different paradigm. For example, class references can be useful when you have a virtual method that needs to be overridden. Virtual method overrides have to have the same signature as the original, so the Generics paradigm doesn't apply here.
One place where class references are used heavily is in form streaming. View a DFM as text sometime, and you'll see that every object is referred to by name and class. (And the name is optional, actually.) When the form reader reads the first line of an object definition, it gets the name of the class. It looks it up in a lookup table and retrieves a class reference, and uses that class reference to call that class's override of TComponent.Create(AOwner: TComponent) so it can instantiate the right kind of object, and then it starts applying the properties described in the DFM. This is the sort of thing that class references buy you, and it can't be done with generics.
I also would use a metaclass whenever I need to be able to make a factory that can construct not only one hard-coded class, but any class that inherits from my base class.
Metaclasses are not the term I am familiar with in Delphi circles however. I believe we call them class references, which has a less "magic" sounding name, so it's great that you put both common monikers in your question.
A concrete example of a place I have seen this used well is in the JVCL JvDocking components where a "docking style" component provides metaclass information to the base docking component set, so that when a user drags their mouse and docks a client form to the docking host form, the "tab host" and "conjoin host" forms that show grabber bars (similar in appearance to the title bar of a regular undocked window) can be of a user-defined plug-in class, that provides a customized appearance and customized runtime functionality, on a plug-in basis.
In some of my applications I have a mechanism that connects classes to forms capable of editing instances of one or more of that classes. I have a central list where those pairs are stored: a class refernce and a form class reference. Thus when I have an instance of a class I can lookup the corresponding form class, create a form from it and let it edit the instance.
Of course this could also be implemented by having a class method returning the appropriate form class, but that would require the form class to be known by the class. My approach makes a more modular system. The form must be aware of the class, but not the other way round. This can be a key point when you cannot change the classes.
I've written a custom SDL GUI toolkit (source is on http://sourceforge.net/projects/lkgui/files/) and I'm having an issue with an inherited object.
When the object is within the main program, the constructor isn't called hence the program doesn't properly initialise the object and it crashes after some commands (Specifically, TStartGameButton inherits from GUI_Canvas inherits from GUI_Element and anything that is not defined in GUI_Element crashes the program with an EAccessViolation). When the object is placed within a unit, this problem goes away.
I understand that I could just leave it in the unit, but it will lead to some ugly code that could hopefully be avoided.
Has anyone any idea why this might be happening and how I may avoid it?
Old-style Delphi objects have been broken since the release of Delphi 2, perhaps earlier. They do not do inheritance well when they have fields of compiler-managed types, such as string or dynamic arrays. There was a discussion about it in 2004 on comp.lang.pascal.delphi.misc. Here was the code to reproduce it:
type
TBase = object
public
s: string;
end;
TDerived = object(TBase)
end;
procedure test;
var
obj: TDerived; //okay for TBase!
begin
assert(obj.s = '', 'uninitialized dynamic variable');
end;
And in fact it's only OK for TBase by accident because of how the function's prologue code happens to be generated. Putting additional code in that function can make it crash anyway.
Indeed, it's exactly as you've observed — old-style objects don't get initialized properly. Their string fields don't start out holding an empty string; instead, they hold garbage, and so it's not even possible to initialize them yourself without using something like FillChar.
This appears to be due to the variables being local variables. Unit-scope ("global") variables seem to work OK. Variables that are declared at unit scope but only used by the unit's initialization section, or at program scope and used only in the DPR file's main begin-end block, are treated by the compiler as local variables, so they're not set to all-bits-zero like their global counterparts. When you move your variable declaration to a unit but continue to use it in your DPR file, it's elevated to "global" status.
Your TGUI_Element type has a string member called DbgName, and it looks like that's the only string field you have in the type hierarchy. Take that out, or change it to ShortString, and I'll bet your crashes go away, at least temporarily.
Why are you giving all objects individual named constructors instead of making them virtual?
type tx = object
constructor init; virtual;
end;
txx = object(tx)
constructor init; virtual; // like override in Delphi classes.
end;
If you need a visual hierarchy to look at, have a look at Free Vision, it demonstrates nearly every facet of the TP object model
Oops apparantly virtual constructors are not possible in the TP model