Can I use generics to do the same operation on similar types of controls? - delphi

I am using Delphi 2010 and I have a unit where over the years I have added my own procedures and functions that can be used with any project I make, such as:
function ListBoxIsSelected(ListBox: TListBox): Boolean;
begin
Result:= ListBox.ItemIndex <> -1;
end;
The above uses TListBox as a parameter, so whenever the above function is used I must supply a listbox that is of TListBox class.
Now suppose I have some other component libraries that could work with the same function, For example the Jedi component classes.
How could I use the above function, when the Jedi listbox is TJvListBox class and my function is looking for TListBox class? Although both components are practically the same, the class names are different. If I provided the same function specifically for the TJvListBox it would likely work because they are both "listboxes":
function ListBoxIsSelected(ListBox: TJvListBox): Boolean;
begin
Result:= ListBox.ItemIndex <> -1;
end;
Now, I have whole load of procedures and functions written in the same kind of way where I need to pass a component as a parameter. Having to rewrite them again just to work with a different component class is not feasible!
How can I write this with generics?

You can't write that with generics, unless your target classes all descend from the same base class of course. (But then you wouldn't need generics for it.)
If you really want something that can check if the ItemIndex property on any object <> -1, though, you can do that with a different Delphi 2010 feature: extended RTTI.
uses
SysUtils, RTTI;
function IsSelected(item: TObject): boolean;
var
context: TRttiContext;
cls: TRttiType;
prop: TRttiProperty;
ItemIndex: integer;
begin
if item = nil then
raise Exception.Create('Item = nil');
context := TRttiContext.Create;
cls := context.GetType(item.ClassType);
prop := cls.GetProperty('ItemIndex');
if prop = nil then
raise Exception.Create('Item does not contain an ItemIndex property.');
ItemIndex := prop.GetValue(item).AsInteger;
result := ItemIndex <> -1;
end;
Careful, though. There's no compile-time type checking here, and this process is significantly slower than your original routine. You probably won't notice it, but if you call something like this in a tight loop, it will slow it down.

I don't understand how I can write this with Generics?
You can’t – not unless your component implements a common interface or inherits from a common base class with the standard ListBox, and that interface / base class offers the ItemIndex property.
In fact, this use-case isn’t such a great example of generics because using an interface or base class in the declaration would work just as well.

In this case, you can write two overloaded functions, one expecting TJvListBox and the other expecting TListBox.
In more complex cases this approach may not apply so well, but I think your case is simple enough for this solution.

I cannot look it up right now (on holiday, no Delphi), but don't TJvListBox and TListBox descend from a common ancestor (my guess would be: TCustomListBox)? In that case something like this should work:
interface
function TListBox_IsItemSelected(_ListBox: TCustomListBox): boolean;
implementation
function TListBox_IsItemSelected(_ListBox: TCustomListBox): boolean;
begin
Result := _ListBox.ItemIndex <> -1;
end;
Just in case ItemIndex (as I said: I cannot check right now) is protected in TCustomListBox, you can just use a typecast hack:
type
TListBoxHack = class(TCustomListBox)
end;
function TListBox_IsItemSelected(_ListBox: TCustomListBox): boolean;
begin
Result := TListBoxHack(_ListBox).ItemIndex <> -1;
end;
(I just thought I should mention this since the original question has already been answered: Using Generics does not help here.)

Related

Trying to combining generics and operator overloading in Delphi [duplicate]

I'm new in Delphi. For a project required by my company, I need to translate some code from our existing C++ classes to Delphi. Some of these classes are templates, such as:
template <class T>
struct APoint
{
T m_X;
T m_Y;
virtual void Add(T value);
};
template <class T>
void APoint<T>::Add(T value)
{
m_X += value;
m_Y += value;
}
I use it e.g. with this code
APoint<float> pt;
pt.m_X = 2.0f;
pt.m_Y = 4.0f;
pt.Add(5.0f);
and this works well.
Now I need to write equivalent code for Delphi. I tried to write a Delphi Generic class, based on the C++ code above:
APoint<T> = record
m_X: T;
m_Y: T;
procedure Add(value: T);
end;
procedure APoint<T>.Add(value: T);
begin
m_X := m_X + value;
m_Y := m_Y + value;
end;
However this code does not compile. I get this error:
E2015 Operator not applicable to this operand type
AFAIK this code should work, and I don't understand what is wrong with it. So can anybody explain to me:
Why a such code does not compile in Delphi?
What is the correct (and simplest) way in Delphi to create a template class that provides an Add() function, as closest as possible to the C++ code and usage above?
EDITED on 17.10.2016
Thanks for all the replies. So if I understood correctly, there is no way to create a c++-like style template, because Delphi imposes several constraints that not exists in c++.
Based on that, I searched a workaround to reach the objective I want. I found the following solution:
IPoint<T> = interface
procedure Add(value: T);
end;
APoint<T> = class(TInterfacedObject, IPoint<T>)
m_X: T;
m_Y: T;
procedure Add(value: T); virtual; abstract;
end;
APointF = class(APoint<Single>)
destructor Destroy; override;
procedure Add(value: Single); reintroduce;
end;
destructor APointF.Destroy;
begin
inherited Destroy;
end;
procedure APointF.Add(value: Single);
begin
m_X := m_X + value;
m_Y := m_Y + value;
end;
I use it e.g. with this code
procedure AddPoint;
var
pt: IPoint<Single>;
begin
pt := APointF.Create;
APointF(pt).m_X := 2.0;
APointF(pt).m_Y := 4.0;
APointF(pt).Add(5.0);
end;
and this works well. However I find the style a little heavy, e.g. the necessity to use APointF(pt). So, in relation to code above, my questions are:
Is this solution a good solution? (i.e. better to write a version of each record for each type I want to support, like e.g APointF, APointI, APointD, ...)
Is there a way to simplify this code, e.g. a solution to call pt.m_X directly without the APointF(pt) conversion? (NOTE I omitted here the implementation of properties, even if I think them more elegant than accessing the variable directly)
What about the performances of this solution? (I.e. is this solution drastically slower than a direct m_X := m_X + value addition?)
Finally, I saw another solution in the Delphi code, where it is possible to implement an equality comparison of 2 generic types this way:
function APoint<T>.IsEqual(const other: APoint<T>): Boolean;
var
comparer: IEqualityComparer<T>;
begin
Result := (comparer.Equals(m_X, other.m_X) and comparer.Equals(m_Y, other.m_Y));
end;
I tried to read the code behind the scene, however I found it terribly complicated. So, my questions are:
Is a such solution better than the one above proposed?
Is there a similar ready-to-use solution for mathematical operations?
Are the performance of a such solution acceptable?
Thanks in advance for your replies
Regards
Delphi Generics are intrinsically different from C++ template, and resemble more their C# counterpart.
In C++ you can do any operation on template types, and at time of the template instantiation the compiler checks that the operation you are performing in the template are available for the specific type you are using. If not you get a compiler error.
In Delphi (and many other languages) you declare a generic type possibly providing some declarative constraints, and those constraints -- base classes or interface -- determine the operations you can do on the generic type. At instantiation time, the only check is if the declared type fits the constraint.
Arguably, the Delphi language could add constraints for floating point or ordinal types, but this would provide a very limited flexibility (changing the floating or integer type you can use in the generic instance). I personally don't regard this as a critical feature.
Delphi generics do not support arithmetic operators that act on generic types. In order for the compiler to accept the code it needs to know that each operation on a generic type is going to be available upon instantiation.
Generic constraints allow you to tell the compiler what capabilities the type has. However generic constraints do not allow you to tell the compiler that the type supports arithmetjc operators.
Unfortunately what you are trying to do is simply not possible. For sure you can construct frameworks yourself that can use tools like interfaces to get the arithmetic performed but doing so gives up performance. If that is acceptable then fine. Otherwise you are best biting the bullet and avoiding generics here.
Oh for C++ templates.
Pointing this out for anyone else looking for an Object Pascal solution to this general issue:
Note that Free Pascal quite simply does support exactly what they're trying to do here (even in "Delphi-syntax compatibility" mode.)
As someone who had only used Free Pascal for a long time, I was honestly very surprised that Delphi doesn't allow this at all, once I realized that was the case. It's a significant limitation IMO.
Valid Free Pascal code:
program Example;
// Using Delphi-mode here allows us to declare and use
// generics with the same syntax as Delphi, as opposed to
// needing the "generic" and "specialize" keywords that
// FPC normally requires.
{$mode Delphi}
// To allow +=, another nice FPC feature...
{$COperators On}
type
TGPoint<T> = record
X, Y: T;
// static class function instead of constructor here so we can inline it
class function Create(constref AX, AY: T): TGPoint<T>; static; inline;
// define our operator overload
class operator Add(constref Left, Right: TGPoint<T>): TGPoint<T>; inline;
end;
class function TGPoint<T>.Create(constref AX, AY: T): TGPoint<T>;
begin
with Result do begin
X := AX;
Y := AY;
end;
end;
class operator TGPoint<T>.Add(constref Left, Right: TGPoint<T>): TGPoint<T>;
begin
with Result do begin
X := Left.X + Right.X;
Y := Left.Y + Right.Y;
end;
end;
var SP: TGPoint<String>;
begin
SP := TGPoint<String>.Create('Hello, ', 'Hello, ');
SP += TGPoint<String>.Create('world!', 'world!');
with SP do begin
WriteLn(X);
WriteLn(Y);
end;
end.
The program of course prints:
Hello, world!
Hello, world!

Arithmetic operations with generic types in Delphi

I'm new in Delphi. For a project required by my company, I need to translate some code from our existing C++ classes to Delphi. Some of these classes are templates, such as:
template <class T>
struct APoint
{
T m_X;
T m_Y;
virtual void Add(T value);
};
template <class T>
void APoint<T>::Add(T value)
{
m_X += value;
m_Y += value;
}
I use it e.g. with this code
APoint<float> pt;
pt.m_X = 2.0f;
pt.m_Y = 4.0f;
pt.Add(5.0f);
and this works well.
Now I need to write equivalent code for Delphi. I tried to write a Delphi Generic class, based on the C++ code above:
APoint<T> = record
m_X: T;
m_Y: T;
procedure Add(value: T);
end;
procedure APoint<T>.Add(value: T);
begin
m_X := m_X + value;
m_Y := m_Y + value;
end;
However this code does not compile. I get this error:
E2015 Operator not applicable to this operand type
AFAIK this code should work, and I don't understand what is wrong with it. So can anybody explain to me:
Why a such code does not compile in Delphi?
What is the correct (and simplest) way in Delphi to create a template class that provides an Add() function, as closest as possible to the C++ code and usage above?
EDITED on 17.10.2016
Thanks for all the replies. So if I understood correctly, there is no way to create a c++-like style template, because Delphi imposes several constraints that not exists in c++.
Based on that, I searched a workaround to reach the objective I want. I found the following solution:
IPoint<T> = interface
procedure Add(value: T);
end;
APoint<T> = class(TInterfacedObject, IPoint<T>)
m_X: T;
m_Y: T;
procedure Add(value: T); virtual; abstract;
end;
APointF = class(APoint<Single>)
destructor Destroy; override;
procedure Add(value: Single); reintroduce;
end;
destructor APointF.Destroy;
begin
inherited Destroy;
end;
procedure APointF.Add(value: Single);
begin
m_X := m_X + value;
m_Y := m_Y + value;
end;
I use it e.g. with this code
procedure AddPoint;
var
pt: IPoint<Single>;
begin
pt := APointF.Create;
APointF(pt).m_X := 2.0;
APointF(pt).m_Y := 4.0;
APointF(pt).Add(5.0);
end;
and this works well. However I find the style a little heavy, e.g. the necessity to use APointF(pt). So, in relation to code above, my questions are:
Is this solution a good solution? (i.e. better to write a version of each record for each type I want to support, like e.g APointF, APointI, APointD, ...)
Is there a way to simplify this code, e.g. a solution to call pt.m_X directly without the APointF(pt) conversion? (NOTE I omitted here the implementation of properties, even if I think them more elegant than accessing the variable directly)
What about the performances of this solution? (I.e. is this solution drastically slower than a direct m_X := m_X + value addition?)
Finally, I saw another solution in the Delphi code, where it is possible to implement an equality comparison of 2 generic types this way:
function APoint<T>.IsEqual(const other: APoint<T>): Boolean;
var
comparer: IEqualityComparer<T>;
begin
Result := (comparer.Equals(m_X, other.m_X) and comparer.Equals(m_Y, other.m_Y));
end;
I tried to read the code behind the scene, however I found it terribly complicated. So, my questions are:
Is a such solution better than the one above proposed?
Is there a similar ready-to-use solution for mathematical operations?
Are the performance of a such solution acceptable?
Thanks in advance for your replies
Regards
Delphi Generics are intrinsically different from C++ template, and resemble more their C# counterpart.
In C++ you can do any operation on template types, and at time of the template instantiation the compiler checks that the operation you are performing in the template are available for the specific type you are using. If not you get a compiler error.
In Delphi (and many other languages) you declare a generic type possibly providing some declarative constraints, and those constraints -- base classes or interface -- determine the operations you can do on the generic type. At instantiation time, the only check is if the declared type fits the constraint.
Arguably, the Delphi language could add constraints for floating point or ordinal types, but this would provide a very limited flexibility (changing the floating or integer type you can use in the generic instance). I personally don't regard this as a critical feature.
Delphi generics do not support arithmetic operators that act on generic types. In order for the compiler to accept the code it needs to know that each operation on a generic type is going to be available upon instantiation.
Generic constraints allow you to tell the compiler what capabilities the type has. However generic constraints do not allow you to tell the compiler that the type supports arithmetjc operators.
Unfortunately what you are trying to do is simply not possible. For sure you can construct frameworks yourself that can use tools like interfaces to get the arithmetic performed but doing so gives up performance. If that is acceptable then fine. Otherwise you are best biting the bullet and avoiding generics here.
Oh for C++ templates.
Pointing this out for anyone else looking for an Object Pascal solution to this general issue:
Note that Free Pascal quite simply does support exactly what they're trying to do here (even in "Delphi-syntax compatibility" mode.)
As someone who had only used Free Pascal for a long time, I was honestly very surprised that Delphi doesn't allow this at all, once I realized that was the case. It's a significant limitation IMO.
Valid Free Pascal code:
program Example;
// Using Delphi-mode here allows us to declare and use
// generics with the same syntax as Delphi, as opposed to
// needing the "generic" and "specialize" keywords that
// FPC normally requires.
{$mode Delphi}
// To allow +=, another nice FPC feature...
{$COperators On}
type
TGPoint<T> = record
X, Y: T;
// static class function instead of constructor here so we can inline it
class function Create(constref AX, AY: T): TGPoint<T>; static; inline;
// define our operator overload
class operator Add(constref Left, Right: TGPoint<T>): TGPoint<T>; inline;
end;
class function TGPoint<T>.Create(constref AX, AY: T): TGPoint<T>;
begin
with Result do begin
X := AX;
Y := AY;
end;
end;
class operator TGPoint<T>.Add(constref Left, Right: TGPoint<T>): TGPoint<T>;
begin
with Result do begin
X := Left.X + Right.X;
Y := Left.Y + Right.Y;
end;
end;
var SP: TGPoint<String>;
begin
SP := TGPoint<String>.Create('Hello, ', 'Hello, ');
SP += TGPoint<String>.Create('world!', 'world!');
with SP do begin
WriteLn(X);
WriteLn(Y);
end;
end.
The program of course prints:
Hello, world!
Hello, world!

Add a property on TWinControl Class

I want to add a published property into TWinControl.
Is there someway to do this without the necessity of recompiling the base source code ?
If not, some way to recompile the base source code without too much troubles ?
Tks in advice...
EDIT 'CAUSE OF NEW IDEAS
Alright, What I'm thinking to do I'm trying to override the _GetMem from System.pas for classes
inherited from TWinControl.
Why ? 'Cause I'll alloc some extra space to the objects enough to an integer.
Why an integer ? 'Cause this way I can add any pointer to object.
So on the helper class to TWinControl I can make a Get an Set function to access this space of memory.
Good isn't it ? How to do this ?
Overrideing the GetMem procedure I can use the same strategy used on FastCode, create a jumper to the new procedure.
What I need now is understand how this memory alloc works InstanceSize to override this.
At all I'm studding how do Delphi do this... And to add this on DFM I will do the same way, I'll create a jumper to the filer.
Someone have some idea to add the new space in objects ? What method I need to override ? The jumper I know how to do.
Tks Again.
EDIT = Evolution
I think that I did the injection of memory.
I need to do more tests.
I've just did it, I'm not caring about optimizations at the moment, if some one would like to test it, here goes the code.
Just add the unit as the first unit of your project.
unit uMemInjection;
interface
uses
Controls;
type
THelperWinControl = class Helper for TWinControl
private
function RfInstanceSize: Longint;
function GetInteger: Integer;
procedure SetInteger(const Value: Integer);
public
property RfInteger: Integer read GetInteger write SetInteger;
end;
implementation
uses
Windows;
procedure SInstanceSize;
asm
call TWinControl.InstanceSize
end;
function THelperWinControl.GetInteger: Integer;
begin
Result := Integer(PInteger(Integer(Self) + (Self.InstanceSize - SizeOf(Integer)))^);
end;
function THelperWinControl.RfInstanceSize: Longint;
begin
Result := PInteger(Integer(Self) + vmtInstanceSize)^;
Result := Result + SizeOf(Integer);
end;
/////////////////////////////////////////////// FastCode ///////////////////////////////////////////////
type
PJump = ^TJump;
TJump = packed record
OpCode: Byte;
Distance: Pointer;
end;
function FastcodeGetAddress(AStub: Pointer): Pointer;
begin
if PBYTE(AStub)^ = $E8 then
begin
Inc(Integer(AStub));
Result := Pointer(Integer(AStub) + SizeOf(Pointer) + PInteger(AStub)^);
end
else
Result := nil;
end;
procedure FastcodeAddressPatch(const ASource, ADestination: Pointer);
const
Size = SizeOf(TJump);
var
NewJump: PJump;
OldProtect: Cardinal;
begin
if VirtualProtect(ASource, Size, PAGE_EXECUTE_READWRITE, OldProtect) then
begin
NewJump := PJump(ASource);
NewJump.OpCode := $E9;
NewJump.Distance := Pointer(Integer(ADestination) - Integer(ASource) - 5);
FlushInstructionCache(GetCurrentProcess, ASource, SizeOf(TJump));
VirtualProtect(ASource, Size, OldProtect, #OldProtect);
end;
end;
/////////////////////////////////////////////// FastCode ///////////////////////////////////////////////
{ THelperWinControl }
procedure THelperWinControl.SetInteger(const Value: Integer);
begin
PInteger(Integer(Self) + (Self.InstanceSize - SizeOf(Integer)))^ := Value;
end;
initialization
FastcodeAddressPatch(FastcodeGetAddress(#SInstanceSize), #TWinControl.RfInstanceSize);
end.
Thanks to Smasher, I remembered how the Delphi team used class helpers and a designer trick to add properties to Delphi 2007 without breaking binary compatibility with Delphi 2006.
See this great article by Hallvard Vassbotn on how to do this.
I think it solves most, if not all, of your problems.
look for these things in the article:
TCustomFormHelper = class helper for TCustomForm
The FPixelsPerInch storage hack
Injecting design-time properties
Defining the streaming properties
You'll have to work your own way to do the streaming, though, as you hook from the outside world into TWinControl, but that might be possible too.
--jeroen
Delphi2007 and higher have "class helpers".
You can introduce new functions and properties, but no fields/variables. So you have to store the value of you new property in a extra object (via factory or whatever) or (very ugly) in the .Tag property...
Don't know if class helper also work in packages/design time?
If you are using this property only on the application level, you may use the following approaches:
composition: bundle a reference to TWinControl object with other properties into new class, and pass/operate objects this class in your calls
dictionary-like functions: GetMyPropertyFor( AWinControl: TWinControl): and SetMyPropertyFor( AWinControl: TWinControl: AValue: ), which internally maintain additional property for each called TWinControl object
ADDITION: Based on your additional comment, existing Tag property should play well for your needs. You can even define 'levels' by using different values there.
No, there is no way to modify TWinControl without recompiling the VCL. Also I don't recommend changing the VCL (since having a "custom" VCL can impact the portability of your project - at the very least between Delphi installations). I would aim at making another class that inherit from TWinControl and then add your published property to this new class.
If you still want to change the VCL see the following post:
http://www.delphigroups.info/2/6/744173.html
Note that "you will no longer be able to compile using runtime
packages"...
(I know the answer is a bit dense, comment on it what details you need more info about)
What you could do is what for instance TGridPanel does: it adds the Column, Row, ColumnSpan and RowSpan 'properties' to the object inspector for all components that are on the GridPanel.
That will solve your design-time support.
I thought I had a reference on how the TGridPanel does this (and TFlowPanel does similar things), but I can't find it right now. Probably Ray Konopka explained this during a conference, but that info might not be on-line.
For run-time support, you could go with class helpers.
When using class helpers, note that only the nearest visible one for a class will apply.
Another route you might follow is to use the Tag property (which is an Integer, but you can cast it to a Pointer or a TObject), but you might be bitten by others using that too.
You'd have to create your own design-time support for those tag properties though.
--jeroen

Bypassing (disabling) Delphi's reference counting for interfaces

For one particular issue in the architecture of an application I'm working on, interfaces seem to be a nice solution. Specifically, some "business objects" depend on a bunch of settings that are pulled from the database in the actual app. Letting those business objects ask for an interface (through Inversion of Control), and letting a central TDatabaseSettings object implement those interfaces, allows for better isolation, and thus for much easier unit testing.
However, in Delphi, interfaces seem to come with an, in this case, unpleasant bonus: reference counting. This means that if I do something like this:
type
IMySettings = interface
function getMySetting: String;
end;
TDatabaseSettings = class(..., IMySettings)
//...
end;
TMyBusinessObject = class(TInterfacedObject, IMySettings)
property Settings: IMySettings read FSettings write FSettings;
end;
var
DatabaseSettings: TDatabaseSettings;
// global object (normally placed in a controller somewhere)
//Now, in some function...
O := TMyBusinessObject.Create;
O.Settings := DatabaseSettings;
// ... do something with O
O.Free;
On the last line (O.Free), my global DatabaseSettings object is now also freed, since the last interface reference to it (which was contained in O) is lost!
One solution would be to store the 'global' DatabaseSettings object with an interface; another solution would be to override the reference counting mechanism for the TDatabaseSettings class, so I can continue to manage the DatabaseSettings as a normal object (which is much more consistent with the rest of the app).
So, in summary, my question is: how do I disable the interface reference counting mechanism for a particular class?
I've been able to find some info that suggests overriding the IInterface methods _AddRef and _Release for the class (TDatabaseSettings in the example); has anyone ever done that?
Or would you say I shouldn't do this (confusing? just a bad idea?), and find a different solution to the architectural problem?
Thanks a lot!
Ok, you can bypass it, but the question is if you really want that.
If you want to use interfaces, you better use them completely. So as you have experienced it, you get problems if you mix class and interface variables.
var
// DatabaseSettings: TDatabaseSettings;
DatabaseSettings : IMySettings;
//Now, in some function...
O := TMyBusinessObject.Create;
O.Settings := DatabaseSettings;
// ... do something with O
O.Free;
You now have a second reference to the interface and losing the first will not free the object.
It as also possible to keep both the class and the object:
var
DatabaseSettings: TDatabaseSettings;
DatabaseSettingsInt : IMySettings;
Be sure to set the interface right after the object has been created.
If you really want to disable reference counting, you just have to create a new descendant of TObject that implements IInterface. I have tested the example below in D2009 and it works:
// Query Interface can stay the same because it does not depend on reference counting.
function TMyInterfacedObject.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
if GetInterface(IID, Obj) then
Result := 0
else
Result := E_NOINTERFACE;
end;
constructor TMyInterfacedObject.Create;
begin
FRefCount := 1;
end;
procedure TMyInterfacedObject.FreeRef;
begin
if Self = nil then
Exit;
if InterlockedDecrement(FRefCount) = 0 then
Destroy;
end;
function TMyInterfacedObject._AddRef: Integer;
begin
Result := InterlockedIncrement(FRefCount);
end;
function TMyInterfacedObject._Release: Integer;
begin
Result := InterlockedDecrement(FRefCount);
if Result = 0 then
Destroy;
end;
FreeRef just lowers the refcount just like _Release. You can use it where you normally use Free.
Don't descend from TInterfacedObject, instead descend from TSingletonImplementation from standard System.Generics.Defaults unit.
TSingletonImplementation is a base for simple classes that need a basic IInterface implementation, with reference counting disabled.
TSingletonImplementation is a thread-safe base class for Delphi classes that support interfaces. Unlike TInterfacedObject, TSingletonImplementation does not implement reference counting.
_AddRef, _Release and _QueryInterface are, in fact, what you want to override. You should be very clear about what you're doing, however, as this can cause memory leaks or strange, hard-to-find bugs.
Don't descend from TInterfacedObject, instead descend from TObject, and implement your own versions of the first two of those methods that return 1.
To disable reference counting, AddRef and Release should do nothing but return -1
function TMyInterfacedObject._AddRef: Integer;
begin
Result := -1;
end;
function TMyInterfacedObject._Release: Integer;
begin
Result := -1;
end;
There is quite a lot of utility in interfaces without reference counting. If you use reference counting, then you cannot mix object and interface references as bad things will happen. By disabling ref counts, you can happily mix interface and object references without worrying about your objects suddenly getting auto destroyed.
Disabling reference counting for this kind of problem smells bad.
A much nicer and architectural solution would be to use some kind of "singleton" pattern.
The easiest way to implement this would look like:
interface
type
TDatabaseSettings = class(..., IMySettings)
end;
function DatabaseSettings: IMySettings;
implementation
var
GDatabaseSettings: IMySettings;
function DatabaseSettings: IMySettings;
begin
if GDatabaseSettings = nil then GDatabaseSettings := TDatabaseSettings.Create;
Result := GDatabaseSettings;
end;
O := TMyBusinessObject.Create;
O.Settings := DatabaseSettings;
O.Free;
By the way: when you use interfaces: always use interface variables! Do not mix both class en interface vars (use "var Settings: IMySettings" instead of "var Settings: TDatabaseSettings"). Otherwise reference counting will get in your way (auto destroy, invalid pointer operations, etc).
In the above solution, GDatabaseSettings is also of type "IMySettings", so it gets a proper reference count, and will last till your program terminates.
Or just use the code below:
var
I: IMyInterface;
begin
I := ...;
...
Do whatever you want in a scope;
Initialize(I); //- this will clear the interface variable without calling the _release.
end.

What are good uses for class helpers?

Delphi (and probably a lot of other languages) has class helpers. These provide a way to add extra methods to an existing class. Without making a subclass.
So, what are good uses for class helpers?
I'm using them:
To insert enumerators into VCL classes that don't implement them.
To enhance VCL classes.
To add methods to the TStrings class so I can use the same methods in my derived lists and in TStringList.
TGpStringListHelper = class helper for TStringList
public
function Last: string;
function Contains(const s: string): boolean;
function FetchObject(const s: string): TObject;
procedure Sort;
procedure Remove(const s: string);
end; { TGpStringListHelper }
To simplify access to record fields and remove casting.
At first I was kind of sceptic about class helpers. But then I read an interesting blog entry and now I'm convinced that they are indeed useful.
For example, if you want extra functionality for an existing instance class and for some reason you are not able to change the existing source. You can create a class helper to add this functionality.
Example:
type
TStringsHelper = class helper for TStrings
public
function IsEmpty: Boolean;
end;
function TStringsHelper.IsEmpty: Boolean;
begin
Result := Count = 0;
end;
Every time, we now use an instance of (a subclass of) TStrings, and TStringsHelper is within the scope. We have access to the method IsEmpty.
Example:
procedure TForm1.Button1Click(Sender: TObject);
begin
if Memo1.Lines.IsEmpty then
Button1.Caption := 'Empty'
else
Button1.Caption := 'Filled';
end;
Notes:
Class helpers can be stored in a separate unit, so you can add your own nifty class helpers. Be sure to give these units a easy to remember name like ClassesHelpers for helpers for the Classes unit.
There are also record helpers.
If there are multiple class helpers within scope, expect some problems, only one helper can be used.
This sounds very much like extension methods in C#3 (and VB9). The best use I've seen for them is the extensions to IEnumerable<T> (and IQueryable<T>) which lets LINQ work against arbitrary sequences:
var query = someOriginalSequence.Where(person => person.Age > 18)
.OrderBy(person => person.Name)
.Select(person => person.Job);
(or whatever, of course). All of this is doable because extension methods allow you to effectively chain together calls to static methods which take the same type as they return.
They're very useful for plug-ins. For example, let's say your project defines a certain data structure and it's saved to disc in a certain way. But then some other program does something very similar, but the data file's different. But you don't want to bloat your EXE with a bunch of import code for a feature that a lot of your users won't need to use. You can use a plugin framework and put importers into a plugin that would work like this:
type
TCompetitionToMyClass = class helper for TMyClass
public
constructor Convert(base: TCompetition);
end;
And then define the converter. One caveat: a class helper is not a class friend. This technique will only work if it's possible to completely setup a new TMyClass object through its public methods and properties. But if you can, it works really well.
I would not recommend to use them, since I read this comment:
"The biggest problem with class
helpers, from the p.o.v of using them
in your own applications, is the fact
that only ONE class helper for a given
class may be in scope at any time."
... "That is, if you have two helpers
in scope, only ONE will be recognised
by the compiler. You won't get any
warnings or even hints about any other
helpers that may be hidden."
http://davidglassborow.blogspot.com/2006/05/class-helpers-good-or-bad.html
The first time I remember experiencing what you're calling "class helpers" was while learning Objective C. Cocoa (Apple's Objective C framework) uses what are called "Categories."
A category allows you to extend an existing class by adding you own methods without subclassing. In fact Cocoa encourages you to avoid subclassing when possible. Often it makes sense to subclass, but often it can be avoided using categories.
A good example of the use of a category in Cocoa is what's called "Key Value Code (KVC)" and "Key Value Observing (KVO)."
This system is implemented using two categories (NSKeyValueCoding and NSKeyValueObserving). These categories define and implement methods that can be added to any class you want. For example Cocoa adds "conformance" to KVC/KVO by using these categories to add methods to NSArray such as:
- (id)valueForKey:(NSString *)key
NSArray class does not have either a declaration nor an implementation of this method. However, through use of the category. You can call that method on any NSArray class. You are not required to subclass NSArray to gain KVC/KVO conformance.
NSArray *myArray = [NSArray array]; // Make a new empty array
id myValue = [myArray valueForKey:#"name"]; // Call a method defined in the category
Using this technique makes it easy to add KVC/KVO support to your own classes. Java interfaces allow you to add method declarations, but categories allow you to also add the actual implementations to existing classes.
As GameCat shows, TStrings is a good candidate to avoid some typing:
type
TMyObject = class
public
procedure DoSomething;
end;
TMyObjectStringsHelper = class helper for TStrings
private
function GetMyObject(const Name: string): TMyObject;
procedure SetMyObject(const Name: string; const Value: TMyObject);
public
property MyObject[const Name: string]: TMyObject read GetMyObject write SetMyObject; default;
end;
function TMyObjectStringsHelper.GetMyObject(const Name: string): TMyObject;
var
idx: Integer;
begin
idx := IndexOf(Name);
if idx < 0 then
result := nil
else
result := Objects[idx] as TMyObject;
end;
procedure TMyObjectStringsHelper.SetMyObject(const Name: string; const Value:
TMyObject);
var
idx: Integer;
begin
idx := IndexOf(Name);
if idx < 0 then
AddObject(Name, Value)
else
Objects[idx] := Value;
end;
var
lst: TStrings;
begin
...
lst['MyName'] := TMyObject.Create;
...
lst['MyName'].DoSomething;
...
end;
Did you ever need to access multi line strings in the registry?
type
TRegistryHelper = class helper for TRegistry
public
function ReadStrings(const ValueName: string): TStringDynArray;
end;
function TRegistryHelper.ReadStrings(const ValueName: string): TStringDynArray;
var
DataType: DWord;
DataSize: DWord;
Buf: PChar;
P: PChar;
Len: Integer;
I: Integer;
begin
result := nil;
if RegQueryValueEx(CurrentKey, PChar(ValueName), nil, #DataType, nil, #DataSize) = ERROR_SUCCESS then begin
if DataType = REG_MULTI_SZ then begin
GetMem(Buf, DataSize + 2);
try
if RegQueryValueEx(CurrentKey, PChar(ValueName), nil, #DataType, PByte(Buf), #DataSize) = ERROR_SUCCESS then begin
for I := 0 to 1 do begin
if Buf[DataSize - 2] <> #0 then begin
Buf[DataSize] := #0;
Inc(DataSize);
end;
end;
Len := 0;
for I := 0 to DataSize - 1 do
if Buf[I] = #0 then
Inc(Len);
Dec(Len);
if Len > 0 then begin
SetLength(result, Len);
P := Buf;
for I := 0 to Len - 1 do begin
result[I] := StrPas(P);
Inc(P, Length(P) + 1);
end;
end;
end;
finally
FreeMem(Buf, DataSize);
end;
end;
end;
end;
I've seen them used for making available class methods consistent across classes: Adding Open/Close and Show/Hide to all classes of a given "type" rather than only Active and Visible properties.
If Dephi supported extension methods, one use i want is:
TGuidHelper = class
public
class function IsEmpty(this Value: TGUID): Boolean;
end;
class function TGuidHelper(this Value: TGUID): Boolean;
begin
Result := (Value = TGuid.Empty);
end;
So i can call if customerGuid.IsEmpty then ....
Another good example is to be able to read values from an XML document (or JSON if you're into that sort of thing) with the IDataRecord paradigm (which i love):
orderGuid := xmlDocument.GetGuid('/Order/OrderID');
Which is much better than:
var
node: IXMLDOMNode;
node := xmlDocument.selectSingleNode('/Order/OrderID');
if Assigned(node) then
orderID := StrToGuid(node.Text) //throw convert error on empty or invalid
else
orderID := TGuid.Empty; // "DBNull" becomes the null guid
Other languages have properly designed class helpers.
Delphi has class helpers that were introduced solely to help the Borland engineers with a compatibility problem between Delphi and Delphi.net.
They were never intended to be used in "user" code and have not been improved since. They can be helpful if developing frameworks (for private use within the framework, as with the original .NET compatibility solution); it is dangerously misguided to equate Delphi class helpers with those in other languages or to draw on examples from those other languages in an effort to identify use cases for those in Delphi.
To this day, the current Delphi documentation has this to say about class and record helpers:
they should not be viewed as a design tool to be used when developing new code
ref: https://docwiki.embarcadero.com/RADStudio/Alexandria/en/Class_and_Record_Helpers_(Delphi)
So the answer to the question "what are the good uses for class helpers" in Delphi specifically is quite simple:
There is only one safe use: For context-specific extensions of utility and interest only in the single codebase that implements and consumes the helper (detailed example here: https://www.deltics.co.nz/blog/posts/683).
The example is a framework for restful API where extensions to a class of interest only to client-side code are provided by "Client Helper" extensions, explicitly imported from client-specific units rather than (over)loading client-concerns into an original class with both server and client context.
Other than that: Do not use them at all (either implementing your own or consuming those provided by others) unless you are prepared to deal with the consequences:
Primarily: Only one helper can be in scope at any time
Secondarily: There is no way to qualify helper referenced
Because of the primary problem:
Adding a unit to (or even just changing the order of) the units in a uses clause may inadvertently "hide" a helper needed in your code (you may not even know where from)
A helper added to a unit already in your uses list could hide some other helper previously "imported" and used from another
And thanks to the secondary problem, if you are unable to re-order the uses list to make a desired helper "visible" or you need 2 unrelated helpers (unaware of each other and so unable to "extent" one another), then there is no way to use it!
Worth emphasising here is that the ability of Delphi class helpers to break other people's code is an almost uniquely bad characteristic. Many language features in many languages can be abused to break your own code; not many enable you to break someone else's!
More details in various posts here: https://www.deltics.co.nz/blog/?s=class+helpers
Particularly this one: https://www.deltics.co.nz/blog/posts/273/

Resources