nebuch nebuch - 3 months ago 7
C Question

Designators in c89

C99 allows array initializers (among others) to specify which element of the array is being set with a positive integer designator ($6.7.8.6, $6.7.8.17), for example like so:

const char *foo[] = {[2] = "foo", [1] = "bar", [0] = "baz"};


I have previously used this to make an enum-to-string table like so:

enum {THING_FOO = 0, THING_BAR, THING_BAZ};
const char *table[] = {
[THING_FOO] = "foo",
[THING_BAR] = "bar",
[THING_BAZ] = "baz"
}


However, I am now working under the requirement that my code is c89 compliant.

I have looked into preprocessor magic (as in here, for example) but I need the strings to be arbitrary, not copies of the enum symbols.

It isn't sufficient to just do

enum {THING_FOO = 0, THING_BAR, THING_BAZ};
const char *table[] = {"foo", "bar", "baz"};


because I will need to add enum elements in the future. Using the c99 method, this would result in NULL pointers in the table which are acceptably easy to debug if they become problems. If I forgot to update the string table using this method, I'd get segfaults which are harder to debug. Also it defeats the point of having symbols if I have to remember offsets anyway.

If the declaration were in a function, I could achieve the desired effect like this:

enum {THING_FOO = 0, THING_BAR, THING_BAZ, NUM_THINGS};
void foo(void)
{
static const char *table[NUM_THINGS];
table[THING_FOO] = "foo";
table[THING_BAR] = "bar";
table[THING_BAZ] = "baz";

/* ... */
}


However, at least with
gcc
, this does not get optimized.

Is there any way of declaring such a string table in c89?
(It's no problem in assembly.)

Answer

After trying a few different techniques, this one is easiest to maintain:

const char *table[] = {
#define FOO 0
    "foo",
#define BAR (FOO + 1)
    "bar",
#define BAZ (BAR + 1)
    "baz"
}

Here all the information about an entry is clustered. To insert an element you only have to modify stuff right around it. For example to insert qux:

const char *table[] = {
#define FOO 0
    "foo",
#define QUX (FOO + 1)    /* new */
    "qux",               /* new */
#define BAR (QUX + 1)    /* modified */
    "bar",
#define BAZ (BAR + 1)
    "baz"
}

It's a bit ugly (kind of endearing, you know?) but it works good.

Comments