Generics and enumerations

Generics and enumerations

We need a type qualifier for enumerated types that allow us to do loops and indexing with generic enumerables.

type

TMyType = (a, b, c);

TEnumToStr = reference to function(Value:T):String;

procedure SomeClass.ListTypes(ToString:TEnumToStr);

var

  v: T;

begin

  for v := Low(T) to High(T)  //<– Error

   do Writeln(ToString(T));

[dcc32 Error] : E2032 For loop control variable must have ordinal type

Argh!

Any suggestion to for a clever workaround?  I need to write tests for dozens of EnumToString functions for enumerable types, and would love to avoid having to recreate the entire loop with declarations, sanity checks, logging and all.

10 thoughts on “Generics and enumerations


  1. Something like this?


    function MyTypeToString(AValue: TMyType): string;


    const


      CMyTypeStrings: array[TMyType] of string = (‘a’, ‘b’, ‘c’);


    begin


      Result := CMyTypeStrings[AValue];


    end;


    Maybe you could use the rtti to get the information?


  2. class procedure TUtils.EnumerateEnum(Action: TProc);


    type


      PointerOfT = ^T;


    var


      I: Integer;


      TI: PTypeInfo;


    begin


      TI := TypeInfo(T);


      Assert(TI.Kind = tkEnumeration);


      for I := TI.TypeData.MinValue to TI.TypeData.MaxValue do


        Action(PointerOfT(@I)^);


    end;


    procedure SomeClass.ListTypes(ToString:TEnumToStr);


    begin


      TUtils.EnumerateEnum(procedure Value: T


         begin


           Writeln(ToString(Value))


         end);


    end;


  3. I would write it like this:


    type


      TUtils = class


        class procedure EnumerateEnum(Action: TProc);


        class function Low: Integer; inline;


        class function High: Integer; inline;


        class function Cast(const i: Integer): T; inline;


      end;


    class procedure TUtils.EnumerateEnum(Action: TProc);


    var


      I: Integer;


    begin


      for I := Low to High do


        Action(Cast(I));


    end;


    class function TUtils.High: Integer;


    begin


      Result := GetTypeData(TypeInfo(T)).MaxValue;


    end;


    class function TUtils.Low: Integer;


    begin


      Result := GetTypeData(TypeInfo(T)).MinValue;


    end;


    class function TUtils.Cast(const i: Integer): T;


    begin


      PInteger(@Result)^:= i;


    end;


  4. Ended up with


    function TestConstantEnumToStrings.TestEnumToString(const Method: TEnumToStringFunc; Language: String): Boolean;


    type


      PointerOfT = ^T;


    var


      EnumAsString: String;


      CaseResult: Boolean;


      I: Integer;


      TI: PTypeInfo;


    begin


      Result := True;


      TI := TypeInfo(T);


      Assert.IsTrue(TI.Kind = tkEnumeration, ‘Type passed is not an enumeration’);


      for I := TI.TypeData.MinValue to TI.TypeData.MaxValue


      do begin


        EnumAsString := Method(PointerOfT(@I)^, Language);


        CaseResult := Pos(‘ToString not implemented for language’, EnumAsString) <= 0;


        if not CaseResult


         then TDUnitX.CurrentRunner.Log(TLogLevel.ltError, EnumAsString);


        Result := Result and CaseResult;


      end;


      Assert.IsTrue(Result, ‘Type ‘ +String(TI.Name) + ‘ is missing ToString value(s) for language “‘+Language+'”‘);


    end;