Tags C & C++

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++.