В приведенном выше надуманном примере, если используется вторая форма макроса B_SHIFT, определение макроса при первом сканировании препроцессора хочет расширить A, что (конечно) не может.
Проблема здесь в том, что вы организовали замену MSB(B)
на себя, пытаясь выполнить подстановку MSB(A)
. Препроцессор C не допускает рекурсивной замены одного и того же макроса, даже в таких случаях, когда рекурсия полностью безопасна. В рамках подстановки макроса имя заменяемого макроса фактически не определено. Тот факт, что вы видите сообщение об ошибке, ссылающееся на A
, является следствием того, что MSB(A)
проходит через неизмененное, в результате чего и MSB
, и A
отображаются как токены после предварительной обработки. Поскольку ни один из них не был объявлен, компилятор жалуется на использование необъявленной функции (MSB
) и переменной (A
).
Одно простое (но явно не идеальное) исправление - использование двух идентичных макросов. определения:
#define SHIFT(label) label##_SHIFT
#define WIDTH(label) label##_WIDTH
#define MSB(label) label##_SHIFT + label##_WIDTH
#define MSB_(label) label##_SHIFT + label##_WIDTH
#define A_SHIFT 0
#define A_WIDTH 4
#define B_SHIFT MSB_(A)
#define B_WIDTH 4
Это позволит расширить расширение B_SHIFT
внутри расширения MSB(B)
.
Если вы хотите посмотреть, как может выглядеть другое решение,вы можете взглянуть на библиотеку препроцессора Boost .