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;

  5. …and using the above, 30 one-liner test methods,  tested for various languages, set from the DUnitX TestCase attributes.  Found one error 🙂

Leave a Reply