[fpc-pascal] Tuples as variant arrays

Sven Barth pascaldragon at googlemail.com
Sat Jun 26 15:57:55 CEST 2021


Am 26.06.2021 um 00:12 schrieb Ryan Joseph via fpc-pascal:
> Is it possible something like this could work? Seems like it should but I get an error (got MyRecord expected variant).
>
> ====================================
>
> {$mode objfpc}
>
> program unit_name;
>
> type
>    TTuple = array of variant;
>
> type
>    MyRecord = record
>    end;
>
> var
>    t: TTuple;
>    r: MyRecord;
>    i: variant;
> begin
>    t := [1,'string', r];
>    for i in t do
>      begin
>        writeln(i);
>      end;
> end.

Variants by themselves can not handle records, because they can't carry 
type information. However by using TCustomVariantType one can come 
rather close though one still needs to some things manually:

=== code begin ===

program tvarrec;

{$mode objfpc}{$H+}
{$modeswitch advancedrecords}

uses
   Variants;

type
   generic TRecVariantType<T: record> = class(TCustomVariantType)
   private type
     PT = ^T;
   public
     procedure Copy(var Dest: TVarData; const Source: TVarData;
       const Indirect: Boolean); override;
     procedure Clear(var V: TVarData); override;
     function ToVariant(const aArg: T): Variant;
     function FromVariant(const aArg: Variant): T;
   end;

{ TRecVariantType }

procedure TRecVariantType.Copy(var Dest: TVarData; const Source: TVarData;
   const Indirect: Boolean);
begin
   New(PT(Dest.vrecord));
   PT(Dest.vrecord)^ := PT(Source.vrecord)^;
end;

procedure TRecVariantType.Clear(var V: TVarData);
begin
   Dispose(PT(V.vrecord));
   V.vtype:=varEmpty;
   V.vrecord:=Nil;
end;

function TRecVariantType.ToVariant(const aArg: T): Variant;
begin
   TVarData(Result).vtype := VarType;
   New(PT(TVarData(Result).vrecord));
   PT(TVarData(Result).vrecord)^ := aArg;
end;

function TRecVariantType.FromVariant(const aArg: Variant): T;
begin
   if TVarData(aArg).VType <> VarType then
     EVariantTypeCastError.Create('Not a suitable record');
   Result := PT(TVarData(aArg).vrecord)^;
end;

type
   TMyRec = record
     f: LongInt;
     s: String;
     class operator := (const aOther: TMyRec): Variant;
     class operator := (const aOther: Variant): TMyRec;
   end;

   TMyRecVarType = specialize TRecVariantType<TMyRec>;

var
   myrectype: TMyRecVarType;

{ TMyRec }

class operator TMyRec.:=(const aOther: TMyRec): Variant;
begin
   Result := myrectype.ToVariant(aOther);
end;

class operator TMyRec.:=(const aOther: Variant): TMyRec;
begin
   Result := myrectype.FromVariant(aOther);
end;

var
   v: Variant;
   varr: array of Variant;
   m, m2, m3: TMyRec;
   i: LongInt;
begin
   myrectype := TMyRecVarType.Create;
   try
     m.f := 42;
     m.s := 'Hello World';

     v := m;

     m2 := v;

     Writeln(m2.f, ' ', m2.s);

     varr := [1, 'Foobar', m];

     for i := Low(varr) to High(varr) do
       try
         m3 := varr[i];
         Writeln('Entry ', i, ': ', m3.f, ' ', m3.s);
       except
         Writeln('Entry ', i, ': failed to cast');
       end;
   finally
     myrectype.Free;
   end;
end.

=== code end ===

This code is merely a proof of concept and might be further improved.

Regards,
Sven


More information about the fpc-pascal mailing list