# Особенности работы препроцессирования 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++