_Прагма и подстановка макросов

Реализуя свой собственный компилятор C11, я пытаюсь понять, как именно обрабатывать ключевое слово / оператор _Pragma. C11 §6.10.9 описывает _Pragma как оператор, поэтому кажется возможным переопределить его с помощью макросов, то есть #define _Pragma(x) SOME_OTHER_MACRO(x). Кроме того, инструкция #undef _Pragma не должна иметь никакого эффекта (при условии, что не было ранее #define из _Pragma). Это похоже на то, как могут быть #defined ключевые слова, например, старый VC ++ взлом #define for if (0) ; else for. Однако, поскольку оператор _Pragma оценивается во время фазы преобразования 3, той же фазы, что и выполнение директив препроцессора, неясно, является ли это исключением; в стандарте не упоминается, может ли его неопределенное поведение использовать _Pragma в качестве имени макроса.

Я провел некоторое тестирование с GCC, используя следующий код:

#define PRAGMA _Pragma
PRAGMA("message \"hi\"")

_Pragma ("message \"sup\"")

#undef PRAGMA

#undef _Pragma
//#define _Pragma(x)
_Pragma("message \"hello\"")

Компиляция с gcc -std=c11 -pedantic -Wall -Wextra -c выходами:

tmp.c:2:1: note: #pragma message: hi
 PRAGMA("message \"hi\"")
 ^
tmp.c:4:1: note: #pragma message: sup
 _Pragma ("message \"sup\"")
 ^
tmp.c:8:8: warning: undefining "_Pragma" [enabled by default]
 #undef _Pragma
        ^
tmp.c:10:9: error: expected declaration specifiers or ‘...’ before string constant
 _Pragma("message \"hello\"")
         ^

Если я добавлю строку #undef _Alignof, GCC на это не пожалуется.

Это говорит о том, что GCC реализует _Pragma через макрос (через предупреждающее сообщение), и что его удаление приводит к ошибке компиляции. Если я раскомментирую #define _Pragma(x), ошибка исчезнет (исчезнет строковый литерал).

Итак, мои вопросы:

  1. Разрешено ли реализациям определять _Pragma только как макрос, а не реализовывать его как оператор?
  2. Если нет, то ошибается ли GCC в этом?
  3. если _Pragma должен быть оператором, является ли неопределенное поведение определение _Pragma как макроса?
  4. Есть ли какой-либо порядок между оценкой _Pragma и другими директивами препроцессора? Или у них одинаковый «приоритет» (т.е. они оцениваются по порядку)?

Опять же, при просмотре стандарта C11 ничего не упоминается о _Pragma, кроме этого оператора, который можно использовать для #pragma директив.


person Drew McGowen    schedule 23.07.2014    source источник
comment
Ожидайте, что некоторые существенные мнения будут играть роль в ответах, которые вы получите на этот вопрос (например, void main(void) обсуждения), однако ваше исследование очевидно, и ваши вопросы хорошо представлены. Должен привести к интересным откликам. (+1)   -  person ryyker    schedule 23.07.2014
comment
Я не уверен, правильно ли я понимаю стандарт здесь, но я думаю, что применяется C11 7.1.3 p3: «Если программа удаляет (с помощью #undef) любое макроопределение идентификатора в первой группе, указанной выше, поведение не определено ». где упомянутая «первая группа» - это идентификаторы, начинающиеся с символа подчеркивания, за которым следует другой символ подчеркивания или заглавная буква.   -  person mafso    schedule 23.07.2014
comment
@mafso, который, кажется, применяется только в том случае, если есть определение макроса в первую очередь, поэтому код типа #ifndef _Pragma <newline>#undef _Pragma<newline>#endif не будет вызывать UB   -  person Drew McGowen    schedule 23.07.2014
comment
@DrewMcGowen - Если стандарт C11 определяет Pragma как _A унарное операторное выражение формы: _Pragma ( строковый литерал ) ..., и реализация решает реализовать это другим способом, будет ли эта другая реализация считаться C? (ссылка 6.10.9 Оператор Pragma, в стандарте C11, проект комитета - 12 апреля 2011 г.)   -  person ryyker    schedule 24.07.2014
comment
аналогично тому, как #defined могут быть определены ключевые слова, например, старый совет VC ++ #define for if (0); остальное для -… вау. Я никогда раньше об этом не слышал - stackoverflow.com/questions/984878/   -  person Rusty Shackleford    schedule 21.02.2018


Ответы (1)


Нет необходимости в специальном правиле, которое запрещало бы _Pragma быть именем макроса. Имея начальное подчеркивание и заглавную букву, он входит в число зарезервированных идентификаторов, которые вы в любом случае не должны использовать. Использование зарезервированных идентификаторов приводит к неопределенному поведению вашей программы, компилятор может делать все, что угодно.

Реализация может реализовать его как макрос, но это должно быть прозрачно для вас, если вы используете его правильно, то есть пока вы не возитесь с ним. Единственная важная вещь, которую должна гарантировать реализация, - это то, что «дестрингизация» и «токенизация» аргумента _Pragma выполняется, как если бы в фазе 3 (что сложно, если это «всего лишь» макрос) и что результирующий #pragma Директива обрабатывается на этапе 4.

person Jens Gustedt    schedule 23.07.2014
comment
Практически неясно, что в стандарте подразумевается под зарезервированным для любого использования - конечно, такой оператор, как int ln = __LINE__;, может рассматриваться как использование идентификатора __LINE__ (если он не зарезервирован для любого использования, он не включает обязательные идентификаторы). - person Drew McGowen; 24.07.2014
comment
@DrewMcGowen: зарезервировано для любого использования означает, что пользователь не может объявлять или определять идентификаторы этой формы для любого использования (поскольку она зарезервирована). Сравните это с формами идентификаторов, которые зарезервированы только для идентификаторов с областью действия файла или только если включен определенный заголовок. - person Michael Burr; 24.07.2014
comment
@DrewMcGowen, это имеет очень точное значение, которое предусмотрено в 7.1.3. В параграфе 2 сказано, что вы не должны с ним делать: Если программа объявляет или определяет идентификатор в контексте, в котором он зарезервирован (кроме разрешенного 7.1.4), или определяет зарезервированный идентификатор как имя макроса, поведение не определено. - person Jens Gustedt; 24.07.2014
comment
Имея начальное подчеркивание и заглавную букву, это один из зарезервированных идентификаторов, которые вы не должны использовать, ... +1 - person ryyker; 24.07.2014
comment
Является ли _Pragma полноправным гражданином в качестве оператора C? Я имею в виду, будет ли int *c = _Pragma(ObscureIntAllocator); законным кодом C? - person cesss; 28.11.2020