Можно ли определить другую директиву препроцессора?
Я просматривал код для гольфа и получил идею попробовать этот код:
#define D #define
после добавления этой строки все работало нормально, однако я расширил ее следующим образом:
#define D #define
D VALUE
И здесь я получил 5 ошибок компиляции. Если я изменю D
на #define
, все будет в порядке, может кто-нибудь объяснить, почему этот код является незаконным?
ПРИМЕЧАНИЕ: Я использовал компилятор VS2008.
EDIT: После некоторых ответов я вижу, что мне нужно предоставить список ошибок компиляций:
- ошибка C2121: '#': недопустимый символ: возможно, результат расширения макроса
- ошибка C2146: синтаксическая ошибка: отсутствует ';' перед идентификатором "VALUE"
- ошибка C4430: отсутствует спецификатор типа - int. Примечание: С++ не поддерживает default-int
- ошибка C2144: синтаксическая ошибка: "void" должен предшествовать ';'
- ошибка C4430: отсутствует спецификатор типа - int. Примечание: С++ не поддерживает default-int
Первая ошибка показывает, что D
не просто define
, но также включает #
.
Ответы
Ответ 1
Этот код является незаконным, поскольку в спецификации языка указано, что оно является незаконным. Согласно спецификации препроцессора C и С++, любой код, который вы создаете с использованием препроцессора, никогда не будет интерпретироваться как другая директива препроцессора. Короче говоря, вы не можете создавать директивы препроцессора с использованием препроцессора. Период.
(Кроме того, вы не можете создавать комментарии с помощью препроцессора.)
Ответ 2
C 2011 (N1570) 6.10.3.4 3: "Полученная полностью макрозаменяемая последовательность токенов предварительной обработки не обрабатывается как директива предварительной обработки, даже если она похожа на одну,..."
С++ 2010 (N3092) 16.3.4 [cpp.rescan] 3 имеет точно такой же текст.
Ответ 3
Это похоже на то, что ваш препроцессор делает нужную вам замену, но вы, скорее всего, не получите желаемого поведения - препроцессор обычно является просто операцией с одним проходом. Пример (с clang, но вы должны иметь возможность воспроизвести, используя соответствующие флаги VS2008):
$ cat example.c
#define D #define
D VALUE
$ cc -P -E example.c
#define VALUE
То, что #define VALUE
идет прямо к компилятору, который не знает, что с ним делать - это, в конце концов, препроцессорная директива. Ошибка Clang для справки аналогична вашей:
$ cc -c example.c
example.c:2:1: error: expected identifier or '('
D VALUE
^
example.c:1:11: note: expanded from macro 'D'
#define D #define
^
1 error generated.
Ответ 4
Это не сработает, потому что предварительная обработка выполняется за один проход. Например, рассмотрим следующий код:
#define MYDEFINEWEIRD #define
MYDEFINEWEIRD N 6
int main() {
return 0;
}
После предварительной обработки ваш код будет выглядеть так:
#define N 6
int main() {
return 0;
}
и "#define" не является допустимым синтаксисом на C или С++. Кроме того, поскольку результирующая директива препроцессора не будет обрабатываться, она не будет разрешать последующие ссылки на макрос "N" в вашем коде.
Просто для удовольствия вы можете дважды вызвать препроцессор из командной строки с помощью g++/gcc. Рассмотрим следующий код (define.cpp):
#include <iostream>
#define MYDEFINEWEIRD #define
MYDEFINEWEIRD N 6
using namespace std;
int main() {
cout << N << endl;
return 0;
}
Затем вы можете сделать:
$ g++ -E define.cpp | g++ -o define -x c++ - && ./define
и выведет:
6
Ответ 5
Линии кода в глазах препроцессоров - это либо предпроцессорные операторы (и, следовательно, на них не выполняются никакие замены), либо обычные текстовые инструкции (и выполняются замены). У вас не может быть и того, и другого, поэтому, как только вы замените "D", вы увидите только, есть ли какие-либо макросы для замены. Поскольку их нет, он просто оставляет "#define" в коде С++, как есть, и тогда компилятор С++ будет ошибочен, когда увидит его (так как "#define" не является допустимым кодом С++).
Итак, покажите мою точку больше, это неверный код для предварительного процессора:
#define D define
#D value
Поскольку предварительный процессор не выполняет макрообмена в предпроцессорных операциях, а "#D" не является распознанной командой предварительного процессора. И это:
#define D #define
D value
Результаты этого кода на С++:
#define value
Это недопустимо, поскольку предварительный процессор уже выполняется.
Ответ 6
Глядя на грамматику в параграфе 16 [cpp], список замещений состоит из pp-токенов, которые могут включать в себя производственную №-директиву, которая описана в параграфе 2 того же абзаца, что и
Не директива не должна начинаться с каких-либо имен директив, входящих в список.
То есть что-то вроде формы
#define NAME # define
оказывается незаконным! Также обратите внимание, что #
в этом контексте не превращает следующее слово в строку: цитирование, следующее за #
, происходит только тогда, когда в строке #
сразу следует имя макроопределения в макрос функционального стиля.