Skip to content

Instantly share code, notes, and snippets.

@ortuagustin
Created December 26, 2017 22:06
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ortuagustin/f7f53dfa4e149047afb2340c6508646d to your computer and use it in GitHub Desktop.
Save ortuagustin/f7f53dfa4e149047afb2340c6508646d to your computer and use it in GitHub Desktop.
Enum.Wrapper.pas
unit Enum.Wrapper;
interface
uses
System.SysUtils,
System.TypInfo;
type
EEnumOutOfRange = class(System.SysUtils.EArgumentOutOfRangeException);
EEnumParseError = class(System.SysUtils.Exception);
{$REGION 'EnumWrapper<T>'}
EnumWrapper<T: record {: enum}> = record
strict private
FEnumValue: T;
constructor Create(const Value: T);
public
class operator Implicit(const Value: T): EnumWrapper<T>; inline;
class operator Implicit(const Value: Integer): EnumWrapper<T>; inline;
class operator Implicit(const Value: string): EnumWrapper<T>; inline;
class operator Implicit(const Value: EnumWrapper<T>): string; inline;
class operator Implicit(const Value: EnumWrapper<T>): Integer; inline;
class operator Implicit(const Value: EnumWrapper<T>): T; inline;
function ToInteger: Integer; inline;
function ToString: string; inline;
property Value: T read FEnumValue;
end;
{$ENDREGION}
{$REGION 'TEnumEnumerator'}
TEnumEnumerator<T: record {: enum}> = record
private
FCurrentIndex: Integer;
function GetCurrent: T;
public
constructor Create(const Dummy: Integer);
function MoveNext: Boolean;
property Current: T read GetCurrent;
end;
{$ENDREGION}
{$REGION 'TEnumIterator'}
TEnumIterator<T: record {: enum}> = record
public
function GetEnumerator: TEnumEnumerator<T>; inline;
end;
{$ENDREGION}
{$REGION 'Enum<T>'}
Enum<T: record {: enum}> = record
strict private
class function EnumTypeInfo: PTypeInfo; static; inline;
class function EnumTypeData: PTypeData; static; inline;
class procedure ValueOutOfRange(const Value: T; const Namespace, MethodName: string); static;
class procedure IntegerOutOfRange(const Value: Integer; const Namespace, MethodName: string); static;
public
class function Iterate: TEnumIterator<T>; static; inline;
class function TypeName: string; static; inline;
class function ValueName(const Value: T): string; static; inline;
class function Parse(const Ordinal: Integer): EnumWrapper<T>; overload; static; inline;
class function Parse(const EnumValueName: string): EnumWrapper<T>; overload; static; inline;
class function ToInteger(const Value: T): Integer; static; inline;
class function MaxValue: Integer; static; inline;
class function MinValue: Integer; static; inline;
class function InRange(const Value: T): Boolean; overload; static;
class function InRange(const Value: Integer): Boolean; overload; static;
class procedure CheckInRange(const Value: T; const MethodName: string); overload; static;
class procedure CheckInRange(const Value: Integer; const MethodName: string); overload; static;
class function Count: Integer; static;
class function AsArray: TArray<EnumWrapper<T>>; static;
class function First: EnumWrapper<T>; static;
class function Last: EnumWrapper<T>; static;
class procedure StringParseError(const Value: string); static;
end;
{$ENDREGION}
implementation
uses
System.Math;
{$REGION 'Enum<T>'}
class function Enum<T>.InRange(const Value: T): Boolean;
begin
Result := InRange(ToInteger(Value));
end;
class function Enum<T>.InRange(const Value: Integer): Boolean;
begin
Result := System.Math.InRange(Value, Enum<T>.MinValue, Enum<T>.MaxValue);
end;
class function Enum<T>.MaxValue: Integer;
begin
Result := Enum<T>.EnumTypeData.MaxValue;
end;
class function Enum<T>.MinValue: Integer;
begin
Result := Enum<T>.EnumTypeData.MinValue;
end;
class function Enum<T>.Last: EnumWrapper<T>;
begin
Result := Enum<T>.Parse(MaxValue);
end;
class function Enum<T>.First: EnumWrapper<T>;
begin
Result := Enum<T>.Parse(MinValue);
end;
class function Enum<T>.Iterate: TEnumIterator<T>;
begin
// managed type by compiler, no need to create
end;
class function Enum<T>.ToInteger(const Value: T): Integer;
begin
Result := 0;
System.Move(Value, Result, System.SizeOf(Value));
end;
class function Enum<T>.TypeName: string;
begin
{$IFDEF NEXTGEN}
Result := System.TypInfo.GetTypeName(Enum<T>.EnumTypeInfo);
{$ELSE NEXTGEN}
Result := string(Enum<T>.EnumTypeInfo.Name);
{$ENDIF NEXTGEN}
end;
class function Enum<T>.ValueName(const Value: T): string;
begin
Result := System.TypInfo.GetEnumName(Enum<T>.EnumTypeInfo, Enum<T>.ToInteger(Value));
end;
class function Enum<T>.EnumTypeData: PTypeData;
begin
Result := System.TypInfo.GetTypeData(Enum<T>.EnumTypeInfo);
end;
class function Enum<T>.EnumTypeInfo: PTypeInfo;
begin
Result := System.TypeInfo(T);
end;
class procedure Enum<T>.CheckInRange(const Value: T; const MethodName: string);
begin
if not Enum<T>.InRange(Value) then
Enum<T>.ValueOutOfRange(Value, Enum<T>.TypeName, MethodName);
end;
class procedure Enum<T>.CheckInRange(const Value: Integer; const MethodName: string);
begin
if not Enum<T>.InRange(Value) then
Enum<T>.IntegerOutOfRange(Value, Enum<T>.TypeName, MethodName);
end;
class function Enum<T>.Count: Integer;
begin
Result := Enum<T>.MaxValue - Enum<T>.MinValue + 1;
end;
class function Enum<T>.Parse(const Ordinal: Integer): EnumWrapper<T>;
begin
Assert(System.SizeOf(Result) <= System.SizeOf(Ordinal));
System.Move(Ordinal, Result, System.SizeOf(Result));
end;
class function Enum<T>.Parse(const EnumValueName: string): EnumWrapper<T>;
var
Each: T;
begin
for Each in Enum<T>.AsArray do
begin
if Enum<T>.ValueName(Each) = EnumValueName then
Exit(Each);
end;
StringParseError(EnumValueName);
end;
class function Enum<T>.AsArray: TArray<EnumWrapper<T>>;
var
I: Integer;
begin
System.SetLength(Result, Enum<T>.Count);
for I := System.Low(Result) to System.High(Result) do
Result[I] := Enum<T>.Parse(I);
end;
class procedure Enum<T>.StringParseError(const Value: string);
const
SCannotParseString = '%s is not defined in enum %s';
begin
raise EEnumParseError.CreateFmt(SCannotParseString, [Value, TypeName]);
end;
class procedure Enum<T>.ValueOutOfRange(const Value: T; const Namespace, MethodName: string);
begin
IntegerOutOfRange(ToInteger(Value), Namespace, MethodName);
end;
class procedure Enum<T>.IntegerOutOfRange(const Value: Integer; const Namespace, MethodName: string);
const
SEnumOutOfRange = '%s.%s :: %d is out of range for enum %s';
begin
raise EEnumOutOfRange.CreateFmt(SEnumOutOfRange, [Namespace, MethodName, Value, TypeName]);
end;
{$ENDREGION}
{$REGION 'EnumWrapper<T>'}
constructor EnumWrapper<T>.Create(const Value: T);
begin
FEnumValue := Value;
end;
class operator EnumWrapper<T>.Implicit(const Value: T): EnumWrapper<T>;
begin
Result := EnumWrapper<T>.Create(Value);
end;
class operator EnumWrapper<T>.Implicit(const Value: Integer): EnumWrapper<T>;
begin
Result := EnumWrapper<T>.Create(Enum<T>.Parse(Value));
end;
class operator EnumWrapper<T>.Implicit(const Value: string): EnumWrapper<T>;
begin
Result := EnumWrapper<T>.Create(Enum<T>.Parse(Value));
end;
class operator EnumWrapper<T>.Implicit(const Value: EnumWrapper<T>): Integer;
begin
Result := Enum<T>.ToInteger(Value.FEnumValue);
end;
class operator EnumWrapper<T>.Implicit(const Value: EnumWrapper<T>): string;
begin
Result := Enum<T>.ValueName(Value.FEnumValue);
end;
class operator EnumWrapper<T>.Implicit(const Value: EnumWrapper<T>): T;
begin
Result := Value.FEnumValue;
end;
function EnumWrapper<T>.ToInteger: Integer;
begin
Result := Self;
end;
function EnumWrapper<T>.ToString: string;
begin
Result := Self;
end;
{$ENDREGION}
{$REGION 'TEnumIterator'}
function TEnumIterator<T>.GetEnumerator: TEnumEnumerator<T>;
begin
Result := TEnumEnumerator<T>.Create(0);
end;
{$ENDREGION}
{$REGION 'TEnumEnumerator<T>'}
constructor TEnumEnumerator<T>.Create(const Dummy: Integer);
begin
FCurrentIndex := -1;
end;
function TEnumEnumerator<T>.GetCurrent: T;
begin
Result := Enum<T>.Parse(FCurrentIndex);
end;
function TEnumEnumerator<T>.MoveNext: Boolean;
begin
if FCurrentIndex < Enum<T>.Count - 1 then
begin
System.Inc(FCurrentIndex);
Result := True;
end
else
Result := False;
end;
{$ENDREGION}
end.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment