Сбой препроцессора из-за - '#' не сопровождается параметром макроса
Я пытаюсь упростить объявление массива, но столкнулся с проблемой с препроцессорами, которые я использую. Мой исходный код выглядит следующим образом:
#define REQ_ENTRY(parm_1, parm_2) \
#if defined(parm_1) \
{ parm_1, parm_2 }, \
#endif
typedef struct {
int parm1,
parm2;
} MyTypedef_t;
static const MyTypedef_t MyList[] =
{
REQ_ENTRY( ID_1, 1 )
REQ_ENTRY( ID_2, 2 )
REQ_ENTRY( ID_3, 3 )
REQ_ENTRY( ID_4, 4 )
REQ_ENTRY( ID_5, 5 )
};
Конечно, сборка завершается неудачно с сообщением об ошибке "error: '#' не сопровождается параметром макроса". Причина этого объясняется здесь ( почему компилятор жалуется на это объявление макроса)
По сути, я стараюсь не объявлять массив следующим образом, который работает:
static const MyTypedef_t MyList[] =
{
#if defined (ID_1)
{ ID_1, 1 },
#endif
#if defined (ID_2)
{ ID_2, 2 },
#endif
#if defined (ID_3)
{ ID_3, 3 },
#endif
#if defined (ID_4)
{ ID_4, 4 },
#endif
#if defined (ID_5)
{ ID_5, 5 },
#endif
};
Список может быть довольно длинным и варьироваться в зависимости от типа сборки проекта. Я пытался подумать об использовании x-macros, но думаю, что у меня возникнет та же проблема. Я надеюсь, что кто-то может увидеть способ создания макроса препроцессора таким образом, чтобы я мог достичь исходного синтаксиса сахара? Любое понимание очень ценится.
3 ответа
Нет хорошего чистого решения. Но есть решения разного безобразия.
Если вы не возражаете, включив в определение макроса и идентификатор, и последовательность, это можно решить следующим образом:
#define CONCAT2(x,y) x##y
#define CONCAT(x,y) CONCAT2(x,y)
#define REQ_ENTRY_YES(p1, p2) { p1 , p2 }
#define REQ_ENTRY_NO(p1)
#define IS_PAIR_HELPER(a, b, c, ...) c
#define IS_PAIR(...) IS_PAIR_HELPER(__VA_ARGS__, YES, NO)
#define REQ_ENTRY(pair) CONCAT(REQ_ENTRY_, IS_PAIR(pair))(pair)
#define ID_1 78723649, 1
#define ID_3 2347602, 3
typedef struct {
int parm1,
parm2;
} MyTypedef_t;
static const MyTypedef_t MyList[] =
{
REQ_ENTRY( ID_1 )
REQ_ENTRY( ID_2 )
REQ_ENTRY( ID_3 )
REQ_ENTRY( ID_4 )
REQ_ENTRY( ID_5 )
};
Запустить через gcc с -std=c11 -Wall -E
и показывая только MyList
определение:
static const MyTypedef_t MyList[] =
{
{ 78723649 , 1 }
{ 2347602 , 3 }
};
Вы можете сделать то же самое, используя любое второе значение в #define ID_x
макросы, пока они есть; реальные параметры могут быть добавлены к REQ_ENTRY
, Но это требует дополнительного жонглирования.
Жаль что defined
Оператор доступен только в контексте #if
а также #ifelse
, но не для расширений макросов. В настоящее время я согласен с rici в отношении решений разного безобразия.
Вот решение, которое требует, чтобы определенные значения были заключены в скобки. Затем вы можете использовать идентификатор в качестве обычного значения и передать его DEF
, который расширяется либо до 1, когда макрос находится в скобках, либо до 0, если нет. (Это уловка, которую я узнал здесь.)
С помощью DEF
макрос, вы можете создавать вспомогательные макросы, которые расширяют или игнорируют данное определение:
/* Auxiliary macros */
#define M_CHECK(...) M_CHECK_(__VA_ARGS__)
#define M_CHECK_(a, b, ...) b
#define M_IS_PAREN(x) M_CHECK(M_IS_PAREN_ x, 0)
#define M_IS_PAREN_(...) 1, 1
#define M_CONCAT(a, b) M_CONCAT_(a, b)
#define M_CONCAT_(a, b) a ## b
/* Conditional definition macros */
#define DEF(x) M_IS_PAREN(x)
#define DEF_IF_0(id, def)
#define DEF_IF_1(id, def) {id, def},
#define COND_DEF(x, y) M_CONCAT(DEF_IF_, DEF(x))(x, y)
/* Implementation */
#define ID_1 (27)
#define ID_3 (28)
#define ID_4 (29)
static const MyTypedef_t MyList[] = {
COND_DEF(ID_1, 1)
COND_DEF(ID_2, 2)
COND_DEF(ID_3, 3)
COND_DEF(ID_4, 4)
COND_DEF(ID_5, 5)
};
Это даст:
static const MyTypedef_t MyList[] = {
{(27), 1},
{(28), 3},
{(29), 4},
};
Вы также можете использовать DEF
макрос в коде, который будет расширен до 0 или 1:
printf("ID_1 is %s.\n", DEF(ID_1) ? "defined" : "undefined");
Это самое близкое, что я смог получить без введения избыточности:
#define PREPROCESSOR_IF #if
#define PREPROCESSOR_ENDIF #endif
#define PREPROCESSOR_NEWLINE /*
*/
#define REQ_ENTRY(parm_1, parm_2) \
PREPROCESSOR_IF defined(parm_1) PREPROCESSOR_NEWLINE \
{ parm_1, parm_2 }, PREPROCESSOR_NEWLINE \
PREPROCESSOR_ENDIF
typedef struct {
int parm1,
parm2;
} MyTypedef_t;
static const MyTypedef_t MyList[] =
{
REQ_ENTRY( ID_1, 1 )
REQ_ENTRY( ID_2, 2 )
REQ_ENTRY( ID_3, 3 )
REQ_ENTRY( ID_4, 4 )
REQ_ENTRY( ID_5, 5 )
};
Вам нужно запустить препроцессор только один раз, используя
-E
а также
-CC
а затем скомпилировать результат первого прохода препроцессора, однако он не работает из-за (например)
#if defined (ID_1) /*
*/ { ID_1, 1 }, /*
*/ #endif
не распознаются препроцессором как отдельные строки, поскольку комментарии заменяются только одним пробелом.
Я смог придумать решение с использованием избыточности, но вряд ли оно лучше того, что вы предложили (каждый раз выписывая весь оператор):
#define PREPROCESSOR_IF #if
#define PREPROCESSOR_ENDIF #endif
#define PREPROCESSOR_DEFINE #define
#define PREPROCESSOR_NEWLINE /*
*/
#define REQ_ENTRY_1(parm_1, parm_2) PREPROCESSOR_IF defined(parm_1)
#define REQ_ENTRY_2(parm_1, parm_2) { parm_1, parm_2 },
#define REQ_ENTRY_3(parm_1, parm_2) PREPROCESSOR_ENDIF
PREPROCESSOR_DEFINE ID_1 (27)
PREPROCESSOR_DEFINE ID_3 (28)
PREPROCESSOR_DEFINE ID_4 (29)
typedef struct {
int parm1,
parm2;
} MyTypedef_t;
static const MyTypedef_t MyList[] =
{
REQ_ENTRY_1( ID_1, 1 )
REQ_ENTRY_2( ID_1, 1 )
REQ_ENTRY_3( ID_1, 1 )
REQ_ENTRY_1( ID_2, 2 )
REQ_ENTRY_2( ID_2, 2 )
REQ_ENTRY_3( ID_2, 2 )
REQ_ENTRY_1( ID_3, 3 )
REQ_ENTRY_2( ID_3, 3 )
REQ_ENTRY_3( ID_3, 3 )
REQ_ENTRY_1( ID_4, 4 )
REQ_ENTRY_2( ID_4, 4 )
REQ_ENTRY_3( ID_4, 4 )
REQ_ENTRY_1( ID_5, 5 )
REQ_ENTRY_2( ID_5, 5 )
REQ_ENTRY_3( ID_5, 5 )
};
Это компилируется по желанию с использованием двухэтапного процесса компиляции, описанного выше.