[fpc-pascal] interfaces and smartpointers [was Traits Proposal]

Benito van der Zander benito at benibela.de
Wed Feb 17 16:27:08 CET 2021


Hi,

>
> (1) A record is not a pointer. So that would require some implicit 
> referencing in the property
>
> (2) If it was managed, it would be an allocation, so I don't 
> understand this.


I am talking about replacing interfaces with an record.

For example this

/////////////////////////////////////

type ITest = interface
   procedure println;
end;
TTest = class(TInterfacedObject, ITest)
   v: integer;
   constructor Create(av: integer);
   procedure println;
end;

constructor TTest.Create(av: integer);
begin
   v := av;
end;

procedure TTest.println;
begin
   writeln(v);
end;

var c: ITest;
begin
   c := TTest.Create(123);
   c.println;
/////////////////////////////////


would become:


/////////////////////////////////

type

TTestRec = class
   rc: Integer;
   v: integer;
   constructor Create(av: integer);
   procedure println;
end;
RTest = record
   ptr: TTestRec;
   procedure println; inline;
   class operator :=(c: TTestRec): RTest;
   class operator Initialize(var aRec: RTest);
   class operator finalize(var aRec: RTest);
   class operator AddRef(var aRec: RTest);
end;

constructor TTestRec.Create(av: integer);
begin
   v := av;
end;

procedure TTestRec.println;
begin
   writeln(v);
end;

procedure RTest.println;
begin
   ptr.println; //the wrapper function is inlined
end;

class operator RTest.:=(c: TTestRec): RTest;
begin
   result := default(RTest);
   result.ptr := c;
   if c <> nil then
     InterlockedIncrement(c.rc);
end;

class operator RTest.Initialize(var aRec: RTest);
begin
   aRec.ptr := nil;
end;

class operator RTest.finalize(var aRec: RTest);
begin
   if aRec.ptr <> nil then
     if InterlockedDecrement(aRec.ptr.rc) = 0 then
       aRec.ptr.Free;
end;

class operator RTest.AddRef(var aRec: RTest);
begin
   if aRec.ptr <> nil then
     InterlockedIncrement(aRec.ptr.rc);
end;

var
     r: RTest;
begin
   r := TTestRec.Create(123);
   r.println;
/////////////////////////////////////////////////////////

Or even replace the class with a record, too:


/////////////////////////////////////////////////////////

type
PTestRec2 = ^TTestRec2;
TTestRec2 = record
   rc: Integer;
   v: integer;
   class function Create(av: integer): PTestRec2; static;
   procedure println;
end;
RTest2 = record
   ptr: ^TTestRec2;
   procedure println; inline;
   class operator :=(c: PTestRec2): RTest2;
   class operator Initialize(var aRec: RTest2);
   class operator finalize(var aRec: RTest2);
   class operator AddRef(var aRec: RTest2);
end;

class function TTestRec2.Create(av: integer): PTestRec2;
begin
   new(result);
   result^.rc := 0;
   result^.v := av;
end;

procedure TTestRec2.println;
begin
   writeln(v);
end;

procedure RTest2.println;
begin
   ptr^.println;
end;

class operator RTest2.:=(c: PTestRec2): RTest2;
begin
   result := default(RTest2);
   result.ptr := c;
   if c <> nil then
     InterlockedIncrement(c^.rc);
end;

class operator RTest2.Initialize(var aRec: RTest2);
begin
   aRec.ptr := nil;
end;

class operator RTest2.finalize(var aRec: RTest2);
begin
   if aRec.ptr <> nil then
     if InterlockedDecrement(aRec.ptr^.rc) = 0 then
       dispose(aRec.ptr);
end;

class operator RTest2.AddRef(var aRec: RTest2);
begin
   if aRec.ptr <> nil then
     InterlockedIncrement(aRec.ptr^.rc);
end;
var
     r2: RTest2;
begin
   r2 := TTestRec2.Create(123);
   r2.println;
/////////////////////////////////////////////////////////




Not sure if it is actually faster. That needs to be investigated.

But it definitely helps with the memory usage:

   writeln(ttest.InstanceSize);
   writeln(ttestrec.InstanceSize);
   writeln(sizeof(ttestrec2));

40
16
8

With many small objects it should be faster just because it fits better 
in the cache.

Cheers,
Benito
On 17.02.21 14:31, Marco van de Voort via fpc-pascal wrote:
>
> Op 2021-02-17 om 00:02 schreef Benito van der Zander via fpc-pascal:
>>>
>>> And there often is a lot of unintentional deep copying. This is also 
>>> why a property returning a record is fairly useless except for 
>>> extremely small records like TPoint (and even that is not optimal no 
>>
>> But a managed record to replace an interface, would only contain a 
>> single pointer/class ref. That can be copied fast
>
> (1) A record is not a pointer. So that would require some implicit 
> referencing in the property
>
> (2) If it was managed, it would be an allocation, so I don't 
> understand this.
>
>
> _______________________________________________
> fpc-pascal maillist  -  fpc-pascal at lists.freepascal.org
> https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freepascal.org/pipermail/fpc-pascal/attachments/20210217/a196a510/attachment.htm>


More information about the fpc-pascal mailing list