Особенности работы препроцессирования lcc¶
Пара слов о запуске препроцессирования¶
Технически в компиляторах lcc и gcc запуск препроцессирования возможен двумя способами:
Использование компилятора lcc или gcc с дополнительной опцией
-E
или-P
, которая включает режим препроцессированияИспользование препроцессора cpp. Здесь нужно понимать, что сами препроцессоры из поставок lcc и gcc имеют одно и то же имя cpp, но имеют разное устройство
Между этими двумя способами имеется ряд небольших отличий. Однако в рамках данной статьи эти отличия существенной роли не играют. Поэтому в примерах мы будем использовать только первый вариант, а в голове будем держать, что для второго варианта всё симметрично
Примеры отличия препроцессирования lcc от препроцессирования gcc¶
Техника работы препроцессирования в компиляторе lcc в некоторых случаях немного отличается от техники работы препроцессирования в gcc. Это может привести к ошибкам при сборке программ, которые используют особенности работы препроцессирования gcc. Рассмотрим несколько примеров, демонстрирующих отличие препроцессирования lcc от препроцессирования gcc
Пример 1¶
#define MACRO a.b.c
MACRO
$ gcc -E t.c
a.b.c
$ lcc -E t.c
a . b . c
В препроцессорной выдаче lcc появились лишние пробелы вокруг точек. При сборке некоторых программ это может оказаться критичным
Пример 2¶
#define MACRO a >>> b
MACRO
$ gcc -E t.c
a >>> b
$ lcc -E t.c
a >> > c
В препроцессорной выдаче lcc появился лишний пробел между второй и третьей угловой скобкой. При сборке некоторых программ это может оказаться критичным
Причина отличия препроцессирования lcc от препроцессирования gcc и метод исправления проблемы¶
В современных версиях компиляторов препроцессор встроен в компилятор, и не является отдельной программой. У gcc и lcc номинально присутствует отдельная компонента cpp, но при работе cpp в реальности используется тот же самый препроцессор, который встроен в компилятор
Компилятор lcc построен на базе frontend’а от EDG. Разработчики EDG позиционируют свой препроцессор как “препроцессор с языков C и C++”. При этом препроцессор в gcc условно можно считать “универсальным препроцессором” (хотя в реальности это не совсем так). Препроцессор от EDG тоже умеет работать как “универсальный препроцессор”, но технически это является отдельным режимом работы. Таким образом, в lcc существуют два режима работы препроцессора: “режим препроцессора с языков C и C++” и “режим универсального препроцессора”. При этом одновременно может работать только один из двух режимов. Препроцессор в gcc всегда работает в гибридном режиме, при котором в некоторых случаях умеет на ходу подстраиваться под один из двух режимов работы
Выбор одного из двух режимов в lcc осуществляется на основании типа исходного файла:
если исходный файл имеет тип C или C++, то препроцессор работает в “режиме
препроцессора с языков C и C++”, иначе в “режиме универсального препроцессора”. Тип
файла определяется по стандартным правилам: если указана опциия -x
, то тип файла
определяется в соответствии с опцией -x
, иначе по расширению имени файла.
Исторически сложилось так, что в lcc для включения “режима универсального препроцессора”
требуется использовать тип файла “ассемблерный файл, требюующий предварительного
препроцессирования”. Это означает, что либо файл должен иметь расширение *.S, либо
в командной строке запуска компилятора где-то слева от имени файла нужно добавить опцию
-xassembler-with-cpp
В качестве примера опишем способ исправления проблемы, описанной выше по именем “Пример 1”
#define MACRO a.b.c
MACRO
$ gcc -E t.c
a.b.c
$ lcc -E t.c
a . b . c
$ lcc -xassembler-with-cpp -E t.c
a.b.c
$ cp t.c t.S
$ lcc -E t.S
a.b.c
Почему нельзя в lcc включить режим универсального препроцессора по умолчанию¶
Может показаться, что в lcc можно было бы включить “режим универсального препроцессора” по умолчанию и не возиться с выбором одного из двух режимов. Но так не получится, потому что не все коды, написанные на C или C++, корректно препроцессируются в “режиме универсального препроцессора”
#define MACRO -1
x=0-MACRO;
$ gcc -E t.c
x=0- -1;
$ lcc -E t.c
x=0- -1;
$ lcc -xassembler-with-cpp -E t.c
x=0--1;
Здесь имеем обратную ситуацию. Чтобы на выходе получить текст, который в будет корректен с точки зрения языков C и C++, требуется добавить лишний пробел. При использовании в lcc “режима универсального препроцессора” этого не происходит и препроцессированный текст получается некорректным с точки зрения языков C и C++