2

I need to browse all published properties of some classes. Properties where the type is an enumeration with fixed values are not listed.

See example below:

TMyEnum = (meBlue, meRed, meGreen);
TMyEnumWithVals = (mevBlue=1, mevRed=2, mevGreen=3);
TMyClass =
...
published
  property Color: TMyEnum read FColor write SetColor; // This one is found
  property ColorVal: TMyEnumWithVals read FColorVal write SetColorVal; // This one is never found
end;

I need fixed values because these properties are stored in a database and I need to ensure that allocated values will always be the same, regardless of Delphi compiler choices in next versions, and prevent any misplaced insert of future values in the enumeration list.

I tried with both new Delphi 2010 RTTI (with .GetDeclaredProperties) and "old" RTTI (with GetPropInfos): all properties are found except the above type of property.

The same behaviour is seen on all classes. I also reproduced this in a sample project.

Tried with and without various RTTI directives without change.

Is this a bug, a known limitation? Is there a workaround (except removing fixed values of the enumeration)?

Using Delphi2010 Ent+Update5

[Edit] The answer below provides the workaround: The first value of the enumeration must be set to 0 instead of 1, and values contiguous. Tested and working solution.

Thanks,

user315561
  • 651
  • 5
  • 18

3 Answers3

8

Barry Kelly says:

Discontiguous enumerations and enumerations which don't start at zero don't have typeinfo. For typeinfo to be implemented, it would need to be in a different format from the existing tkEnumeration, owing to backward compatibility issues.

I considered implementing a tkDiscontiguousEnumeration (or possibly better named member) for Delphi 2010, but the benefit seemed small considering their relative scarcity and the difficulties in enumeration - how do you encode the ranges efficiently? Some encodings are better for some scenarios, worse for others.

So it's not a bug, but a known limitation.

Community
  • 1
  • 1
Wouter van Nifterick
  • 23,603
  • 7
  • 78
  • 122
  • 1
    Thanks for the prompt answer. So there is finally a simple workaround to my question: let enumeration explicit values start with 0 instead of 1. – user315561 Oct 22 '11 at 07:17
3

This is a limitation. Enumerated types with explicit ordinality can not have RTTI. Documented here.

Possible solution is to declare Reserved values within your enumerated type, eg:

TMyEnum = (meReserved1, meBlue, meRed, meGreen, meReserved2, meWhite); // and so on
...
Assert(MyEnum in [meBlue..meGreen, meWhite], 'sanity check failed')

Practical enums tends to occupy contiguous ranges, so you probably will not end up in having declaring gazillions of reserved values. In case you are interoperating with C style bitfields you will have to split value to distinct enums and sets.

Premature Optimization
  • 1,917
  • 1
  • 15
  • 24
1

To add some more background information, Enums with values, are treated as subrange types with predefined constants.

For example:

type
  TMyEnumWithVals = (mevBlue=1, mevRed=2, mevGreen=3);

Is treated as:

type
  TMyEnumWithVals = 1..3;
const 
  mevBlue  : TMyEnumWithVals = 1;
  mevRed   : TMyEnumWithVals = 2;
  mevGreen : TMyEnumWithVals = 3;

However, you can't combine these with integers without casting.

var
  enum   : TMyEnumWithVals;
  ival   : Integer;
begin
  enum := mevBlue;
  ival := Integer(mevRed);

To make matters worse, if you use:

type
  TTypeId  = (tidPrimary = 100, tidSecundary = 200, tidTertiary = 300);
  TTypeArray = array [TTypeID] of string;

You get an array of 201 elements, not of 3. You can use the values in between using:

var
  enum   : TTypeId  ;
  ival   : Integer;
begin
  enum := TTypeId(150);  // Is valid.

This information can be found in the Delphi language guide, with the enum type section.

Toon Krijthe
  • 52,876
  • 38
  • 145
  • 202