Enum-indexed arrays

bearophile bearophileHUGS at lycos.com
Sat Jan 8 07:43:39 PST 2011


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


More information about the Digitalmars-d mailing list