What Baltasar proposes will compile, but not do what you want. marriageDate
and divorceDate
will overlap and writing to one of them will also modify the other, since they are simply at the same address.
But in this case, there is no good reason for a variant record at all.
Why not simply:
type
maritalStates = (single, married, widowed, divorced);
TPerson = record
name: record
first,
middle,
last: string;
end;
sex: (male, female);
dob: TDateTime;
maritalStatus: maritalStates; // single, married, widowed, divorced
marriageDate: TDateTime; // married, widowed, divorced
divorceDate : TDateTime; // divorced
isFirstDivorce: boolean; // divorced
end;
The usage and layout is exactly what you need. If a field does not apply (e.g. marriageDate
for a single
, or divorceDate
for a married
), you simply don't use it.
That is the same as with a variant record. There you also only set the fields that apply. Note that the compiler or runtime do not prevent you from writing to the wrong field of a variant record anyway, i.e. in a variant record, if the status is single
, you can still write to or read from divorceDate
, even if that makes no sense.
If you want to distinguish several different setups, simply do that in comments, and forget the variant record, you don't need it here. Now you can do:
var
P: TPerson;
begin
P.name.first := 'Bob';
P.name.middle := 'The';
P.name.last := 'Builder';
P.sex := male;
P.dob := StrToDate('05/05/1980');
P.maritalStatus := divorced;
P.marriageDate := StrToDate('04/01/2013');
P.divorceDate := StrToDate('04/02/2016');
P.isFirstDivorce := True;
// etc...
Update
Just to show that there is absolutely no need to make this record variant,
I will post my Project62.dpr, which shows exactly the same offsets for corresponding fields and the same record sizes:
program Project62;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
maritalStates = (single, married, widowed, divorced);
tsex = (male, female);
// No variant part
PPerson = ^TPerson;
TPerson = record
name: record
first,
middle,
last: string;
end;
sex: tsex;
dob: TDateTime;
maritalStatus: maritalStates; // single, married, widowed, divorced
marriageDate: TDateTime; // married, widowed, divorced
divorceDate : TDateTime; // divorced
isFirstDivorceDate: boolean; // divorced
end;
// Variant part like tonypdmtr's record
PPerson2 = ^TPerson2;
TPerson2 = record
name: record
first,
middle,
last: string;
end;
sex: tsex;
dob: TDateTime;
case maritalStatus: maritalStates of
single: ();
widowed: (w: record marriageDate: TDateTime; end); // overlaps with m.marriageDate and d.marriageDate
married: (m: record marriageDate: TDateTime; end); // overlaps with w.marriageDate and d.marriageDate
divorced: (d: record
marriageDate: TDateTime; // overlaps with w.marriageDate and m.marriageDate
divorceDate: TDateTime; // same offset as in my non-variant version
isFirstDivorceDate: Boolean // same offset as in my non-variant version
end);
end;
begin
try
Writeln('TPerson: size = ', Sizeof(TPerson));
Writeln('TPerson.maritalStatus: offset = ', NativeUInt(@PPerson(nil)^.maritalStatus));
Writeln('TPerson.marriageDate: offset = ', NativeUInt(@PPerson(nil)^.marriageDate));
Writeln('TPerson.divorceDate: offset = ', NativeUInt(@PPerson(nil)^.divorceDate));
Writeln('TPerson.isFirstDivorceDate: offset = ', NativeUInt(@PPerson(nil)^.isFirstDivorceDate));
Writeln;
Writeln('TPerson2: size = ', Sizeof(TPerson2));
Writeln('TPerson2.maritalStatus: offset = ', NativeUInt(@PPerson2(nil)^.maritalStatus));
Writeln('TPerson2.w.marriageDate: offset = ', NativeUInt(@PPerson2(nil)^.w.marriageDate));
Writeln('TPerson2.m.marriageDate: offset = ', NativeUInt(@PPerson2(nil)^.m.marriageDate));
Writeln('TPerson2.d.marriageDate: offset = ', NativeUInt(@PPerson2(nil)^.d.marriageDate));
Writeln('TPerson2.d.divorceDate: offset = ', NativeUInt(@PPerson2(nil)^.d.divorceDate));
Writeln('TPerson2.d.isFirstDivorceDate: offset = ', NativeUInt(@PPerson2(nil)^.d.isFirstDivorceDate));
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
Output (on Windows):
TPerson: size = 56
TPerson.maritalStatus: offset = 24
TPerson.marriageDate: offset = 32
TPerson.divorceDate: offset = 40
TPerson.isFirstDivorceDate: offset = 48
TPerson2: size = 56
TPerson2.maritalStatus: offset = 24
TPerson2.w.marriageDate: offset = 32
TPerson2.m.marriageDate: offset = 32
TPerson2.d.marriageDate: offset = 32
TPerson2.d.divorceDate: offset = 40
TPerson2.d.isFirstDivorceDate: offset = 48
The layout in 32 bit can be put in a simple diagram like so:
00 TPerson: [name.first] TPerson2: [name.first]
04 [name.middle] [name.middle]
08 [name.last] [name.last]
12 [sex] [sex]
16 [dob] [dob]
24 [maritalStatus] [maritalStatus]
32 [marriageDate] [w.marriageDate] [m.marriageDate] [d.marriageDate]
40 [divorceDate] [d.divorceDate]
48 [isFirstDivorceDate] [d.isFirstDivorceDate]