Recent

Author Topic: Generic way to get back enumeration value as string i/o integer  (Read 25453 times)

User137

  • Hero Member
  • *****
  • Posts: 1791
    • Nxpascal home
Re: Generic way to get back enumeration value as string i/o integer
« Reply #15 on: August 29, 2012, 04:05:51 pm »
    itAdjsutment:   Result := 'Adjsutment'
First i thought it's just a typo, but you wrote it in 4 places. Word should be "adjustment"?

ludob

  • Hero Member
  • *****
  • Posts: 1173
Re: Generic way to get back enumeration value as string i/o integer
« Reply #16 on: August 29, 2012, 04:12:55 pm »
A shorter and easier to maintain version is

Code: [Select]
  function TInvestmentDesciption(const IT: TInvestment): shortstring;
  begin
    Result := copy(GetEnumName(TypeInfo(TInvestment), ord(IT)),3,255);
  end;

  function TInvestmentEnum(const IT: shortstring): TInvestment;
  begin
    for result:=low(TInvestment) to high(TInvestment) do
      if copy(GetEnumName(TypeInfo(TInvestment), ord(result)),3,255)=IT then
        exit;
  end;

You change or add a new enumerator value and the functions still work. Also imagine the typing if you have larger sets.

User137

  • Hero Member
  • *****
  • Posts: 1791
    • Nxpascal home
Re: Generic way to get back enumeration value as string i/o integer
« Reply #17 on: August 29, 2012, 04:16:14 pm »
I made a little more generic way:
Code: [Select]
uses ... , typinfo;

type
  TGender = (gnMale, gnFemale);
...
implementation
...
function EnumToString(info: PTypeInfo; value, startLen: integer): string;
begin
  result:=GetEnumName(info, value);
  delete(result, 1, startLen);
end;

function GenderToString(gender: TGender): string;
begin
  // with startLen 2 it will remove 2 first characters, 'gn'
  result:=EnumToString(TypeInfo(TGender), integer(gender), 2);
end;
...
procedure TForm1.FormCreate(Sender: TObject);
begin
  caption:=GenderToString(gnMale);
end;

Knipfty

  • Full Member
  • ***
  • Posts: 232
Re: Generic way to get back enumeration value as string i/o integer
« Reply #18 on: August 29, 2012, 04:47:43 pm »
User123,

Yep.  It is a typo.  It was typed wrong the 1st time and then I copied and pasted.  At least I was consistent...

Thanks for the catching it.   :-[

ludob,

Yes that does look easier to maintain and I particularly like the Enum function.  That was a clever piece of code.  I'll incorporate these changes ASAP!

Thank you both!

Knipfty
64-bit Lazarus 2.2.0 FPC 3.2.2, 64-bit Win 11

KpjComp

  • Hero Member
  • *****
  • Posts: 680
Re: Generic way to get back enumeration value as string i/o integer
« Reply #19 on: August 29, 2012, 05:45:10 pm »
Quote
My issue with the generic was that I like to put a 2 character prefix in front of the string.

If you always put a 2 character prefix, then the following would have worked.

Code: [Select]
function TInvestmentDesciption(const IT: TInvestment): shortstring;
begin
  WriteStr(result,IT);
  delete(result,1,2);
end; 

Knipfty

  • Full Member
  • ***
  • Posts: 232
Re: Generic way to get back enumeration value as string i/o integer
« Reply #20 on: August 29, 2012, 07:11:07 pm »
And I can also do this instead of a loop:
Code: [Select]
function TInvestmentEnum(const IT: shortstring): TInvestment;
begin
  Result := GetEnumValue(TypeInfo(TInvestment), 'it'+IT)
end;
Again accounting for the 2 characters I stripped out.

Or to further generalize it.  I can do
Code: [Select]
function TInvestmentEnum(const IT: shortstring): TInvestment;
var S:shortstring[2];
begin
  S := copy(GetEnumName(TypeInfo(TInvestment), ord(0)),1,2);
  Result := GetEnumValue(TypeInfo(TInvestment), S+IT)
end;


So the two rountines now look like this:
Code: [Select]
function TInvestmentDesciption(const IT: TInvestment): shortstring;
begin
  Result := copy(GetEnumName(TypeInfo(TInvestment), ord(IT)),3,255);
end;

function TInvestmentEnum(const IT: shortstring): TInvestment;
var S:shortstring[2];
begin
  S := copy(GetEnumName(TypeInfo(TInvestment), ord(0)),1,2);
  Result := GetEnumValue(TypeInfo(TInvestment), S+IT)
end;
64-bit Lazarus 2.2.0 FPC 3.2.2, 64-bit Win 11

KpjComp

  • Hero Member
  • *****
  • Posts: 680
Re: Generic way to get back enumeration value as string i/o integer
« Reply #21 on: August 29, 2012, 08:10:21 pm »
The only thing I would say about using enum names is that it of course doesn't work so well if you want to make your application more international.

Although there are ways of doing Internationalization using DLL & Resources.  A simple way might be like ->

Code: [Select]
type
  TLanguage = ( langEN,langFR );
  TInvestment = (itDeposit,itReturn,itRealized,itDistribution,itAdjustment );
  TInvestmentArray = array[TLanguage,TInvestment] of string;
const
  szInvestmentArray:TInvestmentArray =
  (
    ( 'Deposit','Return','Realized','Distribution','Adjustment' ), //English
    ( 'Dépôt','Retour','Réalisé','Distribution','Ajustement' )  //French
  );
var
  CurrentLanguage:TLanguage=LangEn;

function TInvestmentDesciption(const IT: TInvestment):string;
begin
  result := szInvestmentArray[CurrentLanguage,IT];
end;

function TInvestmentEnum(const IT: string): TInvestment;
var
  I:TInvestment;
begin
  for I := low(I) to high(I) do
  begin
    if sametext(IT,szInvestmentArray[CurrentLanguage,I]) then begin
      result := I;
      exit;
    end;
  end;
  raise Exception.CreateFmt('Investment %s not found',[IT]);
end;

ps, For the French bit I used Google translate so no idea if it's correct. :)

Goodman H__

  • Full Member
  • ***
  • Posts: 130
Re: Generic way to get back enumeration value as string i/o integer
« Reply #22 on: August 30, 2012, 03:48:45 am »
With so many replies and workful codes examples.I think I've already gained what I want to know.

But,please allow my greed.I want it one more step:
Code: [Select]
TLanguage = ( langEN,langFR );
 TInvestment = (itDeposit,itReturn,itRealized,itDistribution,itAdjustment );
 
 //template or not
Function EnumToString<T>(enum:T):string;
begin
...
end;
...
begin
writeln(EnumtoString<TLanguage>(lanEn));//==>output 'lanEn'
writeln(EnumToString<TInvestment>(itDeposit));//==>output 'itDeposit'
end.

Yes I knew the syntax like EnumToString<> is not correct.Just a demonstration what I want to achieve.

Regards,
Sam
« Last Edit: August 30, 2012, 03:50:20 am by Goodman H__ »
fpc:2.6.1 Lazarus:1.1 SVN39277
OS:win 7

ludob

  • Hero Member
  • *****
  • Posts: 1173
Re: Generic way to get back enumeration value as string i/o integer
« Reply #23 on: August 30, 2012, 10:52:15 am »
Generics have different syntax in Delphi or Objfpc mode. Hence 2 snippets to satisfy your greed  ;)
Code: [Select]
{$mode delphi}
uses ... typinfo;

type
  TLanguage = ( langEN,langFR );
  TInvestment = (itDeposit,itReturn,itRealized,itDistribution,itAdjustment );

  TEnumHelper<T> = class
    function ToEnum(Es: string; prefix: string=''): T;
    function ToString(E: T; start: integer=1): string;
  end;

function TEnumHelper<T>.ToEnum(Es: string;prefix:string): T;
begin
  Result:=T(GetEnumValue(TypeInfo(T),prefix+Es));
end;

function TEnumHelper<T>.ToString(E: T; start:integer): string;
begin
  Result := copy(GetEnumName(TypeInfo(T), integer(E)),start,255);
end;
...
var
  lh:TEnumHelper<TLanguage>;
  ih:TEnumHelper<TInvestment>;
begin
  lh:=TEnumHelper<TLanguage>.create;
  writeln(lh.ToString(langEN));     //prints 'langEN'
  lh.destroy;
  ih:=TEnumHelper<TInvestment>.create;
  writeln(ih.ToString(itDeposit,3); //prints 'Deposit'
  ih.destroy;
end;

Code: [Select]
{$mode objfpc}{$H+}
uses ...,typinfo;

type

  TLanguage = ( langEN,langFR );
  TInvestment = (itDeposit,itReturn,itRealized,itDistribution,itAdjustment );

  generic TEnumHelper<T> = class
    function ToEnum(Es: string; prefix: string=''): T;
    function ToString(E: T; start: integer=1): string;
  end;

  TLanguageEnumHelper=specialize TEnumHelper<TLanguage>;
  TInvestmentEnumHelper=specialize TEnumHelper<TInvestment>;

function TEnumHelper.ToEnum(Es: string;prefix:string): T;
begin
  Result:=T(GetEnumValue(TypeInfo(T),prefix+Es));
end;

function TEnumHelper.ToString(E: T; start:integer): string;
begin
  Result := copy(GetEnumName(TypeInfo(T), integer(E)),start,255);
end;

....
var
  lh:TLanguageEnumHelper;
  ih:TInvestmentEnumHelper;
begin
  lh:=TLanguageEnumHelper.create;
  writeln(lh.ToString(langEN));     //prints 'langEN'
  lh.destroy;
  ih:=TInvestmentEnumHelper.create;
  writeln(ih.ToString(itDeposit,3); //prints 'Deposit'
  ih.destroy;
end;
Possible improvements that I leave to your judgement:
- change function ToString(E: T; start: integer=1): string;  to function ToString(E: T; prefix:string): string; and calculate the start from the string. This makes the functions more symmetrical in their use.
- add a property prefix and drop start and prefix from the functions

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Generic way to get back enumeration value as string i/o integer
« Reply #24 on: August 30, 2012, 11:43:25 am »
thank you ludob for your example I find very interesting.

Since this is my first encounter with generics can you propose any good readings?
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

ludob

  • Hero Member
  • *****
  • Posts: 1173
Re: Generic way to get back enumeration value as string i/o integer
« Reply #25 on: August 30, 2012, 01:28:52 pm »
Quote
Since this is my first encounter with generics can you propose any good readings?
I'm not a generics expert neither;)
Some interesting links for Delphi generics can be found here: http://delphi.about.com/od/objectpascalide/a/understanding-generic-types-in-delphi.htm
Info for the objfpc implementation is rather limited: http://wiki.freepascal.org/Generics , http://www.freepascal.org/docs-html/ref/refch8.html

There is also a long list in Mantis of missing Delphi style generics features.

The use of generics for one line functions is a bit exaggerated. When you use class methods you can even eliminate the creation and destruction of the enumhelper objects:
Quote
{$mode delphi}
  TEnumHelper<T> = class
    class function ToEnum(Es: string; prefix: string=''): T;
    class function ToString(E: T; start: integer=1): string;
  end;

class function TEnumHelper<T>.ToEnum(Es: string; prefix: string): T;
begin
  Result:=T(GetEnumValue(TypeInfo(T),prefix+Es));
end;

class function TEnumHelper<T>.ToString(E: T; start: integer): string;
begin
  Result := copy(GetEnumName(TypeInfo(T), integer(E)),start,255);
end;
...
begin
  writeln(TEnumHelper<TLanguage>.ToString(langEN));     //prints 'langEN'
  writeln(TEnumHelper<TInvestment>.ToString(itDeposit,3); //prints 'Deposit'
end;
But is TEnumHelper<TLanguage>.ToString(langEN) easier or better than GetEnumName(TypeInfo(TLanguage), ord(langEN))?

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Generic way to get back enumeration value as string i/o integer
« Reply #26 on: August 30, 2012, 01:40:54 pm »
But is TEnumHelper<TLanguage>.ToString(langEN) easier or better than GetEnumName(TypeInfo(TLanguage), ord(langEN))?

thank you for the links. I'm not interested on the specific problem I'm more interested in a generic Tcollection alternative for example. That would make life a lot easier if I do not have to create  the TCollection specific classes along with the TCollectionItem classes that I need.

This way I can make sure for the type safety of my collections and write less code (from my rough calculations around 30% less code) and I think I've seen less complexity on algorithm implementation especially on parsers but as  I said I'm new on this so I have a long way to go before I can have an opinion on the subject.

Thank you once more

Best regards.
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

Knipfty

  • Full Member
  • ***
  • Posts: 232
Re: Generic way to get back enumeration value as string i/o integer
« Reply #27 on: August 30, 2012, 03:15:07 pm »
Just to wrap this up, here is what I ended up with:

Code: [Select]
function ProperCase(const S: shortstring): shortstring;
begin
  Result := UpperCase(LeftStr(Trim(S), 1)+LowerCase(Copy(Trim(S), 2, 255)))
end;

function TInvestmentDesciption(const IT: TInvestment): shortstring;
begin
  Result := ProperCase(copy(GetEnumName(TypeInfo(TInvestment), ord(IT)),3,255))
end;

function TInvestmentEnum(const IT: shortstring): TInvestment;
begin
  Result := TInvestment(GetEnumValue(TypeInfo(TInvestment), Copy(GetEnumName(TypeInfo(TInvestment), ord(0)),1,2)+IT))
end;

I appreciate that others look at multiple languages, but there is zero chance that I will need that for this project.  These functions are short and sweet and require no maintenance should I add to my type definitions.

Great thread!

Knipfty
64-bit Lazarus 2.2.0 FPC 3.2.2, 64-bit Win 11

Goodman H__

  • Full Member
  • ***
  • Posts: 130
Re: Generic way to get back enumeration value as string i/o integer
« Reply #28 on: August 31, 2012, 02:57:01 am »
Generics have different syntax in Delphi or Objfpc mode. Hence 2 snippets to satisfy your greed  ;)

I've got all I can think I want!Thank you so much!

Thanks all who replied this thread and input so informative and impressive!
fpc:2.6.1 Lazarus:1.1 SVN39277
OS:win 7

 

TinyPortal © 2005-2018