Mike Torrettinni 198 Posted August 23, 2020 4 hours ago, Lars Fosdal said: Translate: array[TAllowState] of xlt = ( { asAllow } (no:'Tillatt'; se:'Tillåten'; en:'Allow'), { asWarn } (no:'Vis advarsel'; se:'Visa varning'; en:'Show warning'), { asDeny } (no:'Sperret'; se:'Sperrad'; en:'Denied') ); Your example gave me an idea, that I completely missed. I don't need 2 consts, 1 should be enough: const cProjectTypeNames: array[TProjectType] of TTypeAsName = ( {ptMain} (DisplayName: 'Main Project'; XMLName: 'xml_main'), {ptExternal} (DisplayName: 'External Project'; XMLName: 'xml_external') ); This is then easier to extend, adding additional name, and if there are many values, which you would split into multiple lines in code, the names are aligned and you can't make a mistake. The only drawback is, if you have 10 values, this becomes 10+2 lines, while before it was 4 lines for 2 consts, total. 1 Share this post Link to post
Fr0sT.Brutal 900 Posted August 24, 2020 // *************************************************************************** // Static class to convert enum value <=> enum name // *************************************************************************** TEnum<T> = class strict private class var FPTypInf: PTypeInfo; FMin, FMax: Integer; {$IFDEF CAPS_CLASSCONSTROK} class constructor Create; {$ELSE} class procedure Init; {$ENDIF} class procedure CheckRange(Item: Integer); inline; public // T => Int class function Int(Item: T): Integer; overload; inline; // Str => Int class function Int(const Name: string): Integer; overload; inline; // Int => T class function Val(Item: Integer; CheckRange: Boolean = True): T; overload; inline; // Str => T class function Val(const Name: string): T; overload; inline; // T => Str class function Str(Item: T): string; overload; inline; // Int => Str class function Str(Item: Integer): string; overload; // Search for Item in Values array and return its index as T class function Find(const Item: string; const Values: array of string): T; overload; class function Find(const Item: Char; const Values: array of Char): T; overload; class property Min: Integer read FMin; class property Max: Integer read FMax; end; {$REGION 'TEnum<T>'} {$IFDEF TYPES_GENERICS} {$IFDEF CAPS_CLASSCONSTROK} // Perform some checks and save type properties. // Executed on unit init if the class is used, raises exception if type is invalid. class constructor TEnum<T>.Create; {$ELSE} class procedure TEnum<T>.Init; {$ENDIF} begin FPTypInf := PTypeInfo(TypeInfo(T)); // type info check if FPTypInf = nil then raise Err(S_E_NoTypeInfo); // run-time type check if FPTypInf.Kind <> tkEnumeration then raise Err(S_EEnum_NotAnEnum, [FPTypInf.Name]); // get range FMin := GetTypeData(FPTypInf).MinValue; FMax := GetTypeData(FPTypInf).MaxValue; end; // Check if Item in enum range class procedure TEnum<T>.CheckRange(Item: Integer); begin if (Item < FMin) or (Item > FMax) then raise Err(S_EEnum_NotInRange, [Item, FMin, FMax, FPTypInf.Name]); end; // Integer => Enum member, the same as Integer(T) // CheckRange: controls whether checking if Item belongs Low(T)..High(T) will // be performed. Useful to return T(-1) as invalid value. class function TEnum<T>.Val(Item: Integer; CheckRange: Boolean): T; var p: Pointer; begin {$IFNDEF CAPS_CLASSCONSTROK} if FPTypInf = nil then Init; {$ENDIF} if CheckRange then Self.CheckRange(Item); p := @Result; case SizeOf(T) of 1: PUInt8(p)^ := UInt8(Item); 2: PUInt16(p)^ := UInt16(Item); else raise Err(S_EEnum_WrongSize, [SizeOf(T)]); end; end; // Enum member => Integer, the same as T(Int) class function TEnum<T>.Int(Item: T): Integer; var p: Pointer; begin p := @Item; case SizeOf(T) of 1: Result := PUInt8(p)^ ; 2: Result := PUInt16(p)^; else raise Err(S_EEnum_WrongSize, [SizeOf(T)]); end; end; // Integer => String class function TEnum<T>.Str(Item: Integer): string; begin {$IFNDEF CAPS_CLASSCONSTROK} if FPTypInf = nil then Init; {$ENDIF} CheckRange(Item); Result := GetEnumName(FPTypInf, Item); end; // T => String class function TEnum<T>.Str(Item: T): string; begin Result := Str(Int(Item)); end; // String => Integer class function TEnum<T>.Int(const Name: string): Integer; begin {$IFNDEF CAPS_CLASSCONSTROK} if FPTypInf = nil then Init; {$ENDIF} Result := GetEnumValue(FPTypInf, Name); if Result = -1 then raise Err(S_EEnum_NoValueForName, [Name, FPTypInf.Name]); end; // String => T class function TEnum<T>.Val(const Name: string): T; begin Result := Val(Int(Name)); end; // Find string representation in array of strings and return T // Similar to Val(Str) but Text and Values could be arbitrary. // Returns T(-1) if Text not found class function TEnum<T>.Find(const Item: string; const Values: array of string): T; begin Result := Val(FindStr(Item, Values), False); // Turn off range check to return -1 end; // The same but for Chars class function TEnum<T>.Find(const Item: Char; const Values: array of Char): T; begin Result := Val(FindChar(Item, Values), False); // Turn off range check to return -1 end; {$ENDIF} {$ENDREGION} // usage TEnum<TSomeEnum>.Str(seFirst) => 'seFirst' TEnum<TSomeEnum>.Val('seFirst') => seFirst TEnum<TSomeEnum>.Find('First', ['Fisrt', 'Second']) => seFirst Just note that only "naturally numbered" enums have type info (if any of elements has explicit index assignment, no type info is generated) 1 Share this post Link to post