Skip to content

Instantly share code, notes, and snippets.

@gabr42
Last active January 29, 2019 16:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gabr42/ec81b596808f43dbc8d62b2c037818fb to your computer and use it in GitHub Desktop.
Save gabr42/ec81b596808f43dbc8d62b2c037818fb to your computer and use it in GitHub Desktop.
class properties as per-type cache
program MetaClassCache;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.TypInfo,
System.SysUtils;
type
TypeInfoCache<T> = class
class var
FMinIntVal: Integer;
FMaxIntVal: Integer;
public
class constructor Create;
class property MaxIntVal: Integer read FMaxIntVal;
class property MinIntVal: Integer read FMinIntVal;
end;
Range<T> = record
private
class function MaxIntVal: Integer; static; inline;
class function MinIntVal: Integer; static; inline;
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
raise Exception.CreateFmt('Value %d lies outside allowed range for %s (%d .. %d)',
[value, PTypeInfo(TypeInfo(T)).Name, MinIntVal, MaxIntVal]);
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;
{ TypeInfoCache<T> }
class constructor TypeInfoCache<T>.Create;
var
ti: PTypeInfo;
typeData: PTypeData;
isSet: Boolean;
i: Integer;
begin
ti := TypeInfo(T);
isSet := ti.Kind = tkSet;
if isSet then
ti := GetTypeData(ti).CompType^;
typeData := GetTypeData(ti);
FMinIntVal := typeData.MinValue;
if isSet 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
en := Range<TEnum>.Check(0);
en := Range<TEnum>.Check(2);
try
en := Range<TEnum>.Check(3);
except
on E: Exception do
Writeln('Expected exception: ', E.ClassName, ' ', E.Message);
end;
ens := Range<TEnumSet>.Check(0);
ens := Range<TEnumSet>.Check(7);
try
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment