Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic functions for converting an enumeration to string and back

I'm trying to write functions that will convert an enumeration to string and back again.

ie:

TConversions = class
    strict private
    public
      class function StringToEnumeration<T:class>(x:String):T;
      class function EnumerationToString<T:class>(x:T):String;
  end;

in the implementation section I have

uses
System.TypInfo
;

class function TConversions.StringToEnumeration<T>(x:String):T;
begin
    Result :=  T(GetEnumValue(TypeInfo(T), x));
end;

class function TConversions.EnumerationToString<T>(x:T):String;
begin
    Result := GetEnumName(TypeInfo(T), integer(x));
end;

The problem is, an enum is not of type T:class in pascal. I can't use T:record either.

Is this possible to do in pascal?

like image 297
sav Avatar asked Jul 24 '15 03:07

sav


People also ask

How to convert enum name to string?

The following code loops through enum and adds string values to a DropDownList. This code converts an enum to string: string name= Enum. GetName(typeof(ArrayListBinding.

How to convert an enum type variable to a string?

Use const char* Array to Convert an Enum to a String in C++ enum is a built-in type, which can be used to declare small named integers usually formed as an array. This mechanism provides a less error-prone and more readable way of representing a set of integer values.

How to convert enum into string in c++?

The stringify() macro method is used to convert an enum into a string. Variable dereferencing and macro replacements are not necessary with this method. The important thing is that, only the text included in parenthesis may be converted using the stringify() method.

How do you convert a string value to a specific enum type in C#?

To convert string to enum use static method Enum. Parse. Parameters of this method are enum type, the string value and optionally indicator to ignore case.


3 Answers

You need to fiddle with things a bit. There is no generic for enums so we get around it by casting to and from the enum using Byte, Word and Cardinal.

program Project6;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils, System.TypInfo;

type
  TConversions<T> = record
    class function StringToEnumeration(x: String): T; static;
    class function EnumerationToString(x: T): String; static;
  end;

class function TConversions<T>.StringToEnumeration(x: String): T;
begin
  case Sizeof(T) of
    1: PByte(@Result)^ := GetEnumValue(TypeInfo(T), x);
    2: PWord(@Result)^ := GetEnumValue(TypeInfo(T), x);
    4: PCardinal(@Result)^ := GetEnumValue(TypeInfo(T), x);
  end;
end;

class function TConversions<T>.EnumerationToString(x: T): String;
begin
  case Sizeof(T) of
    1: Result := GetEnumName(TypeInfo(T), PByte(@x)^);
    2: Result := GetEnumName(TypeInfo(T), PWord(@x)^);
    4: Result := GetEnumName(TypeInfo(T), PCardinal(@x)^);
  end;
end;

type
  TMyEnum = (me_One, me_Two, me_Three);
  TMyEnum2 = (m1,m2,m3,m4,m5,m6,m7,m8,m9,m10,m11,m12,m13,m14,m15,m16,m17,m18,m19,m20,
              m21,m22,m23,m24,m25,m26,m27,m28,m29,m30,m31,m32,m33,m34,m35,m36,m37,m38,m39,m40,
              m41,m42,m43,m44,m45,m46,m47,m48,m49,m50,m51,m52,m53,m54,m55,m56,m57,m58,m59,m60,
              ma1,ma2,ma3,ma4,ma5,ma6,ma7,ma8,ma9,ma10,ma11,ma12,ma13,ma14,ma15,ma16,ma17,ma18,ma19,ma20,
              ma21,ma22,ma23,ma24,ma25,ma26,ma27,ma28,ma29,ma30,ma31,ma32,ma33,ma34,ma35,ma36,ma37,ma38,ma39,
              ma40,ma41,ma42,ma43,ma44,ma45,ma46,ma47,ma48,ma49,ma50,ma51,ma52,ma53,ma54,ma55,ma56,ma57,ma58,ma59,ma60,
              mb1,mb2,mb3,mb4,mb5,mb6,mb7,mb8,mb9,mb10,mb11,mb12,mb13,mb14,mb15,mb16,mb17,mb18,mb19,
              mb20,mb21,mb22,mb23,mb24,mb25,mb26,mb27,mb28,mb29,mb30,mb31,mb32,mb33,mb34,mb35,mb36,mb37,mb38,mb39,
              mb40,mb41,mb42,mb43,mb44,mb45,mb46,mb47,mb48,mb49,mb50,mb51,mb52,mb53,mb54,mb55,mb56,mb57,mb58,mb59,mb60,
              mc1,mc2,mc3,mc4,mc5,mc6,mc7,mc8,mc9,mc10,mc11,mc12,mc13,mc14,mc15,mc16,mc17,mc18,mc19,
              mc20,mc21,mc22,mc23,mc24,mc25,mc26,mc27,mc28,mc29,mc30,mc31,mc32,mc33,mc34,mc35,mc36,mc37,mc38,mc39,
              mc40,mc41,mc42,mc43,mc44,mc45,mc46,mc47,mc48,mc49,mc50,mc51,mc52,mc53,mc54,mc55,mc56,mc57,mc58,mc59,mc60,
              md1,md2,md3,md4,md5,md6,md7,md8,md9,md10,md11,md12,md13,md14,md15,md16,md17,md18,md19,
              md20,md21,md22,md23,md24,md25,md26,md27,md28,md29,md30,md31,md32,md33,md34,md35,md36,md37,md38,md39,
              md40,md41,md42,md43,md44,md45,md46,md47,md48,md49,md50,md51,md52,md53,md54,md55,md56,md57,md58,md59,md60,
              me1,me2,me3,me4,me5,me6,me7,me8,me9,me10,me11,me12,me13,me14,me15,me16,me17,me18,me19,
              me20,me21,me22,me23,me24,me25,me26,me27,me28,me29,me30,me31,me32,me33,me34,me35,me36,me37,me38,me39,
              me40,me41,me42,me43,me44,me45,me46,me47,me48,me49,me50,me51,me52,me53,me54,me55,me56,me57,me58,me59,me60,
              mf1,mf2,mf3,mf4,mf5,mf6,mf7,mf8,mf9,mf10,mf11,mf12,mf13,mf14,mf15,mf16,mf17,mf18,mf19,
              mf20,mf21,mf22,mf23,mf24,mf25,mf26,mf27,mf28,mf29,mf30,mf31,mf32,mf33,mf34,mf35,mf36,mf37,mf38,mf39,
              mf40,mf41,mf42,mf43,mf44,mf45,mf46,mf47,mf48,mf49,mf50,mf51,mf52,mf53,mf54,mf55,mf56,mf57,mf58,mf59,mf60);

var
  enum: TMyEnum;
  enum2: TMyEnum2;
begin
  enum := me_Two;
  WriteLn(TConversions<TMyEnum>.EnumerationToString(enum));
  enum := me_One;
  WriteLn(TConversions<TMyEnum>.EnumerationToString(enum));
  enum := TConversions<TMyEnum>.StringToEnumeration('me_Three');
  WriteLn(TConversions<TMyEnum>.EnumerationToString(enum));
  enum2 := m17;
  WriteLn(TConversions<TMyEnum2>.EnumerationToString(enum2));
  ReadLn;
end.
like image 129
Graymatter Avatar answered Sep 27 '22 22:09

Graymatter


Somehow this crucial information is missing as an answer:

In recent Delphi versions there is no need to write any generic helper to convert enums to string and back, because it is already there in System.Rtti, and in fact it is implemented very similar to the existing answers here.

class function TRttiEnumerationType.GetName<T{: enum}>(AValue: T): string;
class function TRttiEnumerationType.GetValue<T{: enum}>(const AName: string): T;

Usage is very short and simple:

S:= TRttiEnumerationType.GetName(myEnum);
like image 21
maf-soft Avatar answered Sep 27 '22 23:09

maf-soft


There seems to be no T:enum generic type constraint so I think the best you can do is check the type at runtime, something like this:

Edit: Based on David's comment, I've added the T: record constraint which can be used to constrain to value types (and rule out class types).

type
  TConversions = class
  public
    class function StringToEnumeration<T: record>(const S: string): T;
    class function EnumerationToString<T: record>(Value: T): string;
  end;

class function TConversions.EnumerationToString<T>(Value: T): string;
var
  P: PTypeInfo;
begin
  P := PTypeInfo(TypeInfo(T));
  case P^.Kind of
    tkEnumeration:
      case GetTypeData(P)^.OrdType of
        otSByte, otUByte:
          Result := GetEnumName(P, PByte(@Value)^);
        otSWord, otUWord:
          Result := GetEnumName(P, PWord(@Value)^);
        otSLong, otULong:
          Result := GetEnumName(P, PCardinal(@Value)^);
      end;
    else
      raise EArgumentException.CreateFmt('Type %s is not enumeration', [P^.Name]);
  end;
end;

class function TConversions.StringToEnumeration<T>(const S: string): T;
var
  P: PTypeInfo;
begin
  P := PTypeInfo(TypeInfo(T));
  case P^.Kind of
    tkEnumeration:
      case GetTypeData(P)^.OrdType of
        otSByte, otUByte:
          PByte(@Result)^ := GetEnumValue(P, S);
        otSWord, otUWord:
          PWord(@Result)^ := GetEnumValue(P, S);
        otSLong, otULong:
          PCardinal(@Result)^ := GetEnumValue(P, S);
      end;
    else
      raise EArgumentException.CreateFmt('Type %s is not enumeration', [P^.Name]);
  end;
end;
like image 21
Ondrej Kelle Avatar answered Sep 27 '22 21:09

Ondrej Kelle