Определить массив и символьные индексы одновременно - PullRequest
1 голос
/ 27 апреля 2009

Я пытаюсь придумать умный способ (на C) создать массив строк вместе с символическими именами (enum или #define) для индексов массива в одной конструкции для удобства обслуживания. Что-то вроде:

const char * strings [] = { M (STR_YES, "да"), M (STR_NO, "нет"), M (STR_MAYBE, «возможно») };

где результат будет эквивалентен:

const char * strings [] = {"yes", "no", "Maybe"}; индексы перечисления {STR_YES, STR_NO, STR_MAYBE}; (или #define STR_YES 0 и т. д.)

но я рисую пробел для того, как построить макрос M в этом случае.

Какие-нибудь умные идеи?

Ответы [ 3 ]

6 голосов
/ 27 апреля 2009

Метод, используемый в исходном коде компилятора clang, заключается в создании .def файлов, содержащих такой список, который разработан как файл C и может легко поддерживаться, не затрагивая другие файлы кода, которые его используют. Например:

#ifndef KEYWORD
#define KEYWORD(X)
#endif
#ifndef LAST_KEYWORD
#define LAST_KEYWORD(X) KEYWORD(X)
#endif

KEYWORD(return)
KEYWORD(switch)
KEYWORD(while)
....
LAST_KEYWORD(if)

#undef KEYWORD
#undef LAST_KEYWORD

Теперь, что он делает, так это файл, подобный следующему:

/* some code */
#define KEYWORD(X) #X, 
#define LAST_KEYWORD(X) #X

const char *strings[] = { 
#include "keywords.def"
};

#define KEYWORD(X) kw_##X, 
#define LAST_KEYWORD(X) kw_##X

enum { 
#include "keywords.def"
};

В вашем случае вы могли бы сделать подобное. Если вы можете использовать STR_yes, STR_no, ... в качестве имен перечислителей, вы можете использовать тот же подход, что и выше. В противном случае просто передайте макросу две вещи. Одно строчное имя и одно заглавное имя. Тогда вы можете зачеркнуть тот, который вы хотите, как указано выше.

4 голосов
/ 27 апреля 2009

Это хорошее место для генерации кода. Используйте такой язык, как perl, php или любой другой, для создания вашего файла .h.

0 голосов
/ 28 апреля 2009

Нет необходимости помещать это в определенные файлы .def; использование только препроцессора вполне возможно. Я обычно определяю список с именем ...LIST, где каждый элемент содержится в ...LIST_ELEMENT. В зависимости от того, для чего я буду использовать список, я буду либо просто разделять запятой для всех, кроме последней записи (самой простой), либо в общем случае позволять выбирать разделитель индивидуально для каждого использования. Пример:

#include <string.h>

#define DIRECTION_LIST \
DIRECTION_LIST_ELEMENT( up,    DIRECTION_LIST_SEPARATOR ) \
DIRECTION_LIST_ELEMENT( down,  DIRECTION_LIST_SEPARATOR ) \
DIRECTION_LIST_ELEMENT( right, DIRECTION_LIST_SEPARATOR ) \
DIRECTION_LIST_ELEMENT( left,  NO_COMMA )

#define COMMA ,
#define NO_COMMA /**/

#define DIRECTION_LIST_ELEMENT(elem, sep) elem sep
#define DIRECTION_LIST_SEPARATOR COMMA
typedef enum {
    DIRECTION_LIST
} direction_t;
#undef DIRECTION_LIST_ELEMENT
#undef DIRECTION_LIST_SEPARATOR

#define DIRECTION_LIST_ELEMENT(elem, sep) void (*move_ ## elem)(struct object_s * object);
#define DIRECTION_LIST_SEPARATOR NO_COMMA
typedef struct object_s {
    char *name;
    // ...
    DIRECTION_LIST
} object_t;
#undef DIRECTION_LIST_ELEMENT
#undef DIRECTION_LIST_SEPARATOR

static void move(object_t *object_p, const char * direction_string)
{
    if (0) {
    }
#define DIRECTION_LIST_SEPARATOR NO_COMMA
#define DIRECTION_LIST_ELEMENT(elem, sep) \
    else if (strcmp(direction_string, #elem) == 0) { \
        object_p->move_ ## elem(object_p); \
    }
    DIRECTION_LIST
#undef DIRECTION_LIST_ELEMENT
#undef DIRECTION_LIST_SEPARATOR
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...