Fabrizio
Fabrizio

Reputation: 8043

How to check if an integer can be converted to an enumeration type value?

I have defined an InRange function on my enumerator type. The function should return True if the passed integer parameter can be converted to the enumerator type.

  TMyEnum = (eA, eB);
  TMyEnumHelper = record helper for TMyEnum
    class function InRange(AValue : integer) : Boolean; static;
  end;

...

class function TMyEnumHelper.InRange(AValue : integer) : Boolean;
begin
  Result :=
    (AValue >= Low(TMyEnum)) and
    (AValue <= High(TMyEnum));
end;

On compilation, at the line (AValue >= Low(TMyEnum)), I get the following error:

[dcc32 Error] Unit1.pas(34): E2008 Incompatible types

I did some tests but I really don't understand what's wrong... i.e:

  1. I've tried switching the AValue parameter type of InRange function to Byte, ShortInt, Word, SmallInt, LongWord, Cardinal, LongInt, Integer and Int64, but it raises the same error on compiling.
  2. If I define the enumerator as TMyEnum = 0..1;, it compiles without errors.

Upvotes: 2

Views: 852

Answers (2)

saastn
saastn

Reputation: 6015

You can also use generics instead of helpers to make InRange support all enumeration types:

uses
  SysUtils, TypInfo;

type
  TMyEnum1 = (me1A, me1B);
  TMyEnum2 = (me2A, me2B, me2C);
  TMyEnum3 = (me3A = 1, me3B = 3);

  TEnum = class
    class function InRange<T>(const AValue: Integer): Boolean; static;
  end;

{ TEnum }

class function TEnum.InRange<T>(const AValue: Integer): Boolean;
var
  TI: PTypeInfo;
  TD: PTypeData;
begin
  TI := TypeInfo(T);

  if not Assigned(TI) then
    raise Exception.Create('InRange does not support discontinuous enumerations.');

  if TI^.Kind <> tkEnumeration then
    raise Exception.Create('InRange only supports enumeration types.');

  TD := GetTypeData(TI);
  Result :=
    (AValue >= TD^.MinValue) and
    (AValue<=TD^.MaxValue);
end;

begin
  try
    Writeln(BoolToStr(TEnum.InRange<TMyEnum1>(2), true));
    Writeln(BoolToStr(TEnum.InRange<TMyEnum2>(2), true));
    Writeln(BoolToStr(TEnum.InRange<TMyEnum3>(2), true));
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
    Readln;
end.

That produces:

False
True
Exception: InRange does not support discontinuous enumerations.

Notice that your current approach will return True for AValue=2 if it was implemented for TMyEnum3.

Upvotes: 4

HeartWare
HeartWare

Reputation: 8243

You can't compare an enumerated value with an integer directly. You'll have to convert the enumerated value to an integer value in order to do the comparison:

class function TMyEnumHelper.InRange(AValue : integer) : Boolean;
begin
  Result :=
    (AValue >= Ord(Low(TMyEnum))) and
    (AValue <= Ord(High(TMyEnum)));
end;

Notice the added "ord" cast, which converts its "parameter" (the expression within the parentheses) to an integer value.

The reason your

TMyEnum = 0..1;

works is that this isn't an enumeration, but an integer sub-range, and thus the base type of TMyEnum is an integer and not an enumeration.

Upvotes: 7

Related Questions