Created
December 26, 2017 22:06
-
-
Save ortuagustin/f7f53dfa4e149047afb2340c6508646d to your computer and use it in GitHub Desktop.
Enum.Wrapper.pas
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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