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:
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:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With