Enumerations, Subranges & Sets

Subranges document intent and, with {$R+}, catch invalid values at run time.

16.1 Enumerated types

Ord(Wed) = 2 Successor / predecessor Succ(Mon) = Tue, Pred(Sun) = Sat Low(TDay) = Mon, High(TDay) = Sun

TFlag = (fNone = 0, fRead = 1, fWrite = 2, fExec = 4); (no Succ/Pred/for over gaps)

{$SCOPEDENUMS ON} then TStatus.Active is required TDay.Mon is allowed even without scoping (Delphi and FPC 3.x) Name as string

GetEnumName(TypeInfo(TDay), Ord(D)) (unit TypInfo); FPC also Writeln(D) and WriteStr(S, D)

type
  TDay = (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
  TTrafficLight = (tlRed, tlYellow, tlGreen);
var
  D: TDay;
begin
  D := Wed;
  Writeln(Ord(D));               // 2
  D := Succ(D);                  // Thu
  D := Pred(D);                  // Wed
  if D < Sat then Writeln('weekday');
  for D := Low(TDay) to High(TDay) do
    Write(Ord(D), ' ');
  D := TDay(4);                  // Fri — unchecked cast
end.
uses TypInfo;
...
Writeln(GetEnumName(TypeInfo(TDay), Ord(Fri)));          // Fri
D := TDay(GetEnumValue(TypeInfo(TDay), 'Sun'));          // string to enum

16.2 Subrange types

Subranges document intent and, with {$R+}, catch invalid values at run time. Assigning an out-of-range constant is a compile-time error.

type
  TPercent = 0..100;
  TWeekday = Mon..Fri;
  TLower = 'a'..'z';
var
  P: TPercent;
begin
  {$R+}
  P := 50;
  P := P + 60;     // run-time range-check error 201 (ERangeError with SysUtils)
end.

16.3 Sets

A set holds any combination of values from an ordinal base type with at most 256 values (ordinals 0..255):

symmetric difference (FPC) equality, inequality subset, superset membership (element in set) Include(S, X), Exclude(S, X) add / remove an element in place

Sets are implemented as bit vectors (one bit per possible element), so operations are very fast. They are perfect for flags (TFontStyles = set of TFontStyle), character classes, and state machines.

type
  TDays = set of TDay;
  TCharSet = set of Char;          // AnsiChar in Delphi 2009+ (a warning reminds you)
const
  Weekend: TDays = [Sat, Sun];
  Digits = ['0'..'9'];
var
  Work, Free, Busy: TDays;
  D: TDay;
begin
  Work := [Mon..Fri];
  Free := Weekend + [Wed];         // union
  Busy := Work - Free;             // difference: [Mon, Tue, Thu, Fri]
  if Wed in Free then Writeln('Wednesday is free');
  Include(Busy, Sat);
  Exclude(Busy, Mon);
  for D in Busy do Write(Ord(D), ' ');
  Writeln;
  if [Sat, Sun] <= Free then Writeln('whole weekend free');
end.

16.4 Example: flags with sets

type
  TPermission = (pRead, pWrite, pExecute, pDelete);
  TPermissions = set of TPermission;

function PermissionString(P: TPermissions): string;
const
  Letters: array[TPermission] of Char = ('r', 'w', 'x', 'd');
var
  Perm: TPermission;
begin
  Result := '';
  for Perm := Low(TPermission) to High(TPermission) do
    if Perm in P then
      Result := Result + Letters[Perm]
    else
      Result := Result + '-';
end;

begin
  Writeln(PermissionString([pRead, pExecute]));   // r-x-
end.