Подтвердить что ты не робот

C Препроцессор: сначала оцените макрос

Рассмотрим следующую настройку:

хиджры

#define A 5
#define B A
#undef A
#define A 3

a.c

#include "a.h"
#include <stdio.h>

int main()
{
    printf("%d\n", B);
    return 0;
}

В то время как это очень разумно печатает 3, существует ли способ сделать его печать 5, т.е. сделать замену 5 для A уже на второй строке a.h?

4b9b3361

Ответ 1

Нет, нет способа сделать это. Если вы не знаете все возможные значения A, и они всегда являются целыми числами, в этом случае вы можете с трудом проверить каждый из них по очереди:

#if A == 0
# define B 0
#elif A == 1
# define B 1
#elif A == 2
# define B 2
/*  ... and a very long etc. */
#endif

Если ваш случай использования включает только целые числа, у вас есть больше вариантов. Например, можно объявить B как static const int или enum (в зависимости от языка) вместо макроса, который, очевидно, будет использовать текущее значение макроса. Если вам действительно нужны макросы, библиотека предварительной обработки Boost имеет реализацию трудоемкой последовательности #if выше (с некоторой уловкой, чтобы уменьшить число инструкций препроцессора, необходимых для log (N) вместо N).


В директиве #define препроцессора нет макроподстановки; этот факт подпадает под действие пункта 6.10. 7 стандарта C (раздел 16, пункт 6 стандарта С++, с идентичной формулировкой):

Знаки предварительной обработки в директиве предварительной обработки не подпадают под макрорасширение, если не указано иное.

В описании директив #if и #include в стандарте указывается, что происходит замена макроса, поэтому работает выше работающее решение #if (и реализация Boost, которая также использует вычисленный #include).

Ответ 2

Да. Boost preprocessor library (набор переносимых включает, а не расширенный препроцессор) включает поддержку "изменяемых" макроопределений. Вместо того, чтобы определять макрос для непосредственного расширения до значения, вы можете определить его для расширения до ссылки на изменяемый слот, который можно изменить, потому что он рано расширяет значение "назначено" ему. В этом случае вы менее заинтересованы в способности изменить значение, чем в том, что это раннее расширение означает, что он может извлечь значение из A в точку, предшествующую как использованию B, так и переопределению A.

#include <boost/preprocessor/slot/slot.hpp>

#define A 5
#define B BOOST_PP_SLOT(1)

// "assign" A to B
#define BOOST_PP_VALUE A
#include BOOST_PP_ASSIGN_SLOT(1)

#undef A
#define A 3

#include "a.h"
#include <stdio.h>

int main()
{
    printf("%d\n", B);  // 5
    return 0;
}

Поддержка ограничена целыми числами. Он использует тот факт, что директивы #if принудительно расширяют любые содержащиеся макросы (например, #line и #error, хотя для этой цели они не очень полезны), и использует их для создания эквивалентного целочисленного значения для слот назначается, сохраняется в скрытых макросах. Таким образом, он может "извлечь" значение из A, а B может ссылаться на само значение, даже если A изменяется или удаляется.