Delphi Clinic | C++Builder Gate | Training & Consultancy | Delphi Notes Weblog | Dr.Bob's Webshop |
|
One of the earliest Under The Hood Columns focussed on RTTI (Run-Time Type Information), and show us how we could use RTTI to get inside components and get a list of properties, etc. This time, we again make use of RTTI, and we will see that the syntax has changed somewhat between Delphi 1 and 2 - something we were warned about...
Run-Time Type Information is stored within classes, so we can use it to identify a class and use the stored information for our own purposes. However, RTTI is not only stored with classes, but with regular types as well. Or at least, we can use RTTI when dealing with regular types...
Take for example an enumerated type:
type TCustomColor = (Red, Blue, Green);It would be nice to be able to convert these enumerated types Red, Blue and Green to their string equivalent ('Red', 'Blue' and 'Green'). In fact, this is a question that I've been receiving a lot lately (I have a standard answer ready ;-)
We need to take a closer look at the unit TypInfo (you know, the one with the warning that the implementation will change from one version to another).
Inside this unit, we find a function called GetEnumName, which sounds at least to be what we're looking for.
The Delphi 2.x version of GetEnumName is implemented as follows:
function GetEnumName(TypeInfo: PTypeInfo; Value: Integer): string; var P: ^ShortString; begin P := @GetTypeData(GetTypeData(TypeInfo)^.BaseType)^.NameList; while Value <> 0 do begin Inc(Integer(P), Length(P^) + 1); Dec(Value); end; Result := P^; end;Using this function, we can write our ColorStr function. The first argument needs to be the TypeInfo of the enumerated type, which is returned by a call to the TypeInfo built-in function. The second argument is the (Ord) value of the enumerated element we want the string from, which is even easier.
program Hood9; {$APPTYPE CONSOLE} uses TypInfo; type TCustomColor = (Red, Blue, Green); function ColorStr(Color: TCustomColor): String; begin ColorStr := GetEnumName(TypeInfo(TCustomColor), Ord(Color)) end {ColorStr}; var Color: TCustomColor; begin for Color := Red to Green do writeln(ColorStr(Color)) end.
The above code works flawlessly with Delphi 2.x. However, if we try the same code with Delphi 1.x, it doesn't work, and in fact will give us an "invalid type" error message when trying to compile. What are we doing wrong? Well, we didn't pay attention to the warning in the TYPINFO.PAS for Delphi 1.0, which cleary states:
{ Warning: } { The interface section of this file will change } { in future versions of Delphi. }So, while we found out how it works in Delphi 2.x, it used to work in a different way (porting down is even more fun sometimes than porting up)...
function GetEnumName(TypeInfo: PTypeInfo; Value: Integer): PString;GetEnumName is returning a pointer-to-a-String here, while in the Delphi 2 version we get a plain String! So, fortunately all that was changed for GetEnumName from one version to another (so far) is the need for a pointer dereference symbol in the Delphi 1.x version, which leads to the use of an {$IFDEF} from now on:
function ColorStr(Color: TCustomColor): String; begin {$IFDEF WIN32} ColorStr := GetEnumName(TypeInfo(TCustomColor), Ord(Color)) {$ELSE} ColorStr := GetEnumName(TypeInfo(TCustomColor), Ord(Color))^ {$ENDIF} end {ColorStr};
What have we learned today? Well, Delphi is storing more in RTTI than we would've thought at first sight. Unfortunately, the syntax of the RTTI internal routines changed somewhat from Delphi 1 to 2, but once you find out how to port them, you can even get RTTI strings for regular enumerated types!