Recent

Author Topic: convert enum to string and back  (Read 20453 times)

TBug

  • Newbie
  • Posts: 4
convert enum to string and back
« on: October 28, 2015, 09:09:41 pm »
Hello to all,

1. How do I get the amount of posible enum-values from the type 'TComponentStyle'?

2. How can I convert an enum-value from TComponentStyle to a string, or get the enum-value from the string 'csInheritable'?



crosspost -  german lazarusforum
http://www.lazarusforum.de/viewtopic.php?f=10&t=9137


.

Cyrax

  • Hero Member
  • *****
  • Posts: 836
Re: convert enum to string and back
« Reply #1 on: October 28, 2015, 09:59:03 pm »
1. High(TComponentStyle) - Low(TComponentStyle).
2. Convert to string : WriteStr(AStringVariable, AComponentStyleVariable)
    Convert from string : ReadStr(AStringVariable, AComponentStyleVariable)

TBug

  • Newbie
  • Posts: 4
Re: convert enum to string and back
« Reply #2 on: October 28, 2015, 11:07:03 pm »
Thank you,

but the first part of your answer doesn't work.

1. High(TComponentStyle) - Low(TComponentStyle).
lInt := (High(TComponentStyle) - Low(TComponentStyle));
 IDE-Output:
Quote
udatamoduleglobalfunc.pas(778,35) Error: Operation "-" not supported for types "<enumeration type>" and "<enumeration type>"


I need to do something like this:

Code: Pascal  [Select][+][-]
  1. function HGTAnchorKindToString(AValue: TAnchorKind): String;
  2. begin
  3.   Result := GetEnumName(TypeInfo(TAnchorKind),Ord(AValue));
  4. end;
  5.  
  6. function HGStringToTAnchorKind(AValue: String): TAnchorKind;
  7. begin
  8.   Result := TAnchorKind(GetEnumValue(TypeInfo(TAnchorKind),AValue));
  9. end;
  10.  

Code: Pascal  [Select][+][-]
  1. procedure TDM_GlobalFunc.ListTAnchorKindAssignTo(ADest: TStrings);
  2. var
  3.   lEnumValue: TAnchorKind;
  4. begin
  5.   if (FListTAnchorKind.Count = 0) then
  6.    begin
  7.     for lEnumValue := Low(TAnchorKind) to High(TAnchorKind) do
  8.      begin
  9.       FListTAnchorKind.Add(HGTAnchorKindToString(lEnumValue));
  10.      end;
  11.    end;
  12.   ADest.Assign(FListTAnchorKind);
  13. end;
  14.  

Code: Pascal  [Select][+][-]
  1. procedure TF_HGFormPanel.UpdateComponentValues;
  2. var
  3.   lEnumValueAnchorKind: TAnchorKind;
  4. begin
  5.     for lEnumValueAnchorKind := Low(TAnchorKind) to High(TAnchorKind) do
  6.      begin
  7.       lIndex := CLB_Anchors.Items.IndexOf(HGTAnchorKindToString(lEnumValueAnchorKind));
  8.       if (lIndex >= 0) then
  9.        begin
  10.         CLB_Anchors.Checked[lIndex] := (lEnumValueAnchorKind in MyComponent.Anchors);
  11.        end;
  12.      end;
  13. end;
  14.  


This works with e.g. TControlCellAlign but not with TComponentStyle or any kind of type declared like:
Code: Pascal  [Select][+][-]
  1. TComponentStyle = set of (csInheritable, csCheckPropAvail, csSubComponent, csTransient);
  2.  

Bart

  • Hero Member
  • *****
  • Posts: 5290
    • Bart en Mariska's Webstek
Re: convert enum to string and back
« Reply #3 on: October 28, 2015, 11:10:47 pm »

1. High(TComponentStyle) - Low(TComponentStyle).
lInt := (High(TComponentStyle) - Low(TComponentStyle));
 IDE-Output:
Quote
udatamoduleglobalfunc.pas(778,35) Error: Operation "-" not supported for types "<enumeration type>" and "<enumeration type>"

Try (untested):
Code: [Select]
  lInt := Ord((High(TComponentStyle))- Ord(Low(TComponentStyle)));

Bart

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: convert enum to string and back
« Reply #4 on: October 28, 2015, 11:58:22 pm »
Code: Pascal  [Select][+][-]
  1. function GetSetValues(const aSet:PTypeInfo):string;
  2. var
  3.   vData1 : PTypeData;
  4.   vData2 : PTypeData;
  5.   vCntr  : Integer;
  6. begin
  7.   Result := '';
  8.   if aSet^.Kind = tkSet then begin
  9.     vData1 := GetTypeData(aSet);
  10.     vData2 := GetTypeData(vData1^.CompType);
  11.     for vCntr := vData2^.MinValue to vData2^.MaxValue do
  12.       Result := Result+ GetEnumName(vData1^.CompType,vCntr)+LineEnding;
  13.   end;
  14. end;
  15.  
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

TBug

  • Newbie
  • Posts: 4
Re: convert enum to string and back
« Reply #5 on: October 29, 2015, 01:02:00 pm »
Thanks a lot.

The sample-code posted by taazz works fine for me.

But no I have another problem.

I can't find a way to pass 'csInherited' as a parameter of a function, or as "Result".
Code: Pascal  [Select][+][-]
  1. function HGTComponentStyleValueToString(AValue: T???): String;
  2. begin
  3.   WriteStr(Result , AValue)
  4. end;
  5.  



Code: Pascal  [Select][+][-]
  1. function HGStringToTComponentStyleValue(AString: String): T???;
  2. begin
  3.   ReadStr(AString , Result)
  4. end;
  5.  



WriteStr uses a type named "Arguments" for the 2. parameter, but I couldn't find a unit where it is declared. So I cannot use this type.

There are several types I tryed e.g. "TOrdinal" and "TSetElement", but don't know in which units they are declared.


.
« Last Edit: October 29, 2015, 01:52:00 pm by TBug »

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: convert enum to string and back
« Reply #6 on: October 29, 2015, 01:32:11 pm »
Thanks a lot.

The sample-code posted by taazz works fine for me.

But no I have another problem.

I can't find a way to pass 'csInherited' as a parameter of a function, or as "Result".
Use the same trick as is used on the GetEnumName function aka declare it as a integer and use a cast (or ord) to pass the value.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

TBug

  • Newbie
  • Posts: 4
Re: convert enum to string and back
« Reply #7 on: October 29, 2015, 01:50:23 pm »
Thanks for this highspeed-answer! 8)

Use the same trick as is used on the GetEnumName function aka declare it as a integer and use a cast (or ord) to pass the value.

Thats only the 2. choice to do it, because other functions of the same style are declared with the right types and doesn't need any casts.

Therefor I actually don't want to use an integer as parameter-type.  :'(

Code: Pascal  [Select][+][-]
  1. function HGStringToTAlign(AValue: String): TAlign;
  2. function HGStringToTAlignment(AValue: String): TAlignment;
  3. function HGStringToTAnchorKind(AValue: String): TAnchorKind;
  4. function HGStringToTBiDiMode(AValue: String): TBiDiMode;
  5. function HGStringToTBorderIcon(AValue: String): TBorderIcon;
  6. function HGStringToTBorderStyle(AValue: String): TBorderStyle;
  7. function HGStringToTCheckBoxState(AValue: String): TCheckBoxState;
  8. function HGStringToTComboBoxStyle(AValue: String): TComboBoxStyle;
  9. function HGStringToTComponentStyle(AValue: String): TComponentStyle;
  10. function HGStringToTControlCellAlign(AValue: String): TControlCellAlign;
  11. function HGStringToTControlStyleType(AValue: String): TControlStyleType;
  12. ...
  13.  
« Last Edit: October 29, 2015, 01:54:44 pm by TBug »

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: convert enum to string and back
« Reply #8 on: October 29, 2015, 02:10:26 pm »
Thanks for this highspeed-answer! 8)

Use the same trick as is used on the GetEnumName function aka declare it as a integer and use a cast (or ord) to pass the value.

Thats only the 2. choice to do it, because other functions of the same style are declared with the right types and doesn't need any casts.

Therefor I actually don't want to use an integer as parameter-type.  :'(

Code: Pascal  [Select][+][-]
  1. function HGStringToTAlign(AValue: String): TAlign;
  2. function HGStringToTAlignment(AValue: String): TAlignment;
  3. function HGStringToTAnchorKind(AValue: String): TAnchorKind;
  4. function HGStringToTBiDiMode(AValue: String): TBiDiMode;
  5. function HGStringToTBorderIcon(AValue: String): TBorderIcon;
  6. function HGStringToTBorderStyle(AValue: String): TBorderStyle;
  7. function HGStringToTCheckBoxState(AValue: String): TCheckBoxState;
  8. function HGStringToTComboBoxStyle(AValue: String): TComboBoxStyle;
  9. function HGStringToTComponentStyle(AValue: String): TComponentStyle;
  10. function HGStringToTControlCellAlign(AValue: String): TControlCellAlign;
  11. function HGStringToTControlStyleType(AValue: String): TControlStyleType;
  12. ...
  13.  
Tcomponentstyle is a set not an enum type it does not have a base enum type declared your only choice if you do not want to cast is to use the set type. eg
Code: Pascal  [Select][+][-]
  1. function ComponentStyleTostring(const aStyle:TComponentStyle):string;
  2. function StringToComponentStyle(const aSTyle:String):TComponentStyle;
  3.  
The details are already coded for you, take a look on the SetToString and StringToSet Functions in typInfo unit.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

wp

  • Hero Member
  • *****
  • Posts: 11916
Re: convert enum to string and back
« Reply #9 on: October 29, 2015, 02:30:34 pm »
Here's another problem related: In one of the testing routines of fpspreadsheet I am converting another set to a string. Here is the code which works with fpc 2.6.4:
Code: Pascal  [Select][+][-]
  1. type
  2.   TsFontStyle = (fssBold, fssItalic, fssStrikeOut, fssUnderline);
  3.   TsFontStyles = set of TsFontStyle;
  4. var
  5.   fs: TsFontStyles;
  6.   s: String;
  7. //...
  8.   s := GetEnumName(TypeInfo(TsFontStyles), integer(fs));

When I run this in fpc 3.1.1 the compiler complains in line 8 about the "integer": 'Illegal type conversion: "TsFontStyles" to "LongInt"'. I have to replace it by a cast to "byte" to make it work. Why? And is there a working solution for both versions without an IFDEF?
« Last Edit: October 29, 2015, 02:34:14 pm by wp »

garlar27

  • Hero Member
  • *****
  • Posts: 652
Re: convert enum to string and back
« Reply #10 on: October 29, 2015, 03:22:34 pm »
Try (untested):
Code: [Select]
  lInt := Ord((High(TComponentStyle))- Ord(Low(TComponentStyle)));

I know is untested but Please be aware of this: These class of enum types are Zero based.
for:
Code: Pascal  [Select][+][-]
  1. type
  2.   TComponentStyle = set of (csInheritable, csCheckPropAvail, csSubComponent,
  3.     csTransient);
  4.  
we have the following values:
   Ord(Low(TComponentStyle)) = 0
   Ord(High(TComponentStyle)) = 3

High(TComponentStyle) + 1 will return the the TComponentStyle's count.

EDIT: This is not due to the zero based, is a mathematic's thing:
If you have let's say 7 elements and are 1 based order (from 1 to 7). Doing 7-1 to get the count won't work because you are excluding the first operator.
Thus if you wish to obtain a count from a substraction you need to add 1 to the substraction and it would be something like:
Code: [Select]
  lInt := Ord((High(TAnyEnumeratedType))- Ord(Low(TAnyEnumeratedType))) + 1;
« Last Edit: October 29, 2015, 03:39:40 pm by garlar27 »

Cyrax

  • Hero Member
  • *****
  • Posts: 836
Re: convert enum to string and back
« Reply #11 on: October 29, 2015, 03:59:21 pm »
Here's another problem related: In one of the testing routines of fpspreadsheet I am converting another set to a string. Here is the code which works with fpc 2.6.4:
Code: Pascal  [Select][+][-]
  1. type
  2.   TsFontStyle = (fssBold, fssItalic, fssStrikeOut, fssUnderline);
  3.   TsFontStyles = set of TsFontStyle;
  4. var
  5.   fs: TsFontStyles;
  6.   s: String;
  7. //...
  8.   s := GetEnumName(TypeInfo(TsFontStyles), integer(fs));

When I run this in fpc 3.1.1 the compiler complains in line 8 about the "integer": 'Illegal type conversion: "TsFontStyles" to "LongInt"'. I have to replace it by a cast to "byte" to make it work. Why? And is there a working solution for both versions without an IFDEF?

Hmm, which revision of FPC 3.1.1 (SVN trunk) do you use because I don't see such error? I use r32185 and it is compiled with these options :
Quote
clean
all
install
OPT=-gw2 -godwarfsets -godwarfmethodclassprefix -gl -O- -Xs- -Si- -vbq -XX -CX -dTEST_WIN32_SEH
COMPILER_OPTIONS=-gw2 -godwarfsets -godwarfmethodclassprefix -gl -O- -Xs- -Si- -vbq -XX -CX -dTEST_WIN32_SEH
INSTALL_PREFIX=F:\free_pascal_and_lazarus\fpc\i386\trunk\binary\fpc_trunk
UPXPROG=echo
IDE=1
REVSTR=32185
ALLOW_WARNINGS=1
NOWPOCYCLE=1


EDIT : I compiled your test case with these options : -MObjFPC -Scghi -gw2 -godwarfsets -gl -l -vewnhibq -FiE:\temp\lib\i386-win32-win32-30101 -Fu. -FUE:\temp\lib\i386-win32-win32-30101\ -FEE:\temp\bin\i386-win32-win32-30101\ -godwarfmethodclassprefix
« Last Edit: October 29, 2015, 04:01:31 pm by Cyrax »

wp

  • Hero Member
  • *****
  • Posts: 11916
Re: convert enum to string and back
« Reply #12 on: October 30, 2015, 01:01:02 pm »
Strange. I updated fpc and Laz via fpcup (fpc r32196, Laz r50199) and don't see the error in a simple test program (attached). But I still see it when I compile the test program of fpspreadsheet (spreadtestgui in folder "tests").

On the other hand, as I see now this is not the main issue. The main issue is that GetEnumName for a set type always seems to return the same string ("CharSet") whatever elements are included in the set - see attached demo. Therefore, GetEnumName is not a good candidate to compare sets as I am doing in the fpspreadsheet test (will be fixed soon).

[EDIT]
Regarding the issue with casting the set to integer or byte I found out that this is not caused by the compiler version, but by the compiler mode: The demo uses mode objpas, but if I switch it to mode Delphi (that what the spreadtestgui has as well) then the cast to byte must be used. Please note a few posts below that Delphi XE2 requires the cast to byte when calling GetEnumName. Therefore, everything fine!
« Last Edit: October 30, 2015, 06:10:34 pm by wp »

Leledumbo

  • Hero Member
  • *****
  • Posts: 8757
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: convert enum to string and back
« Reply #13 on: October 30, 2015, 04:54:30 pm »
On the other hand, as I see now this is not the main issue. The main issue is that GetEnumName for a set type always seems to return the same string ("CharSet") whatever elements are included in the set - see attached demo. Therefore, GetEnumName is not a good candidate to compare sets as I am doing in the fpspreadsheet test (will be fixed soon).
I'm amazed that GetEnumName works for sets. AFAIK it's only for enumerated types and if you want to print a set as string, you have to loop over it (using for-in loop) and call GetEnumName on each element instead.

wp

  • Hero Member
  • *****
  • Posts: 11916
Re: convert enum to string and back
« Reply #14 on: October 30, 2015, 05:48:05 pm »
I think the GetEnumName of a set either should execute the code that you propose, or it should not compile at all. A candidate for a possible bug report?

I checked with Delphi XE2: The demo does compile, but only after changing the type cast of the set to "byte" (instead of "integer"). But at runtime an exception is generated at the GetEnumNume line.

Based on taazz's code above I modified my demo to display correct strings for the font styles, and to do also the conversion from strings back to a set:
Code: Pascal  [Select][+][-]
  1. function GetSetName(const aSet:PTypeInfo; Value: Integer):string;
  2. var
  3.   vData1 : PTypeData;
  4.   vData2 : PTypeData;
  5.   vCntr  : Integer;
  6.   v: Integer;
  7. begin
  8.   Result := '';
  9.   if aSet^.Kind = tkSet then begin
  10.     vData1 := GetTypeData(aSet);
  11.     vData2 := GetTypeData(vData1^.CompType);
  12.     for vCntr := vData2^.MinValue to vData2^.MaxValue do
  13.       if (Value shr vCntr) and 1 <> 0 then
  14.         Result := Result+ GetEnumName(vData1^.CompType,vCntr)+',';
  15.     if Result <> '' then Delete(Result, Length(Result), 1);
  16.   end;
  17. end;
  18.  
  19. function GetSetValue(const aSet:PTypeInfo; Name: String): Integer;
  20. var
  21.   vData1 : PTypeData;
  22.   vData2 : PTypeData;
  23.   vCntr  : Integer;
  24.   p      : Integer;
  25. begin
  26.   Result := 0;
  27.   if aSet^.Kind = tkSet then begin
  28.     vData1 := GetTypeData(aSet);
  29.     vData2 := GetTypeData(vData1^.CompType);
  30.     for vCntr := vData2^.MinValue to vData2^.MaxValue do begin
  31.       p := pos(GetEnumName(vData1^.CompType, vCntr), Name);
  32.       if p = 0 then
  33.         Continue;
  34.       if (p = 1) or (Name[p-1] = ',') then
  35.         Result := Result or (1 shl vCntr);
  36.     end;
  37.   end;
  38. end;
  39.  
  40. { TForm1 }
  41.  
  42. procedure TForm1.CbBoldClick(Sender: TObject);
  43. var
  44.   fs: TFontStyles;
  45.   s: String;
  46. begin
  47.   fs := [];
  48.   if CbBold.Checked then begin
  49.     Memo1.Lines.Add('Bold: yes');
  50.     Include(fs, fsBold);
  51.   end else
  52.     Memo1.Lines.Add('Bold: -');
  53.  
  54.   if CbItalic.Checked then begin
  55.     Memo1.Lines.Add('Italic: yes');
  56.     Include(fs, fsItalic);
  57.   end else
  58.     Memo1.Lines.Add('Italic: -');
  59.  
  60.   if CbUnderline.Checked then begin
  61.     Memo1.Lines.Add('Underline: yes');
  62.     Include(fs, fsUnderline);
  63.   end else
  64.     Memo1.Lines.Add('Underline: -');
  65.  
  66.   if CbStrikeout.Checked then begin
  67.     Memo1.Lines.Add('Strikeout: yes');
  68.     Include(fs, fsStrikeOut);
  69.   end else
  70.     Memo1.Lines.Add('Strikeout: -');
  71.  
  72.   s := GetSetName(TypeInfo(TFontStyles), integer(fs));
  73.   Memo1.Lines.Add('GetSetName:  ' + s);
  74.   Memo1.Lines.Add('GetSetValue: ' + IntToStr(GetSetValue(TypeInfo(TFontStyles), s)));
  75.   Memo1.Lines.Add('-----------------------------------------------------');
  76.   Memo1.Lines.Add('');
  77. end;
  78.  

@taazz: Since this is based on your code: Would you allow me to add this to bug tracker?
« Last Edit: October 30, 2015, 07:18:43 pm by wp »

 

TinyPortal © 2005-2018