PDA

Просмотр полной версии : [Урок] Препроцессор



VVWVV
13.12.2016, 21:47
Введение


Препроцессор, как утверждает Википедия – это компьютерная программа, принимающая данные на входе и выдающая данные, предназначенные для входа другой программы (например, компилятора). Чаще всего он применяется для автоматизации какого-либо процесса в программировании, например, подсчёта переменных и т.п. В Pawn препроцессор используется непосредственно для формирования какого-то структурированного кода, анализа чего-либо и т.д.


Определения и их эквиваленты


Определения (в данном случае имеется в виду object-like macro) – это константы, которые заменяются значениями в момент прекомпиляции. Они имеют глобальную зону видимости.


#define SOME_CONSTANT (0xFFFFFFFF)


Эквивалент определений – оператор const. Есть два основных отличия: оператор может иметь локальную зону видимости; заменяется на этапе компиляции.

(!) Следует отметить, что, как и у определения, так и у оператора const имеется ограничение на количество символов в названии – 31 символ.


Макросы


Макросы (в данном случая имеется в виду function-like macro, т.е. функция-подобный макрос) – важнейшая часть препроцессора, которая может обрабатывать полученные пользовательские данные и распоряжаться ими. В программировании макросы используются довольно часто, это обусловлено тем, что в программировании очень много одинаковых операций.

Синтаксис


Синтаксис макроса стоит из нескольких частей:

#define <название и его содержимое><конец макроса> <значение для переопределения>

Примечание:

От начала названия и до конца макроса не должно быть ни единого пробела, иначе будет ошибка. Однако, чтобы парсить пробелы необходимо использовать ascii-идентификаторы символов. Например, ascii-код для пробела – '\32;'
Первый символа макроса должен состоять из букв латинского алфавита или знаков ‘_’, ‘@’. Последующие же символы могут использовать и цифры.
В третьей же части использовать пробелы можно.



Параметры


Параметры являются неотъемлемой частью макрофункций, ведь с помощью них передаётся вся пользовательская информация.
Параметры в макрофункциях задаются с помощью процента и номера аргумента.

#define macrofunc(%0,%1) %0 %1

Опасные моменты использования параметров


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

Перейдём к рассмотрению примера:


#define macrofunc(%0) (8 * %0)

main()
{
printf("%d", macrofunc(5 + 5));
}

В выводе ожидаем 80, а получаем 45. Дело в том, что макрос просто подставляет код туда, куда указанно. Он не определяет последовательность операций и т.п.
Для того чтобы избежать данную проблему необходимо использовать круглые скобки, которые говорят о том, что начальной операцией является выражение в скобках.


#define macrofunc(%0) (8 * (%0))

main()
{
printf("%d", macrofunc(5 + 5));
}


Теперь все правильно. Итак, давайте выведем несколько правил, которые помогут избежать выше указанную проблему:

Скобки вокруг всего выражения;
Скобки вокруг каждого параметра;

Однако, все выше перечисленные правила не помогут в случае, если вы используете инкрементацию/декрементацию в параметре.

Рассмотрим пример:


#define macrofunc(%0) ((%0) * (%0))

main()
{
new x;
printf("%d", macrofunc(++x));
}

В данном случаем стоит, вообще, отказаться от макросов в пользу функций.



Переход на новую строку


Для читаемости макрофункций используется обратный слеш, которые позволяет определить переход к новой строчке. Кроме того, это работает практически для каждой препроцессорной директивы.

Перейдём к примеру:


#define SOME_MACRO(%0) \
forward SOME_%0(); \
public SOME_%0();


Также мы можем сделать и при обращении к макрофункциям:


#define SOME_MACRO_FUNC(%0,%1) \
(%0 + %1)

main()
{
SOME_MACRO_FUNC(0, \
8 \
);
}




Другие препроцессорные директивы


#include и #tryinclude


Данные директивы вставляют код из указанного файла в исходный. Т.е. подключив какой-либо файл, мы можем пользоваться его содержимым: функциями, глобальными переменными и т.п.

Главным отличием #include и #tryinclude является то, что последний не выводит ошибок, если файл не был загружен.


#assert


Проверяет указанное выражение. Если выражение возвращает нуль, то выдаёт ошибку.


#if, #endif, #elseif, #else


Начнём с того, что у каждой директивы #if должна быть соответствующая директива #endif. Кроме того, между данными препроцессорными директивами может располагаться любое количество директив #elseif. Однако, допускается не более одной директивы #else.


#define SOME_CONSTANT (0xFFFFFFFF)

#if SOME_CONSTANT == 0
#define SOME_CONSTANT_1 0
#elseif SOME_CONSTANT > 0
#define SOME_CONSTANT_1 1
#elseif SOME_CONSTANT < 0
#define SOME_CONSTANT_1 2
#else
#error Opss
#endif



#endinput


Данная препроцессорная директива указывает на то, что компилятор не должен читать последующий код, т.е. игнорировать его.


#if defined _PRO_PAWN_RU
#endinput
#endif

#define _PRO_PAWN_RU



#error


Директива выдаёт ошибку с указанным текстом при компиляции.


#error Some text



#undef


Удаляет макрофункцию или константу. Синтаксис данной директивы следующий:


#undef <имя>




Строки


В препроцессоре существует оператор “#” помещающий значение в двойные кавычки. Например, рассмотрим код:


#define TO_STR(%0) (#%0)

main()
{
printf("%s", TO_STR(pro-pawn.ru));
}

Данный код выведет "pro-pawn.ru" в консоль.


Вывод


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



Если у вас появились какие-то вопросы, или вы думаете, что я что-то забыл, напишите об этом.



Автор: VVWVV
Исключительно для pro-pawn.ru


Копирование данной статьи на других ресурсах без разрешения автора запрещено!

DCPSHER
16.06.2018, 16:43
Интересует вот такой момент:
Можно ли как-то посмотреть результаты работы Препроцессора?
Т.е. промежуточное состояние между окончанием его работы и началом компиляции. Чтобы хоть как-то иметь возможность отдебажить макросы.
Не представляю, как без нечто подобного Y_Less или Slice делали кучи макросов.

VVWVV
16.06.2018, 17:34
Интересует вот такой момент:
Можно ли как-то посмотреть результаты работы Препроцессора?
Т.е. промежуточное состояние между окончанием его работы и началом компиляции. Чтобы хоть как-то иметь возможность отдебажить макросы.
Не представляю, как без нечто подобного Y_Less или Slice делали кучи макросов.

Используйте флаг -l при компиляции, чтобы создать файл с результатом работы препроцессора.

DCPSHER
17.06.2018, 16:36
Используйте флаг -l при компиляции, чтобы создать файл с результатом работы препроцессора.

Огромное спасибо. Очень помогло мне! Никак не мог найти в гугле.