Hi Primož,
that's a nice piece of code, but i notice in my Delphi Tokyo, that the ens-Result for the TEnumSet (for valid values) is empty.
But if you add a untyped Move, than it works:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.TypInfo,
System.Rtti,
System.SysUtils;
resourcestring
SValueLiesOutsideAllowedRange = 'Value %d lies outside allowed range for %s (%d .. %d)';
type
TypeInfoCache<T> = class
class var
FMinIntVal: Integer;
FMaxIntVal: Integer;
FIsSet: Boolean;
public
class constructor Create;
class property MaxIntVal: Integer read FMaxIntVal;
class property MinIntVal: Integer read FMinIntVal;
class property IsSet: Boolean read FIsSet;
end;
Range<T> = record
private
class function MaxIntVal: Integer; static; inline;
class function MinIntVal: Integer; static; inline;
class procedure RaiseException(const Value: Integer); static;
public
class function Check(const Value: Integer): T; static;
end;
{ Range<T> }
class function Range<T>.Check(const Value: Integer): T;
begin
if (Value < MinIntVal) or (Value > MaxIntVal) then
RaiseException(Value);
if TypeInfoCache<T>.IsSet then
begin
Move(Value, Result, SizeOf(T)); // here is the magic
end;
end;
class function Range<T>.MaxIntVal: Integer;
begin
Result := TypeInfoCache<T>.MaxIntVal;
end;
class function Range<T>.MinIntVal: Integer;
begin
Result := TypeInfoCache<T>.MinIntVal;
end;
class procedure Range<T>.RaiseException(const Value: Integer);
begin
raise Exception.CreateFmt(SValueLiesOutsideAllowedRange,
[Value, PTypeInfo(TypeInfo(T)).Name, MinIntVal, MaxIntVal]);
end;
{ TypeInfoCache<T> }
class constructor TypeInfoCache<T>.Create;
var
ti: PTypeInfo;
typeData: PTypeData;
i: Integer;
begin
ti := TypeInfo(T);
FIsSet := ti.Kind = tkSet;
if FIsSet then
ti := GetTypeData(ti).CompType^;
typeData := GetTypeData(ti);
FMinIntVal := typeData.MinValue;
if FIsSet then
begin
FMaxIntVal := 0;
for i := typeData.MinValue to typeData.MaxValue do
FMaxIntVal := FMaxIntVal or (1 shl i);
end
else
FMaxIntVal := typeData.MaxValue;
end;
type
TEnum = (en1, en2, en3);
TEnumSet = set of TEnum;
var
en: TEnum;
ens: TEnumSet;
begin
try
try
en := Range<TEnum>.Check(0);
en := Range<TEnum>.Check(2);
en := Range<TEnum>.Check(3);
except
on E: Exception do
Writeln('Expected exception: ', E.ClassName, ' ', E.Message);
end;
try
ens := Range<TEnumSet>.Check(0);
ens := Range<TEnumSet>.Check(2);
ens := Range<TEnumSet>.Check(7);
ens := Range<TEnumSet>.Check(8);
except
on E: Exception do
Writeln('Expected exception: ', E.ClassName, ' ', E.Message);
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
if DebugHook <> 0 then
Readln;
end.