Есть ли другой механизм, позволяющий препроцессору GCC сделать это:
#define LIMIT 16
#pragma GCC unroll LIMIT
for (size_t ii = 0; ii < LIMIT; ++ii) {
...
Этот код вызывает ошибку:
/path/to/my/file.c:100:20 error: 'LIMIT' undeclared (first use in this function)
100 | #pragma GCC unroll LIMIT
| ^~~~~
В документации gcc сказано, что это необходимо an integer constant expression specifying the unrolling factor
. Я считаю, что мой макрос представляет собой «целочисленное константное выражение», но...
Мой компилятор: riscv64-unknown-elf-gcc (g2ee5e430018) 12.2.0
.
Оба они работают @JonathanLeffler. Я не могу жестко запрограммировать его, потому что он генерирует код разных размеров. Я использую решение, опубликованное ниже. Спасибо!
Любопытно, что clang на это не жалуется. Хотя он жалуется, если я пишу #pragma GCC unroll gibberish
.
Я бы, наверное, назвал это еще одной ошибкой gcc. И тот, который существует с версии 8, когда, кажется, была введена эта прагма. gcc 8 дает диагностику предоставления целочисленного константного выражения. Он принимает 8+8
, но не константу препроцессора, расширяющуюся до целого числа.
#pragmas не затрагиваются препроцессором.
См. C11 §6.10.6 Директива #pragma. В сноске говорится: Реализация не обязана выполнять замену макросов в прагмах, но это разрешено, за исключением стандартных прагм (где STDC следует сразу за прагмой). Таким образом, GCC имеет право не пытаться расширять макросы; другие составители имеют право, если они это сделают. Переносимый код не может предполагать, что произойдет расширение макроса. C23 и C18 говорят примерно то же самое.
@JonathanLeffler, это убийственный ответ. Это объясняет, почему GCC не работает, а Clang работает! Спасибо!
const
и constexpr
(если вы умеете использовать C++), похоже, работают (проверено с помощью gcc 13.2 на https://godbolt.org/).
const int x = 5;
#pragma GCC unroll x
for (std::size_t ii = 0; ii < x; ++ii) {
Это работает! Спасибо! Раскрась меня в недоумении. Я думал, что макрос препроцессора будет рассматриваться как постоянное целое число. Хм....
Вместо директивы #pragma
можно использовать оператор _Pragma
. Немного сложно правильно раскрыть аргумент, но это должно работать как для C, так и для C++ (см. пример на godbolt.org):
#define STRING(V) #V
#define MAKE_PRAGMA(S) _Pragma(S)
#define UNROLL(N) MAKE_PRAGMA(STRING(GCC unroll N))
#define LIMIT 16
...
UNROLL(LIMIT)
for (size_t ii = 0; ii < LIMIT; ++ii) {
...
}
Expanding on my comment.
Стандарт C11 §6.10.6 Директива #pragma гласит:
Директива предварительной обработки формы
# pragma pp-tokens-opt new-line
где токен предварительной обработки
STDC
не следует сразу за прагмой в директиве (до любой замены макроса)174) заставляет реализацию вести себя способом, определяемым реализацией. Такое поведение может привести к сбою перевода или к тому, что транслятор или полученная программа будут вести себя несоответствующим образом. Любая такая прагма, не распознаваемая реализацией, игнорируется.Если токен предварительной обработки
STDC
сразу следует заpragma
в директиве (до любой замены макроса), то в директиве не выполняется замена макроса, и директива должна иметь одну из следующих форм175), значения которых описаны в другом месте:#pragma STDC FP_CONTRACT on-off-switch #pragma STDC FENV_ACCESS on-off-switch #pragma STDC CX_LIMITED_RANGE on-off-switch on-off-switch: one of ON OFF DEFAULT
и сноска 174 гласит:
Реализация не обязана выполнять замену макросов в прагмах, но разрешена, за исключением стандартных прагм (где
STDC
сразу следует заpragma
). Если результат замены макроса в нестандартной прагме имеет ту же форму, что и стандартная прагма, поведение по-прежнему определяется реализацией; реализации разрешено вести себя так, как если бы это была стандартная прагма, но это не обязательно.
(Сноска 175 ссылается на текст, резервирующий #pragma STDC …
для будущего использования по стандарту.)
Сноска 174 имеет решающее значение. Это означает, что GCC имеет право не предпринимать попытки макроэкспансии; другие составители имеют право, если они это сделают. Оба соответствуют стандарту C.
#pragma
.C23 и C18 говорят примерно одно и то же.
Что вы с этим сделаете, зависит от ваших требований. Однако создание оператора _Pragma
(см. §6.10.9 Прагма-оператор ), вероятно, является лучшим (наиболее переносимым и надежным) способом — как предложил Нильсен в своем ответе. Пример в стандарте показывает построение строки с заменой макросов и т.д.
Работает ли это, если вы используете
#pragma GCC unroll 16
? А что, если вы используете#pragma GCC unroll (8+8)
?