Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Declaring array with enums error in Delphi XE6

When I declare a enum like this:

TConfigBaudRate = (br9600 = 6, br19200 = 8);

and try to use this enum with an array, like this:

const
    CBaudRates: array [TConfigBaudRate] of string = ('9600', '19200');

Delphi returns me the following error:

E2072 Number of elements (2) differs from declaration (3)

But if I declare TConfigBaudRate without values or a value for the first item it works.

It seems that Delphi creates one element of number 7. Is this a normal characteristic of Delphi? I couldn't find any reference.

like image 311
Daniel Grillo Avatar asked Dec 26 '14 16:12

Daniel Grillo


3 Answers

This is documented behavior.

From Simple Types - Enumerated Types with Explicitly Assigned Ordinality: (emphasis mine)

By default, the ordinalities of enumerated values start from 0 and follow the sequence in which their identifiers are listed in the type declaration. You can override this by explicitly assigning ordinalities to some or all of the values in the declaration. To assign an ordinality to a value, follow its identifier with = constantExpression, where constantExpression is a constant expression that evaluates to an integer. For example:

type Size = (Small = 5, Medium = 10, Large = Small + Medium);

defines a type called Size whose possible values include Small, Medium, and Large, where Ord(Small) returns 5, Ord(Medium) returns 10, and Ord(Large) returns 15.

An enumerated type is, in effect, a subrange whose lowest and highest values correspond to the lowest and highest ordinalities of the constants in the declaration. In the previous example, the Size type has 11 possible values whose ordinalities range from 5 to 15. (Hence the type array[Size] of Char represents an array of 11 characters.) Only three of these values have names, but the others are accessible through typecasts and through routines such as Pred, Succ, Inc, and Dec. In the following example, "anonymous" values in the range of Size are assigned to the variable X

var 
  X: Size; 
  X := Small;   // Ord(X) = 5 
  X := Size(6); // Ord(X) = 6 
  Inc(X);       // Ord(X) = 7

So, for your example, even though you've only used two of the values (6 & 8), 7 is an included value even though it's not named, because it falls within the ordinalities assigned, in the same way that the described array[Size] of Char would represent an array of 11 characters.

like image 182
Ken White Avatar answered Nov 13 '22 07:11

Ken White


The problem is that your enumerated type specifies ordinal values, and you missed some values out using explicitly assigned ordinality.

The key quote from the documentation is:

An enumerated type is, in effect, a subrange whose lowest and highest values correspond to the lowest and highest ordinalities of the constants in the declaration.

This program shows that ord(low(TConfigBaudRate)) is 6 and ord(high(TConfigBaudRate)) is 8.

{$APPTYPE CONSOLE}

type
  TConfigBaudRate = (br9600 = 6, br19200 = 8);

begin
  Writeln(ord(low(TConfigBaudRate)));
  Writeln(ord(high(TConfigBaudRate)));
end.

Output

6
8

This all means that your array contains three items with ordinalities 6, 7 and 8. You only specified two values in your constant, and that's what the compiler error is telling you.

You could resolve the compiler error by supplying three values, like this:

const
  CBaudRates: array [TConfigBaudRate] of string = ('9600', '', '19200');

However, I think that this whole experience is the language's way of telling you to stop using explicitly assigned ordinality.

like image 41
David Heffernan Avatar answered Nov 13 '22 06:11

David Heffernan


In addition to answers already provided...

In Delphi, the indexes of an array must form a contiguous Ordinal range. The declaration:

CBaudRates: array [TConfigBaudRate] of string;

Is equivalent to:

CBaudRates: array [Ord(Low(TConfigBaudRate))..Ord(High(TConfigBaudRate))] of string;

Or:

CBaudRates: array [6..8] of string;

And this would obviously include element 7 - even if it's not used.

Delphi's base language constructs don't have a means to define a "discrete array" (i.e. one with gaps). Which is why things like sparse arrays require higher level data structures.

Now although it would be theoretically possible for the compiler to internally map 6 == 0 and 8 == 1 (allowing your array declaration to hold only 2 elements) this would be both impractical and inefficient.

like image 2
Disillusioned Avatar answered Nov 13 '22 08:11

Disillusioned