A little ObjectPascal program (works with FreePascal, similar code works in Ada 
too):

Type  
  Direction = (North, East, South, West); 
  Tarray = array [Direction] of integer;
var
  a: Tarray;
  d: Direction;
begin
  d := North;
  a[d] := 1;
  writeln(a[d]);
end.


'Direction' is an assigned enumerated type, similar to a D enum, and T is a 
strong type (like D1 typedef, currently there is no replacement in D2) of a 
fixed-sized (here stack-allocated) array of four integers that has Direction as 
indexes.

Such kind of enum-indexed arrays are common enough, and in ObjectPascal they 
are efficient and safe (the compiler refuses an index of type different from 
Direction. There is no need of runtime array bound tests).

A similar D2 program is:


import std.stdio: writeln;
enum Direction { North, East, South, West }
alias int[Direction] Tarray; // weak type definition
void main() {
    Direction d = Direction.North;
    Tarray a = [d: 1];
    writeln(a[d]);
    // a[5] = 1; // good, cannot implicitly convert expression
}


Beside not being Tarray a strong type definition, another important difference 
is that Tarray is an associative array.
An advantage of associative arrays over fixed-sized arrays is that it works 
even if you assign arbitrary numbers to the enum items ({ North = 756, ... }), 
and it's managed by reference (so there are less troubles in passing it 
around), but compared to a naive fixed-sized array they are quite less 
efficient in performance, memory and GC activity.

So when enums are default ones (with no numbers specified for indexes) I miss 
Enum-indexed fixed-sized arrays a bit in D2 :-)

A first try at D2 implementation:


import std.stdio: writeln;
import std.traits: EnumMembers;

struct EnumIndexedArray(E, T) if (is(E == enum)) {
    static bool areConsequential(E)() {
        foreach (i, field; EnumMembers!E)
            if (i != field)
                return false;
        return true;
    }

    static assert(areConsequential!E(),
                  "Enum fields aren't consequential starting from 0");

    alias EnumMembers!E Efields;
    enum int NFIELDS = Efields.length;
    T[NFIELDS] data;
    alias data this;

    T opIndexAssign(T value, E e) { data[e] = value; return value; }
    T opIndex(E e) { return data[e]; }
}

// demo ---------------

enum Direction { North, East, South, West }
alias EnumIndexedArray!(Direction, int) Tarray;

void main() {
    Direction d = Direction.North;
    Tarray a;
    a[d] = 1;
    writeln(a[d]);
    //a[2] = 1; // good, error
}

Bye,
bearophile

Reply via email to