Home > Net >  Record type to temporary variable
Record type to temporary variable

Time:02-03

How to store record to temporary variable and pass it through function?

If I have two records like:

TMyRec1 = packed record
  SomeValue : Integer;
end;

TMyRec2 = packed record
  ThisIsMessage : String;
end;

And now I want to be able to do something like this:

function GetRec(recId: Integer) : Variant;
begin
  case (recId) of
    1 : Result := TMyRec1.Create();
    2 : Result := TMyRec2.Create();
    //... many
  else
    
  end;
end;

And also to return it back to original type like:

function GetRec1(rec: Variant) : TMyRec1;
begin
  Result := TMyRec1(rec);
  // here I do lots of default things with this record type
end;

function GetRec2(rec: Variant) : TMyRec2;
begin
  Result := TMyRec2(rec);
  // here I do lots of default things with this record type
end;

Finally an complete function should be able to do the following:

procedure MainFunction();
var myRec : Variant; //I want to avoid to specify each T here
begin
  myRec := GetRec(1);
  PrintRec1(GetRec1(myRec));

  myRec := GetRec(2);
  PrintRec2(GetRec2(myRec));
end;

procedure PrintRec1(rec: TMyRec1);
begin
  Print(IntToStr(rec.SomeValue));
end;

procedure PrintRec2(rec: TMyRec2);
begin
  Print(rec.ThisIsMessage);
end;

I have tried with Variant, TObject, NativeUInt casting but nothing seem to work.

Thank you for any help.

EDIT

TMyRec = record
end;

TMyRec1 = TMyRec
  SomeValue : Integer;
end;

TMyRec2 = TMyRec
  ThisIsMessage : String;
end;

Can be done something like this?

I don't need safety checking and rising exceptions I will take care of that to make sure I pass correct one where required.

CodePudding user response:

A record does not have a Create() constructor by default, like a class does, so TMyRec1.Create() and TMyRec2.Create() will not work as shown.

But, in Delphi 2006 and later, you can manually add a static Create() method that returns a new record instance (several of Delphi's own native RTL records do this, such as TFormatSettings, TRttiContext, etc), eg:

TMyRec1 = packed record
  SomeValue : Integer;
  class function Create: TMyRec1; static;
end;

TMyRec2 = packed record
  ThisIsMessage : String;
  class function Create: TMyRec2; static;
end;

...

class function TMyRec1.Create: TMyRec1;
begin
  Result.SomeValue := ...;
end;

class function TMyRec2.Create: TMyRec2;
begin
  Result.ThisIsMessage := ...;
end;

Otherwise, for earlier versions, you will have to use standalone functions, eg:

TMyRec1 = packed record
  SomeValue : Integer;
end;

TMyRec2 = packed record
  ThisIsMessage : String;
end;

function CreateRec1: TMyRec1;
function CreateRec2: TMyRec2;

...

function CreateRec1: TMyRec1;
begin
  Result.SomeValue := ...;
end;

function CreateRec2: TMyRec2;
begin
  Result.ThisIsMessage := ...;
end;

But, either way, know that by default you can't just store arbitrary record types in a Variant, it doesn't know how to store and retrieve them. You have to teach it how to do that. You do that by deriving a class from TCustomVariantType and override its various operational methods for casting, comparing, etc, and then register that class with the RTL so that the Variant infrastructure knows about it. See Defining Custom Variants in Delphi's documentation for more details about that. Only then will your GetRec(), GetRec1(), and GetRec2() functions be able to work exactly as you have coded them.

Otherwise, consider an alternative approach, for instance defining a custom tagged record, similar to how Variant works internally, eg:

PMyRec1 = ^TMyRec1;
TMyRec1 = packed record
  SomeValue : Integer;
end;

PMyRec2 = ^TMyRec2;
TMyRec2 = packed record
  ThisIsMessage : String;
end;

TMyRec = record
  case Tag: Integer of
    1 : (Rec1: PMyRec1);
    2 : (Rec2: PMyRec2);
    ...
end;

function GetRec(recId: Integer) : TMyRec;
begin
  Result.Tag := recId;
  case recId of
    1 : New(Result.Rec1);
    2 : New(Result.Rec2);
    ...
  else
    raise ...;    
  end;
end;

function DisposeRec(var rec: TMyRec);
begin
  case rec.Tag of
    1 : Dispose(rec.Rec1);
    2 : Dispose(rec.Rec2);
    ...
  end;
  rec.Tag := 0;
end;

function GetRec1(var rec: TMyRec) : TMyRec1;
begin
  if rec.Tag <> 1 then raise ...;
  Result := rec.Rec1^;
  // here I do lots of default things with this record type
end;

function GetRec2(var rec: TMyRec) : TMyRec2;
begin
  if rec.Tag <> 2 then raise ...;
  Result := rec.Rec2^;
  // here I do lots of default things with this record type
end;

procedure MainFunction;
var
  myRec : TMyRec;
begin
  myRec := GetRec(1);
  try
    PrintRec1(GetRec1(myRec));
  finally
    DisposeRec(myRec);
  end;

  myRec := GetRec(2);
  try
    PrintRec2(GetRec2(myRec));
  finally
    DisposeRec(myRec);
  end;
end;

procedure PrintRec1(const rec: TMyRec1);
begin
  Print(IntToStr(rec.SomeValue));
end;

procedure PrintRec2(const rec: TMyRec2);
begin
  Print(rec.ThisIsMessage);
end;
  • Related