Array initialization with enum indices in C but not C++

February 15th, 2011 at 10:43 pm

Suppose you have the following scenario: some function you’re writing needs a small lookup table keyed by enumeration values. It should preferably be static to avoid initializing it every time the function runs.

In C99, with support for named (a.k.a. "designated") initializers, this is possible in a very clean way:

#define ARRSIZE(arr) (sizeof(arr) / sizeof(arr[0]))

enum Fruit_t {
    APPLES,
    ORANGES,
    STRAWBERRIES = 8
};

void foo()
{
    static const int price_lookup[] = {
        [APPLES] = 6,
        [ORANGES] = 10,
        [STRAWBERRIES] = 55
    };

    for (int i = 0; i < ARRSIZE(price_lookup); ++i) {
        printf("[%d] = %d\n", i, price_lookup[i]);
    }
}

The named initializer here is the funny syntax inside the braces assigned to price_lookup. Any constant expression can go inside the brackets, specifying which element of the array is assigned. As long as the compiler can resolve it at compile time, it’s fine. A similar syntax is supported for struct initialization.

Turns out that if you want to achieve the same effect in C++, you’re out of luck. Standard C++ does not support the named initializer construct – you’re stuck only with the old positional initialization of array elements.

So what do you do in C++? There are at least a couple of options, none of which is as clean as the one presented above, at least for our simple use-case.

One way is to make foo a method of some class (chances are, if you’re writing C++ code, that it already is) and have the lookup table a member initialized in the constructor. This isn’t very convenient for large classes, where you should be reluctant to pollute the members and constructors with helper data structures any of its methods needs.

Another way is to encapsulate the lookup table in a singleton object. Keep in mind, though, that a singleton in C++ is mostly just a prettified global variable, so when several such lookup tables in several functions are needed, things may become less clean than you’d want.

One may wonder why this feature is supported by C but not C++. Isn’t the latter a superset of the former? Well, no. While C++ indeed is almost a superset of the old C standard (ISO C, or C89/90), with the appearance of C99 the two languages grew further apart. Named initializers is one of the examples of C99 code that can’t be compiled in C++.

Related posts:

  1. Initialization of structures and arrays in C++
  2. Initializing an array in constant time
  3. Some thoughts on JSON vs. S-expressions
  4. Variable initialization in C++
  5. Non-constant global initialization in C and C++

10 Responses to “Array initialization with enum indices in C but not C++”

  1. DavidNo Gravatar Says:

    Learned something new today. Didn’t know about C99′s named initializers. Never seemed to hold me back, but it’s good to know about it.

  2. petkeNo Gravatar Says:

    In C++0x there is std::initializer_list. With some template metaprogramming magic I’m sure one could create a function like generates initializer_lists at compile time.

    Say std::vector val = generate_initializer_list(coll, {APPLES, 6}, {ORANGES, 10}, {STRAWBERRIES, 55}));

    Then again. I don’t miss this feature much. In C++ you first create an empty list, and then append values to it. I find it a bit clearer.

  3. petkeNo Gravatar Says:

    Sorry, the “coll” there would be number of elements

  4. elibenNo Gravatar Says:

    petke,

    Thanks for the feedback. I’m not familiar with std::initializer_list (or C++0x for that matter). Creating an empty list and appending values to it is not something done at compile-time, unfortunately.

  5. EddwardNo Gravatar Says:

    std::initializer_list and & const_expr will likely become the C++ answer to this problem. Basically, std::initializer gets you “type object = { a, b, c};” style initialization. const_expr will get you limited compile time execution. For maps, you pass in key/value pairs, so this would work easily. You still won’t get designated initializers for things like vectors/arrays or class/structs without some kind of hack I don’t think.

  6. petkeNo Gravatar Says:

    C++0x is like a whole new language. std::array is a fixed size collection, that gets its elements default constructed at compile time. In C++0x std::array supposed to works with std::initializer_list just like all other collections. So I guess that means it will have to initialize with std::initializer_list at compile time. I bet one could get the syntax you want (or something close to it), if one worked some metaprogramming magic one hid inside a convenient const_expr function.

    Thanks

  7. Tim HNo Gravatar Says:

    C++0x allows this:

    map<Fruit_t, int> price_lookup = {
        {APPLES, 6},
        {ORANGES, 10},
        {STRAWBERRIES, 55}
    };
  8. elibenNo Gravatar Says:

    Tim H,

    Interesting, thanks. Can it be done at compile time, like the C version, though? I doubt it since map is a dynamic container the compiler doesn’t really know about, so this syntax is probably just syntactic sugar saving some constructor invocations.

    Again I must mention I’m unfortunately not familiar with C++0x at all.

  9. Aleksey S.No Gravatar Says:

    If you make that map static vaiable in function, it won’t be done in compile time, but it’ll be done only once during the execution. It’s not a big loss.

  10. elibenNo Gravatar Says:

    @Aleksey,

    True, and I mentioned something similar in the post itself – surely you can use a global static function, or a singleton for that. I was just lamenting the lack of possibility to avoid such external aids.

Leave a Reply

To post code with preserved formatting, enclose it in `backticks` (even multiple lines)