Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is Fluent call style self referencing records in Delphi possible?

Tags:

types

delphi

The goal is to create a type called TURLString to be called as follows:

var
  newURl : TURLString;
begin

  newURL.Append('http://').Append('www.thehost.com').Append('path/on/server').Append('?');

  ...lots of app logic...

  newURL.AppendParam('name', 'value').Append('#').AppendParam('name', 'value');

  ...more params added...

  result := httpClient.Get(newURL);

end;

With TURLString defined like this (note its a record):

//from actual code used
TURLString = record
private
    FString : string;
public
    function Append(APart : string) : TURLString;
    function AppendParam(AParam, AValue : string) : TURLString;
end;

function TURLString.Append(APart: string) : TURLString;
begin
  FString := FString + APart;
  result := self;
end;

function TURLString.AppendParam(AParam, AValue: string): TURLString;
begin
  if (not Empty) then
    FString := FString + URL_AMB;
  FString := FString + AParam + '=' + AValue;
  result := self;
end;

When stepping through the fluid calls, the values are appended but when exiting they revert to the first string passed into the first append call and newURL is equal to 'http://' while debugging the append call you see 'http://www.thehost.com/path/on/server?name=value#name=value'.

Is this concept possible with a record?

like image 366
MX4399 Avatar asked Dec 27 '22 16:12

MX4399


1 Answers

To get rid of the performance and memory use problem of record copy, you may use pointers as results type for your methods:

type
  PURLString = ^TURLString;
  TURLString = record
  private
    FString : string;
  public
    function Append(const APart : string) : PURLString;
    function AppendParam(const AParam, AValue : string) : PURLString;
  end;

function TURLString.Append(const APart: string) : PURLString;
begin
  FString := FString + APart;
  result := @self;
end;

function TURLString.AppendParam(const AParam, AValue: string): PURLString;
begin
  if FString <> '' then
    FString := FString + URL_AMB;
  FString := FString + AParam + '=' + AValue;
  result := @self;
end;

So you may be able to call your original TURLString just as wished:

newURL.Append('http://').Append('www.thehost.com').Append(...

And don't forget the const keyword for string parameters of methods.

Note that the proper way of using a pointer should have been

newURL.Append('http://')^.Append('www.thehost.com')^.Append(...

but in fact the Delphi compiler is clever enough to add the ^ sign implicitly.

With this trick, you don't have to create nor free your newURL instance. Note that only reference-counted parameters (string, variants, interfaces) will be initialized on the stack: integers or doubles will be pure random.

This is exactly what I used for creating some RTF content in my SynProject documentation tool (at least in the first versions, now I use a class instead of a record, since I wanted to add inheritance).

like image 140
Arnaud Bouchez Avatar answered Mar 15 '23 23:03

Arnaud Bouchez