Макросы препроцессора C: проверьте, был ли объявлен токен - PullRequest
3 голосов
/ 05 сентября 2010

Это для экспертов препроцессора C :

Как я могу объявить enum со списком некоторых идентификаторов и позже во время проверки оператора switch, если идентификатор был включенв списке?

Пример того, что мне нужно:

typedef enum { e1, e2, e3, e4, e5, e6 } e;

e x;
switch (x) {
#if DECLARED_IN_ENUM (e1)
  case e1 : ...
#endif
  /* etc. */
}

Я думал об использовании последовательности Boost и развертывании ее в список через запятую в перечислении, но как я могу проверить позжеесли последовательность содержит определенный токен?

РЕДАКТИРОВАТЬ : что я смог сделать с помощью Boost:

#define e1 e1
#define e2 e2
#define e3 e3
#define e4 e4
#define e5 e5
#define e6 e6
#define E (e1)(e2)(e3)(e4)(e5)(e6)

typedef enum { BOOST_PP_SEQ_ENUM(E) } e;

e x;
switch (x) {
#if defined (e1)
  case e1 : ...
#endif
  /* etc. */
}

Это не очень красиво, и я быпредпочитаю что-то вроде:

#define E (e1)(e2)(e3)(e4)(e5)(e6)

typedef enum { BOOST_PP_SEQ_ENUM(E) } e;

e x;
switch (x) {
#if BOOST_PP_SEQ_CONTAINS (e1,E)
  case e1 : ...
#endif
  /* etc. */
}

но как можно реализовать BOOST_PP_SEQ_CONTAINS?

Ответы [ 4 ]

4 голосов
/ 05 сентября 2010

Вы не можете.Препроцессор C не «понимает» язык программирования C, он просто маркирует его.Он не знает, что на самом деле означает «enum».Компилятор обрабатывает это.

Если вы хотите что-то протестировать в препроцессоре, вам придется предоставить макросы препроцессора для его использования.

Редактировать: извините, вы пропустилииспользовать Boost.Preprocessor.Я не знаю, может ли это обеспечить необходимые макросы или нет, если вы включили что-то из Boost в определение вашего перечисления.

2 голосов
/ 05 сентября 2010

Я не думаю, что BOOST_PP_SEQ_CONTAINS может быть реализовано. Для этого вам потребуется сравнить две последовательности токенов предварительной обработки, чего вы не можете сделать.

Однако, если вы немного перестроите свою логику, вы можете получить что-то ближе к тому, что вы хотите. Во-первых, нам нужна пара вспомогательных макросов для использования с BOOST_PP_SEQ_FOR_EACH:

#include <boost/preprocessor.hpp>

// General purpose macros:
#define EXPAND_ENUM_CASE_2(text1, text2) text1 ## text2
#define EXPAND_ENUM_CASE(r, data, elem) \
    case elem : EXPAND_ENUM_CASE_2(data ## _ ## CASE ## _ , elem)

Мы можем определить список перечислителей и перечисление так же, как в исходном вопросе:

#define WORKDAY_ENUMERATORS (Monday)(Tuesday)(Wednesday)(Thursday)

enum Workday { BOOST_PP_SEQ_ENUM(WORKDAY_ENUMERATORS) }; 

Как вы можете видеть, я исключил пятницу из списка, потому что никто не работает в пятницу. Давайте рассмотрим в качестве примера функцию, которая возвращает некоторый текст, описывающий день недели.

Вместо того, чтобы проверять, был ли перечислитель включен в список, мы определяем регистры для каждого из значений, используя макросы:

#define WORKDAY_CASE_Monday    { return "Mondays suck";                     }
#define WORKDAY_CASE_Tuesday   { return "Tuesdays are better than Mondays"; }
#define WORKDAY_CASE_Wednesday { return "Hooray for humpday!";              }
#define WORKDAY_CASE_Thursday  { return "Thursdays are okay";               }
#define WORKDAY_CASE_Friday    { return "No one really works on Friday";    }

Затем мы генерируем правильные операторы case для списка, используя WORKDAY_ENUMERATORS и объединяя перечислители с префиксом WORKDAY_CASE_:

const char* get_day_text(Workday d)
{    
    switch (d)
    {
        BOOST_PP_SEQ_FOR_EACH(EXPAND_ENUM_CASE, WORKDAY, WORKDAY_ENUMERATORS)
    }
    return "WTF?!  That's not a workday!";
}

Если день не был включен в список WORKDAY_ENUMERATORS, для него не будет создано ни одного случая.

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

#undef WORKDAY_CASE_Monday
#undef WORKDAY_CASE_Tuesday
#undef WORKDAY_CASE_Wednesday
#undef WORKDAY_CASE_Thursday
#undef WORKDAY_CASE_Friday

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

0 голосов
/ 05 сентября 2010

Один из подходов состоит в том, чтобы иметь большой большой файл #define или ".h", который охватывает все ваши рабочие дни (у файла .h есть то преимущество, что вам не нужно заканчивать все лайки обратной косой чертой) и включает в себя все соответствующие информация для них в макросах. Затем #define макрос генератора для выполнения чего-либо, вызов большого макроса (или #include заголовок), #undef макрос генератора и определение его для выполнения чего-то другого, повторный вызов большого макроса и т. Д. В этом сценарии один вариант макрос генератора сгенерирует что-то вроде «case ENUM_foo: func_foo (); break;». Затем вы можете написать весь соответствующий код для соответствующих функций func_ *, и они будут вызваны соответствующим образом.

0 голосов
/ 05 сентября 2010

Только не используйте enum. Это не служит никакой полезной цели. Объявите все свои константы с #define и используйте #ifdef.

...