2016-07-23 5 views
1

С99 позволяют инициализаторы массива (среди прочих), чтобы указать, какой элемент массива создается с натуральным целеуказателем ($ 6.7.8.6, $ 6.7.8.17), например так:обозначений в c89

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

ранее я использовал это, чтобы сделать таблицу перечисления в строку следующим образом:

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

Однако, сейчас я работаю под требованием, что мой код c89 соответствует.

Я изучил магию препроцессора (например, в here), но мне нужно, чтобы строки были произвольными, а не копиями символов перечисления.

Это не достаточно просто сделать

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

потому, что мне нужно будет добавить перечисления элементов в будущем. Используя метод c99, это приведет к указателям NULL в таблице, которые можно легко отладить, если они станут проблемами. Если я забыл обновить таблицу строк с помощью этого метода, я бы получил segfaults, которые сложнее отлаживать. Также он побеждает точку с символами, если я все равно должен помнить о смещениях.

Если декларация была в функции, я мог бы достичь желаемого эффекта, как это:

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"; 

    /* ... */ 
} 

Однако, по крайней мере, с gcc, это не получить оптимизированы.

Есть ли способ объявить такую ​​таблицу строк в c89? (Это не проблема в сборке.)

+0

Возможный дубликат [Как преобразовать имена переименований в строку в c] (http://stackoverflow.com/questions/9907160/how-to-convert-enum-names-to-string-in-c) – Leandros

+1

Там много способов, выбрали путь, который, по вашему мнению, является наименее уродливым. Есть много, много способов, один хуже другого. – Leandros

+0

На ваш вопрос, ничего такого элегантного, как C99 или новее, оставит пробелы, которые вы используете между значениями перечисления. Однако я немного смущен. Поскольку вам явно нужно изменить код в любом случае, что, по вашему мнению, займет меньше времени: «огромное предложение switch» или сообщение об этом вопросе и * все еще нужно менять код везде независимо? – WhozCraig

ответ

0

Попробовав несколько различных методов, это один простой в обслуживании:

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

Здесь вся информация о записи сгруппирована. Чтобы вставить элемент, вам нужно только изменить его. Например, чтобы вставить 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" 
} 

Это немного некрасиво (вид покоряющий, вы знаете?), Но это работает хорошо.

1

насчет простой, старомодный

const char* table[] = { "foo", "bar", "baz" }; 

Другими словами, просто положить их в правильном порядке.

char *foo_string = table[FOO]; 

Конечно, это работает только для простых перечислений, как выше, а не для перечислений в стиле

enum { FOO = 13; BAR = 15, BAZ = 312 }; 

Но для этого, вам придется создать массив с, по меньшей мере, 313 элементов, большинство из которых являются NULL в любом случае, что было бы довольно расточительной конструкцией. В таких случаях компилятор может оптимизировать это для вас, когда вы используете конструкцию switch.


Также взгляните на S.O. вопрос @Leandros указал на: How to convert enum names to string in c. Ответ там использует макросы для генерации массива, который гарантирует, что записи находятся в правильном порядке.

Или, как говорит, что ответ:

#define enum_str(s) #s 

Что избавляется от массива в целом.

+0

Проблема с другим ответом заключается в том, что он не позволяет назначать произвольную строку каждому элементу перечисления; Я уточню в вопросе. – nebuch

+0

Хорошо, вы получаете только имена в исходном коде. Таким образом, FOO не связан с «foo», он автоматически «FOO». Тогда массивы - это одно решение. Или, скажем, хэш-таблицы. Но последние не так просты. Тогда конструкция коммутатора, вероятно, проще, тем более, что перечисления редко содержат сотни элементов или больше. –

+0

Я только что видел, что у вас есть эти огромные перечисления. Затем все становится сложным или сложным. Почему эти перечисления настолько велики? –

2
#define DEF_FOO_ENUM(E0, S0, E1, S1, E2, S2) \ 
    enum foo    { E0, E1, E2 }; \ 
    const char *foo_str = { S0, S1, S2 }; 

DEF_FOO_ENUM(THING_FOO, "foo", 
       THING_BAR, "bar", 
       THING_BAZ, "baz"); 

Символы и строки в паре.Вам не легко добавить новый символ без строки, или наоборот. Чтобы добавить элемент, у вас должно быть два новых аргумента для макроса — E3, S3 — и так далее. Там нет ничего, чтобы синхронизировать там, только что enum имеет все E -s и массив имеет все S-s. Это почти невозможно испортить.

+0

Некоторые из таблиц имеют более 100 элементов. Мне нравится это решение, но это будет работать во многих Es и Ss. – nebuch

+3

Я бы либо создавал код, либо проверял каким-то внешним инструментом. То есть утилита для обработки текста, которая специально понимает эти декларации (которые обслуживаются вручную) и проверяет их целостность. – Kaz

1

Вы можете держать их вместе с помощью X-макросов:

#define MYXMACRO(OP) \ 
    OP(ENUM_FOO, "foo") \ 
    OP(ENUM_BAR, " bar") \ 
    OP(ENUM_BAZ, "baz") 

/* use the first parameter to set up your enums*/ 
enum { 
#define AS_ENUMS(x,y) x, 
MYXMACRO(AS_ENUMS) 
#undef AS_ENUMS /*not required, just playing nice*/ 
NUMTHINGS 
}; 

/* use the 2nd parameter to set up your strings*/ 
const char *strings[] = { 
#define AS_STRINGS(x,y) y, 
MYXMACRO(AS_STRINGS) 
#undef AS_STRINGS 
}; 
#undef MYXMACRO 

Теперь ваши новые данные могут быть добавлены в виде набора перечисления и строки. Если позже вы решили добавить что-то другое на основе перечислений, его легко расширить с помощью параметра «z» в OP() или даже ... и __VA_ARGS__ для множественного, но различного количества параметров.

 Смежные вопросы

  • Нет связанных вопросов^_^