www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Enum-indexed arrays

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
Jan 08 2011