Converting an string to a enum type using TValue?
Asked Answered
C

4

6

I want to convert an string to an enum type using TValue, I googled but I didn't find how to do that.

type 
  TEnumTest = (etFirst, etSecond);

var 
  D: TEnumTest;
begin
  D := StrToENumTest('etFirst');
end;

function StrToEnumTest(pStr:String):TEnumTest;
var 
  V: TValue;
begin
  V := TValue.From<String>(pstr);
  Result := V.AsType<TEnumTest>;
end;

It doesn't work. That's must be something stupid I'm not seeing - but I didn't found it. What I made wrong?

I know how to use GetEnumValue.

EDIT: @Warren, it goes here as this is easier to post code:

  TEnumUtils = class
    class function GetAs<T>(pValor: String): T;
  end;

class function TEnumUtils.GetAs<T>(pValor: String): T;
var
  Tipo: PTypeInfo;
  Temp: Integer;
  PTemp: Pointer;

begin
   Tipo := TypeInfo(T);
   Temp := GetEnumValue(Tipo, pValor);
   PTemp := @Temp;
   Result := T(PTemp^);
end;

Usage:

type 
  TEnumTest = (etFirst, etSecond);

var 
  D: TEnumTest;
begin
  D := TEnumUtils.GetAs<TEnumTest>('etFirst');
end;  
Creole answered 18/3, 2010 at 18:46 Comment(0)
F
3

What you're not seeing is the way TValue was designed. It's intended specifically as a way to contain values, not a way to convert them. If you want to convert between srtings and enums, as you said, you already know how. Use the functions provided for that purpose in TypeInfo.

Fosque answered 18/3, 2010 at 19:34 Comment(1)
Yeah, I know. But I could swear that I have seen someone doing this using TValue. Thank you, anyway.Creole
S
7

is this what you was looking for?

Using Generics & RTTI to get enum string name or enum value

Enum conversion with Generics / RTTI Unit System.RTTI is cross platform and contains a great class for converting enum to string and back: TRttiEnumerationType

The TRttiEnumerationType class has two class functions (methods you can call without creating an instance of the class) that clean up the code required from using the TypInfo methods. The easy reading version of these method declarations is:

class function GetName(AValue: T):string; class function GetValue(AName: string): T; Note these methods use Generics (thats the T bit). Generics are very cool as they allow you to write functionality once and then reuse it with different types at different times in code.

In this instance TRttiEnumerationType’s generic methods are for use with Enums only and not other class types as the functionality defined is specific for Enum’s.

To convert the TCompass enum now after adding RTTI to the uses would look like this.

S := TRttiEnumerationType.GetName(D); ShowMessage(S); To convert back from a string is also simpler.

D := TRttiEnumerationType.GetValue(S); How much easier is that to read! and as we have only had to declare the type once, we have less chance of silly copy paste errors in code.

Subside answered 19/5, 2016 at 10:2 Comment(0)
F
3

What you're not seeing is the way TValue was designed. It's intended specifically as a way to contain values, not a way to convert them. If you want to convert between srtings and enums, as you said, you already know how. Use the functions provided for that purpose in TypeInfo.

Fosque answered 18/3, 2010 at 19:34 Comment(1)
Yeah, I know. But I could swear that I have seen someone doing this using TValue. Thank you, anyway.Creole
B
3

You can use the same approach as it's write in the top of this page with GetEnumValue. You can easy declare a codeblock like this:

unit Unit3;

interface

uses
   Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
   System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, 
   typinfo, Vcl.StdCtrls;

   Type TMyEnumerator=(enm_Case0, enm_Case1, enm_Case2);

   Type TEnumConverter = class
   public
        class function EnumToInt<T>(const enValue: T): Integer;
        class function EnumToString<T>(enValue: T): string;
        class procedure StringToEnum<T>(strValue:String; var enValue:T);
   end;

implementation

class function TEnumConverter.EnumToInt<T>(const enValue: T): Integer;
begin
   Result := 0;
   Move(enValue, Result, sizeOf(enValue));
end;

class function TEnumConverter.EnumToString<T>(enValue: T): string;
begin
  Result := GetEnumName(TypeInfo(T), EnumToInt(enValue));
end;

 class procedure TEnumConverter.StringToEnum<T>(strValue: String; var enValue:T);
   var Tipo : PTypeInfo;
       Temp:Integer;
       PTemp : pointer;
begin
    Tipo := TypeInfo(T);
    Temp := GetEnumValue(Tipo, strValue);
    PTemp := @Temp;
    enValue := T(PTemp^);
end;

procedure TForm3.Button1Click(Sender: TObject);
   var s: String;
       v : TMyEnumerator;
begin
       { ************** Example *************** }

       showmessage(TEnumConverter.EnumToString(enm_Case1)); 

        s := 'enm_Case2';
        TEnumConverter.StringToEnum(s, v);

   case v of
     enm_Case0: showmessage('ok -> enm_Case0');
     enm_Case1: showmessage('ok -> enm_Case1');
     enm_Case2: showmessage('ok -> enm_Case2');
   end;

end;
Betweenwhiles answered 3/2, 2017 at 10:40 Comment(0)
E
1

So you know how to do this:

function StrToEnumTest(aStr:String):TEnumTest;
begin
  result := TEnumTest(GetEnumValue(TypeInfo(TEnumTest),aStr));
end;

But you don't want to do it that way? Why? I wish we could do this:

inline function StrToEnumTest(aStr:String):<T>;
begin
  result := <T>(GetEnumValue(TypeInfo(<T>),aStr));
end;
Eshelman answered 18/3, 2010 at 20:2 Comment(2)
The angle brackets around the Ts only belong in the definition, not in the function body.Fosque
@Warren P: Be aware that Typeinfo will be lost when the enumeration is given different ordinal values. With the next enumeration example it's not possible to use GetEnumValue and will result in 'E2134 Type 'TTestType' has no type info' type TTestType = (ttTest1 = 2, ttTest2 = 4, ttTestUnknown = 6); It works only when the enumeration is not given any ordinal values.Edmondo

© 2022 - 2024 — McMap. All rights reserved.