Does Delphi support explicit specialization of generic methods? - delphi

In C++, you can explicitly define a unique specialization for a templated function, like (to steal an example)
// A generic sort function
template <class T>
void sort(T arr[], int size)
{
// code to implement Quick Sort
}
// Template Specialization: A function
// specialized for char data type
template <>
void sort<char>(char arr[], int size)
{
// code to implement counting sort
}
Is there a way to do the equivalent with Delphi generic methods? When I try
function TryStrConv<T>(S: string; var Val: T): boolean;
function TryStrConv<float>(S: string; var Val: float): boolean;
I get warnings about how I have to use the Overload directive.
What I'm hoping to get is a way to write a generic TryStrConv where the default instantiation returns false and does nothing, while the int and float instantiations, which I want to provide explicitly, use TryStrToInt and TryStrToFloat. Alternatively, if there's a generic conversion facility in Delphi that I'm missing, I'd like to get pointed at it.
Thanks.

You cannot at declaration already fill the generic argument. You either overload with one generic method and one without being generic like this:
function TryStrConv<T>(S: string; var Val: T): boolean; overload;
function TryStrConv(S: string; var Val: Extended): boolean; overload;
But need to be aware that it only picks the non generic one for Extended and not the other floating point types Delphi has like Double or Single.
Another way can be if you are on a version of Delphi XE7 or higher to use the new intrinsic functions to branch the generic methods implementation (it gets resolved at compiletime and the non executed path gets eliminated). It could for example look like this (I omitted the type of the TryStrConv method but you know in Delphi you cannot have generic standalone routines but they have to be methods of some type even if just static):
function TryStrConv<T>(S: string; var Val: T): boolean;
begin
if GetTypeKind(T) = tkFloat then
begin
// do stuff with val being a float type, still need to handle the different float types though
case GetTypeData(TypeInfo(T)) of
ftDouble: DoStuffWithDouble;
// if you need to pass Val here you might need to do some pointer
// ref/deref hardcasts like PDouble(#Val)^ because otherwise you
// are not allowed to cast type T to Double (or any other type)
....
end;
else
Result := False;
end;

You can do something similar like this:
Type
TCalc = record
class function TryStrConv(S: string; var Val: Double): boolean; overload; static;
class function TryStrConv(S: string; var Val: integer): boolean; overload; static;
class function TryStrConv<T>(S: string; var Val: T): boolean; overload; static;
end;
{ TCalc }
class function TCalc.TryStrConv(S: string; var Val: Double): boolean;
begin
Result := TryStrToFloat(s,Val);
end;
class function TCalc.TryStrConv(S: string; var Val: integer): boolean;
begin
Result := TryStrToInt(S,Val);
end;
class function TCalc.TryStrConv<T>(S: string; var Val: T): boolean;
begin
Result := false;
end;
And testing:
var
iVal : Integer;
dVal : Double;
sVal : String;
ok : Boolean;
begin
ok := TCalc.TryStrConv('12',iVal);
WriteLn(ok,' ',iVal); // True 12
ok := TCalc.TryStrConv('12',dVal);
WriteLn(ok,' ',dVal); // True 1.2 E+1
ok := TCalc.TryStrConv('12',sVal);
WriteLn(ok,' ',sVal); // False
ReadLn;
end.
As Stefan says: You will have to write a specific function for each of the float types.

Related

Delphi interface reference count mechanism

Indeed there is a lot of stuff online about this but more I read more confuse I am. I have written a component called Combinatorics that does some math probability stuff. The code is pretty short and easy because I don't want it to be complicated. I am doing a little preview here:
//Combinatorio.pas
type
ICombinatorio = interface
function getSoluzioni(): integer; //soluzioni means "Solutions"
function getFormula(): string;
end;
//ImplCombinatorio.pas
type
TCombinazioni = class(TInterfacedObject, ICombinatorio)
private
n, k: integer;
ripetizione: boolean;
function fattoriale(const x: integer): integer;
public
constructor Create(const n, k: integer; const ripetizione: boolean);
function getSoluzioni(): integer;
function getFormula(): string;
end;
TDisposizioni = class(TInterfacedObject, ICombinatorio)
private
n, k: integer;
ripetizione: boolean;
function fattoriale(const x: integer): integer;
public
constructor Create(const n, k: integer; const ripetizione: boolean);
function getSoluzioni(): integer;
function getFormula(): string;
end;
TPermutazioni = class(TInterfacedObject, ICombinatorio)
private
n: integer;
k: string;
ripetizione: boolean;
function fattoriale(const x: integer): integer;
public
constructor Create(const n: integer; const k: string; ripetizione: boolean);
function getSoluzioni(): integer;
function getFormula(): string;
end;
You don't need to see how functions and procedures are implemented, it's not important for the question (and you can easily imagine what they do).
This is my first component ever, I have compiled and installed it and it works. However I cannot understand something.
unit TCombinatorio;
interface
uses
System.SysUtils, System.Classes, Combinatorio, ImplCombinatorio;
type
cCombinatorio = (cNull = 0, cDisposition = 1, cPermutation = 2, cCombination = 3);
type
TCombinatorics = class(TComponent)
strict private
{ Private declarations }
Fn, Fk: integer;
FRep: boolean;
FType: cCombinatorio;
FEngine: ICombinatorio;
procedure Update;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
function getSolution: integer;
function getFormula: string;
published
property n: integer read Fn write Fn;
property k: integer read Fk write Fk;
property kind: cCombinatorio read FType write FType default cNull;
property repetitions: boolean read FRep write FRep;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('RaffaeleComponents', [TCombinatorics]);
end;
{ TCombinatorics }
constructor TCombinatorics.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Fn := 0;
Fk := 0;
FType := cNull;
repetitions := false;
end;
function TCombinatorics.getFormula: string;
begin
Update;
Result := FEngine.getFormula;
end;
function TCombinatorics.getSolution: integer;
begin
Update;
Result := FEngine.getSoluzioni;
end;
procedure TCombinatorics.Update;
begin
case FType of
cDisposition:
FEngine := TDisposizioni.Create(n, k, repetitions);
cPermutation:
FEngine := TPermutazioni.Create(n, '', repetitions);
cCombination:
FEngine := TCombinazioni.Create(n, k, repetitions);
cNull:
raise Exception.Create('You have to select a type.');
end;
end;
end.
Look at the Update; procedure. I have created that because when the user drops the component ( link ) in the form he has to setup in the object inspector (or with the code somewhere) 3 important parameters required in the constructor.
Since FEngine: ICombinatorio I can assign to it a class (TCombinazioni, TDisposizioni or TPermutazioni) without try finally because there is the ref count mechanism. I am not sure if I have coded this properly. Suppose that:
The user selects cDisposition and does a calculation
The user selects cDisposition (different values) and does a calculation
The user selects cPermutation and does a calculation
I am always working on the FEngine. How does the ref count go to zero? Does it go to zero when the form (and the component) destroys? I hope I have explained well what I don't understand. The FEngine is a private variable and I assing to it at runtime different classes (calling the Create). Does the ref count go to 0 when the form destroys or when a new class is assigned?
I coded it like above because nick hodges did that in his book and I trust him of course but I'd like to know what I do.
Based on the code that can be seen, the first time Update is called, a new implementor of ICombinatorio is created and assigned to FEngine; the reference count will be 1. The following times that Update is called, another new instance of ICombinatorio implementor will be created (its reference count will be 1) and is assigned to FEngine. The previous implementor instance that FEngine pointed to will have its reference count decremented; if it is zero, then it will be destroyed. (It probably will be based on your code sample).
Also, when the destructor of the component is called (when the owning Form is destroyed), the implicit instance clean-up code will set FEngine to nil, which will decrement the reference count (and, based on your sample, will be destroyed).
So, based on your code sample, I would expect your code will work properly; cleanly instanciating and destroying the ICombinatorio interfaced objects.

Function Pointers with different signatures (example: optional parameter with a default value)

Is it possible to create a function-pointer with a default parameter, something like
TFunctionPointer = function(sName:AnsiString; tOptional: TObject = nil):smallint;
What I want to achieve:
A function pointer, which can accept a function of type
function A(sName:AnsiString)
or
function B(sName:AnsiString, tOptional: TObject)
How can I achieve this?
Default parameter is just a syntactic sugar - actually function call has two parameters.
But you can use function references and anonymous methods to create such function pointers - function adapters.
type
fnA = function(const sName: AnsiString): integer;
fnB = function(const sName: AnsiString; const tOptional: TObject); integer;
fnRef = reference to function(const sName: AnsiString; const tOptional: TObject): integer;
fnBridge = record
Bridge: fnRef;
class operator Implicit(fn: fnA): fnBridge;
class operator Implicit(fn: fnB): fnBridge;
end;
class operator fnBridge.Implicit(fn: fnA): fnBridge;
begin
Result.Bridge :=
function(const sName: AnsiString; const tOptional: TObject): integer
begin
Result := fn(sName);
end;
end;
class operator fnBridge.Implicit(fn: fnB): fnBridge;
begin
Result.Bridge :=
function(const sName: AnsiString; const tOptional: TObject): integer
begin
Result := fn(sName, tOptional);
end;
end;
function A(const sName: AnsiString): integer;
begin Result := Length(sName) end;
function B(const sName: AnsiString; const tOptional: TObject): integer;
begin Result := Length(sName) - Length(tOptional.ClassName) end;
function Consumer (const Param1, Param2: integer; const Action: fnBridge): integer;
begin
Result := Param1 + Param2 * Action.Bridge('ABCDE', Application);
end;
....
ShowMessage( IntToStr( Consumer(10, 20, A) ));
ShowMessage( IntToStr( Consumer(10, 20, B) ));
PS: since Delphi version was not specified it means that answer for ANY Delphi version suits fine. This method should work wit hany version starting with Delphi 2009 and later.
PPS: references to functions with captured variables are implemented internally as TInterfacedObject descendants. So overall this is just a reduced case of "Strategy pattern" using "higher-order functions"
http://en.wikipedia.org/wiki/Strategy_pattern
http://en.wikipedia.org/wiki/Higher-order_function
That is not possible. In order for a function to be of type TFunctionPointer, it must declare two parameters.
A default parameter is still a parameter. Your TFunctionPointer is a function with two parameters. When you call it and supply only one parameter, the compiler supplies the default parameter at the call site. So two parameters are still passed to the function.
To expand on this. Consider the following:
procedure Foo(Bar: Integer=666);
begin
end;
When you call the procedure like this:
Foo();
it looks as though the procedure has no parameters. But that is not the case. The compiler translates your code into this:
Foo(666);
The conclusion is that if you want to allow receipt of functions with different numbers of parameters, you'll need to provide an explicit mechanism to receive those different function types. For instance:
procedure DoSomething(const Callback: TProc<string, TObject>); overload;
begin
Callback(str, obj);
end;
procedure DoSomething(const Callback: TProc<string>); overload;
begin
DoSomething(
procedure(arg1: string; arg2: TObject)
begin
Callback(arg1);
end
);
end;

How to Pass Functions as Parameters in Procedures in Delphi?

Is it possible to pass an object function as a parameter in a procedure rather than passing the whole object?
I have a record definition with a function defined as a public class parameter such as:
TMyRecord = record
public
class function New(const a, b: Integer): TMyRecord; static;
function GiveMeAValue(inputValue: integer): Single;
public
a, b: Integer;
end;
The function could be something like:
function TMyRecord.GiveMeAValue(inputValue: Integer): Single;
begin
RESULT := inputValue/(self.a + self.b);
end;
I then wish to define a procedure that calls on the class function GiveMeAValue but I don't want to pass it the whole record. Can I do something like this, for example:
Procedure DoSomething(var1: Single; var2, var3: Integer, ?TMyRecord.GiveMeAValue?);
begin
var1 = ?TMyRecord.GiveMeAValue?(var2 + var3);
//Do Some Other Stuff
end;
If so then how would I correctly pass the function as a procedure parameter?
You can define a new type for the function like
TGiveMeAValue= function(inputValue: integer): Single of object;// this definition works fine for methods for records.
then define the method DoSomething
Procedure DoSomething(var1: Single; var2, var3: Integer;GiveMeAValue: TGiveMeAValue);
begin
writeln(GiveMeAValue(var2 + var3));
end;
And use like so
var
L : TMyRecord;
begin
l.a:=4;
l.b:=1;
DoSomething(1, 20, 5, L.GiveMeAValue);
end;

Implicit cast for overloaded record in Delphi as a parameter in a const array

We got rid of shortstring as part of a conversion from Delphi 7. I wanted to make it as painless as possible so we figured we could change the ShortString to some record which acted in the same way. Here's how it's declared (there's more to it, but this is the basic structure, which outlines the problem):
TShortStringRec = record
private
FStuff: array [0..49] of Char;
public
class operator Implicit(AStuff: TShortStringRec): String;
class operator Implicit(S1: String): TShortStringRec;
end;
This works well for setting strings to the record. But then there's functions like format which take as its parameter const array of const's. Is there any way to do an implict cast to what we'd want to pass into a const array?
function FunkyFunc : string;
var
ssr : TShortStringRec;
begin
ssr := 'Wall';
result := format('Hello %s', [ssr]); //<---error here
end;
Gives a syntax error while compiling because ssr is not a type of parameter that you can use on one of those arrays.
Short answer: No.
Long answer: What you're asking for is that the compiler to somehow know that you want a inherently untyped parameter to be coerced into the type you intend. The compiler simply doesn't have enough information at the call-site to make the determination. If you add an "Explicit" operator and then explicitly cast the parameter to a string, then it will work.
You could add the following to the public declaration :
function AsAnsiString : AnsiString;
function AsShortString : ShortString;
Then explicitly use the cast you want to use :
result := Format('hello %s',[ssr.AsAnsiString]);
I did something very similar in our migration from Delphi 2007 and also discovered that you can't use Format() to pass records to, and after reading the comments it makes perfect sense. An explicit cast (ideally to string) will tell the compiler what to do; the "explicit" methods are not required, however. As for suggestions to use "AsAnsiString": I personally don't like this idea because 1) extra function to write whereas an explicit cast can do the job 2) if readability is important, then so should be consistency, i.e. you do TShortStringRec.AsAnsiString, but do you also need to add an explicit method to set data, like SetAsAnsiString (or just do AsAnsiString as a property)? To me this defeats the point of implicit class operators. I recommend to stick to explicit casts, let the compiler determine which call is correct.
We use a lot of string[] types, so I auto-generated all my records. I thought it would be better to specify a default property to get AnsiChars out of ShortString types rather than have them get converted to UnicodeString then getting the char through [ ], for example:
type
_ShortString3 = string[3]:
ShortString3 = record
private
FData: _ShortString3;
function GetAnsiChar(Index: Integer): AnsiChar;
procedure PutAnsiChar(Index: Integer; const Value: AnsiChar);
public
class operator Implicit(const A: string): ShortString3;
class operator Implicit(const A: ShortString3): string;
class operator Equal(const A: ShortString3; B: AnsiChar): Boolean;
class operator NotEqual(const A: ShortString3; B: AnsiChar): Boolean;
class operator Equal(const A: ShortString3; B: ShortString3): Boolean;
class operator NotEqual(const A: ShortString3; B: ShortString3): Boolean;
class operator Add(const A: ShortString3; B: ShortString3): string;
class operator Add(const A: ShortString3; B: AnsiChar): string;
class operator Add(const A: ShortString3; B: string): string;
property AnsiChars[Index: Integer]: AnsiChar read GetAnsiChar write PutAnsiChar; default;
end;
FWIW here's the implementation:
{ ShortString3 }
function ShortString3.GetAnsiChar(Index: Integer): AnsiChar;
begin
Result := FData[Index];
end;
procedure ShortString3.PutAnsiChar(Index: Integer; const Value: AnsiChar);
begin
FData[Index] := Value;
end;
class operator ShortString3.Implicit(const A: string): ShortString3;
begin
Result.FData := _ShortString3(A);
end;
class operator ShortString3.Implicit(const A: ShortString3): string;
begin
Result := string(A.FData);
end;
class operator ShortString3.Equal(const A: ShortString3; B: AnsiChar): Boolean;
begin
Result := A.FData = B;
end;
class operator ShortString3.NotEqual(const A: ShortString3; B: AnsiChar): Boolean;
begin
Result := A.FData <> B;
end;
class operator ShortString3.Equal(const A: ShortString3; B: ShortString3): Boolean;
begin
Result := A.FData = B.FData;
end;
class operator ShortString3.NotEqual(const A: ShortString3; B: ShortString3): Boolean;
begin
Result := A.FData <> B.FData;
end;
class operator ShortString3.Add(const A: ShortString3; B: ShortString3): string;
begin
Result := string(A.FData + B.FData);
end;
class operator ShortString3.Add(const A: ShortString3; B: AnsiChar): string;
begin
Result := string(A.FData + B);
end;
class operator ShortString3.Add(const A: ShortString3; B: string): string;
begin
Result := string(A.FData) + B;
end;
This has been turned out to be overall a good trick because we didn't manually fiddle with hundreds of files, instead, just wrote 1 file with all our custom ShortString records with implicit class operators. (There was an intermediate step that automatically changed all ShortString types to our own and added the unit StringTypes to the uses, but it was safe.) Thousands of ShortString related warnings disappeared.

How to change a generic type value?

In my application, I've created the TList type list, intended to store Integers or Doubles:
TKList<T> = class
private
FItems: TList<T>;
function GetItem(Index: Integer): T;
procedure SetItem(Index: Integer; const Value: T);
function GetMaxValue(): T;
function GetMinValue(): T;
public
constructor Create; overload;
constructor Create(const AKList: TKList<T>); overload;
destructor Destroy; override;
procedure Assign(const AKList: TKList<T>);
function Add(const AValue: T): Integer;
procedure Clear;
function Count: Integer;
procedure Invert;
function ToString: string; override;
function Info: string;
property Values[Index: Integer]: T read GetItem write SetItem; default;
end;
How can I implement Invert() procedure to invert values in generic List?
Thanks in advance.
Assuming you mean to Reverse the array as in you have values 1, 3, 5 after calling this function you want to have 5, 3, 1
Then, you could implement the procedure like this.
procedure TKList<T>.Invert;
var
I: Integer;
begin
for I := 0 to (Count - 1) div 2 do
FItems.Exchange(I, Count - I - 1);
end;
Altho I would suggest Reverse as it's name, since Invert is kind of confusing.
There's no way to specify constraints on generics such that you can require the types to be numbers, so there's no way you can use numeric operators on the values in your list. Craig Stuntz wrote a series of posts describing how to build a generic statistical library, and he came up against the same problem. He solved it by providing additional arguments to his functions so that the caller could provide implementations for the type-specific numeric operations — the template method pattern. Here's how he declared the Average operation:
type
TBinaryOp<T> = reference to function(ALeft, ARight: T): T
TStatistics<T> = class
public
class function Average(const AData: TEnumerable<T>;
AAdder, ADivider: TBinaryOp<T>;
AMapper: TFunc<integer, T>): T; overload;
Callers of that function need to provide their own code for adding, dividing, and "mapping" the generic type. (Mapping is covered in a later post and isn't important here.) You could write your Invert function like this:
type
TUnaryOp<T> = reference to function(Arg: T): T;
TKList<T> = class
procedure Invert(ANegater: TUnaryOp<T>);
procedure TKList<T>.Invert;
var
i: Integer;
begin
for i := 0 to Pred(Count) do
Values[i] := ANegater(Values[i]);
end;
To make it more convenient to call the methods without having to provide the extra arguments all the time, Stuntz showed how to declare a type-specific descendant that provides the right arguments. You could do it like this:
type
TIntKList = class(TKList<Integer>)
private
class function Negate(Arg: Integer): Integer;
public
procedure Invert;
end;
procedure TIntKList.Invert;
begin
inherited Invert(Negate);
end;
You can provide type-specific descendants for the common numeric types, and if consumers of your class need to use other number-like types, they can provide their own implementations for the basic numeric operations without having to re-implement your entire list class.
Thanks Rob, I got it.
What advantages/disadvantages has the following approach:
procedure TKList<T>.Invert;
var
i: Integer;
Val: TValue;
begin
if TTypeInfo(TypeInfo(T)^).Kind = tkInteger then
begin
for i := 0 to FItems.Count - 1 do
begin
Val := TValue.From<T>(FItems[i]);
TValue.From<Integer>(-Val.AsInteger).AsType<T>;
end;
end
else if TTypeInfo(TypeInfo(T)^).Kind = tkFloat then
begin
for i := 0 to FItems.Count - 1 do
begin
Val := TValue.From<T>(FItems[i]);
FItems[i] := TValue.From<Double>(-Val.AsExtended).AsType<T>;
end;
end;
end;

Resources