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;