TForge 0.75 is released.
Starting from ver. 0.75 TForge consists of a single package; that means simplified installation.
Among other features:
- cryptographically secure pseudo-random generator TRandom
- a lot of minor improvements on BigInteger
TForge 0.75 is released.
Starting from ver. 0.75 TForge consists of a single package; that means simplified installation.
Among other features:
I’ve posted before what the “out” parameter specifier is actually doing in a Delphi code. Now let us consider when the “out” keyword should be used instead of “var”.
There is only one case when the “out” keyword should be used. It is closely related to COM technology support, but it is not really limited to COM. The case is importing a function with a parameter of interface type which violates Delphi contract on reference counting. To illustrate the point consider the following DLL:
library DemoDll; uses SysUtils; type PMyUnknown = ^TMyUnknown; TMyUnknown = record FVTable: PPointer; FRefCount: Integer; class function QueryIntf(Inst: Pointer; const IID: TGUID; out Obj): HRESULT; stdcall; static; class function AddRef(Inst: PMyUnknown): Integer; stdcall; static; class function Release(Inst: PMyUnknown): Integer; stdcall; static; end; const VTable: array[0..2] of Pointer = ( @TMyUnknown.QueryIntf, @TMyUnknown.Addref, @TMyUnknown.Release); function ReleaseInstance(Inst: Pointer): Integer; type TVTable = array[0..2] of Pointer; PVTable = ^TVTable; PPVTable = ^PVTable; TRelease = function(Inst: Pointer): Integer; stdcall; begin Result:= TRelease(PPVTable(Inst)^^[2])(Inst); end; class function TMyUnknown.QueryIntf(Inst: Pointer; const IID: TGUID; out Obj): HRESULT; begin Result:= E_NOINTERFACE; end; class function TMyUnknown.Addref(Inst: PMyUnknown): Integer; begin Inc(Inst.FRefCount); Result:= Inst.FRefCount; end; class function TMyUnknown.Release(Inst: PMyUnknown): Integer; begin Dec(Inst.FRefCount); Result:= Inst.FRefCount; if Result = 0 then FreeMem(Inst); end; procedure GetIUnknown1(var Inst: PMyUnknown); var P: PMyUnknown; begin New(P); P^.FVTable:= @VTable; P^.FRefCount:= 1; if Inst <> nil then ReleaseInstance(Inst); Inst:= P; end; procedure GetIUnknown2(var Inst: PMyUnknown); begin New(Inst); Inst^.FVTable:= @VTable; Inst^.FRefCount:= 1; end; exports GetIUnknown1, GetIUnknown2; {$R *.res} begin ReportMemoryLeaksOnShutdown:= True; end.
The DLL exports 2 procedures which create instances implementing IUnknown. The first procedure (GetIUnknown1) follows Delphi rules on reference counting, the second (GetIUnknown2) does not. To use the procedures in an executable we need an import unit:
unit DemoImport; interface procedure GetIUnknown1(var I: IUnknown); //procedure GetIUnknown2(var I: IUnknown); // memory leak procedure GetIUnknown2(out I: IUnknown); // ^^^! implementation procedure GetIUnknown1(var I: IUnknown); external 'DemoDLL' name 'GetIUnknown1'; //procedure GetIUnknown2(var I: IUnknown); external 'DemoDLL' name 'GetIUnknown2'; procedure GetIUnknown2(out I: IUnknown); external 'DemoDLL' name 'GetIUnknown2'; end.
Note that the second procedure is imported with out parameter specifier; the result of replacing “out” by “var” is a memory leak which can be shown by the test application:
program DemoEXE; {$APPTYPE CONSOLE} uses SysUtils, DemoImport in 'DemoImport.pas'; procedure Test; var I: IUnknown; begin GetIUnknown1(I); GetIUnknown2(I); end; begin Test; Readln; end.
C# implements BigInteger.Parse as a static class method, and there is obvious reason for it: you can call a static class methods in a variable’s declaration:
BigInteger N = BigInteger.Parse(stringToParse);
Delphi/Pascal does not support the above syntax, so the equivalent code is
var N: BigInteger; begin N:= BigInteger.Parse(stringToParse);
But now it looks like implementing BigInteger.Parse as a static class method is nothing but additional typing; using an instance method looks better:
var N: BigInteger; begin N.Parse(stringToParse);
So, what is the right “Delphi way” of implementing BigInteger.Parse – as a static class method or as an instance method?