Search code examples
arraysindexingenumsd

D straight array indexed by an enum


In my D program, I have a read-only array of fixed length and I wish to index the array by an enumerated type.

If I do something like

static const my_struct_t aray[ my_enum_t ] = ... whatever ...;

my_enum_t index;

result = aray[ index ];

then the code produced by GDC is huge, full of calls to the runtime when the array is indexed. So it looks as if either the array is being treated as variable-length or as an associative array (hash table) or something, anyway far from a lightweight C-style array of fixed length with straightforward indexing. Since enums have a fixed cardinality and can't grow, and I have a modest sparse range of values (I'm not misusing the keyword enum just to defined a load of random constants) then I don't know why this happened.

I fixed the issue by changing the line to

static const my_struct_t aray[ my_enum_t.max + 1 ]

and as I understand it that will mean the value in the square brackets is just a known constant of integral type. Since the index is now not an enum at all, I now have an array indexed by an integer, so I have lost type checking, I could index it with any random integer typed variable rather than ensuring that only the correct (strong) type is used.

What should I be doing?

In the more general case, (silly example)

static const value_t aray[ bool ] = blah

for example, where I have an index type that is perfectly sensible semantically, but not just a typeless size_t/int/uint I presume I would get the same problem.

I wouldn't want to say that this is a compiler design problem. It's certainly a case of sub-optimal behaviour. But to be fair to the compiler what exactly is telling it whether the array is fixed-length or variable, and sparse or dense? I want two things; type checking of the index and non-variable length. Actually, in this particular case the array is const (I could have put immutable just as well) so it clearly can't be variable-length any way. But with an array that has modifiable content but is of fixed length you need to be able to declare that it is fixed-length.


Solution

  • V[K] name is the syntax for an associative array which does indeed do runtime calls and such, even when the type is limited to a small number of values like bool or an enum. The compiler probably could optimize that, making it act to the program like an AA while implementing it as a simple fixed-length array, but it doesn't; it treats all key types the same.

    I would suggest going with what you started: T[enum.max + 1], but then doing a wrapper if you want to force type safety. You can make the index overloads static if you only want one instance of it:

    enum Foo {
            one,
            two
    }
    
    struct struct_t {}
    
    struct array {
            static private struct_t[Foo.max + 1] content;
            static struct_t opIndex(Foo idx) { return content[cast(int) idx]; }     
    }
    
    void main() {
            struct_t a = array[Foo.one];
    }
    

    Then, you can just genericize that if you want simpler reuse.

    struct enum_array(Key, Value) {
            static private struct_t[Key.max + 1] content;
            static Value opIndex(Key idx) { return content[cast(int) idx]; }
    }
    
    alias array = enum_array!(Foo, struct_t);
    

    Or, of course, you don't need to make it static, you could do a regular instance too, and initialize the contents inside and such.