AMXX Pawn | Директивы препроцессора

Сообщения
336
Реакции
414
Помог
7 раз(а)
Автор: Albertio
Первоисточники: Репозиторий Pawn на GitHub | Репозиторий AMX Mod X на GitHub | Директивы препроцессора | AMX ASM & Оператор #emit | Директива препроцессора - #pragma
Распространение данной статьи запрещено без указания ссылки на первоисточник.
Примечание: Все директивы должны быть первыми в строке и начинаться с символа "#", а полная инструкция не может занимать более одной строки - за исключением #define.

#assert постоянное выражение

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

Код:
#define TEST 5
#assert TEST >= 3
// Успешная компиляция

#define TEST 1
#assert TEST >= 3
// fatal error 110: assertion failed: 1 >= 3

#define TEST
#if defined TEST
    #assert "Variable TEST is defined"
#endif
// fatal error 110: assertion failed: "Variable TEST is defined"

#define TEST
#if defined TEST
    #assert Variable Test is defined
#endif
// fatal error 110: assertion failed: Variable Test is defined
#define замена шаблона
Определяет макрос подстановки текста. Шаблон сопоставляется со всеми строками, считанными из исходных файлов; совпавшие участки заменяются текстами замены. Шаблон и тексты замены могут содержать параметры, обозначаемые от "%0" до "%9".

Код:
#define TEST
// Переменная TEST объявлена

#define TEST 5
// Переменная TEST равна 5

#define TEST "Testing"
// В переменной TEST хранится строка "Testing"

#define TEST(%0,%1,%2,%3) \
    %0 + %1 * %2 / %3
// Макрос
#emit опкод операнд
Директива #emit служит в качестве поточного ассемблера. По сути, это встроенный компилятор, если вы знаете AMX, вы можете использовать его для вставки опкодов AMX непосредственно в ваш код. Единственное ограничение - он допускает только один аргумент. Синтаксис: #emit <опкод> <операнд> может быть рациональным числом, целым числом или(локальным или глобальным) символом(переменные, функции и метки).

Код:
new g_iInt1 = 1, g_iInt2 = 3;

public function()
{
    new iInt1, iInt2;
    #emit LOAD.pri g_iInt1 // Загрузка содержимого переменной "g_iInt1" в основной(pri) регистр.
    #emit LOAD.alt g_iInt2 // Загрузка содержимого переменной "g_iInt2" в резервный/альтернативный(alt) регистр.

    #emit XCHG // Замена местами значений оснвоного и резервного/альтернативного регистра.
    // Теперь .pri-регистр хранит значение переменной "g_iInt2", а .alt-регистр - значение переменной "g_iInt1".

    #emit STOR.S.pri iInt1 // Значение .pri-регистра теперь хранится в переменной iInt1.
    // .S теперь используется в операциях с локальными переменными(которые заносятся в стек).

    #emit LOAD.S.pri iInt1 // Загрузка содержимого переменной "iInt1" в основной(pri) регистр.
    #emit LOAD.S.alt iInt2 // Загрузка содержимого переменной "iInt2" в резервный/альтернативный(alt) регистр.

    // Обратите внимание, что .S должен использоваться во время загрузки содержимого локальных переменных.

    #emit ADD // PRI = PRI + ALT = iInt1 + iInt2

    #emit STOR.pri g_iInt1 // Значение .pri-регистра теперь хранится в переменной g_iInt1.
}
Основные инструкции:
Код:
#        мнемоника        операнд        семантика
1        LOAD.pri        address        PRI = [address]
2        LOAD.alt        address        ALT = [address]
3        LOAD.S.pri        offset        PRI = [FRM + offset]
4        LOAD.S.alt        offset        ALT = [FRM + offset]
5        LREF.pri        address        PRI = [[ address ]]
6        LREF.alt        address        ALT = [[ address ]]
7        LREF.S.pri        offset        PRI = [[FRM + offset]]
8        LREF.S.alt        offset        ALT = [[FRM + offset]]
9        LOAD.I                        PRI = [PRI]
11        CONST.pri        value        PRI = value
12        CONST.alt        value        ALT = value
13        ADDR.pri        offset        PRI = FRM + offset
14        ADDR.alt        offset        ALT = FRM + offset
15        STOR            address        [address] = PRI
16        STOR.S            offset        [FRM + offset] = PRI
19        SREF.pri        address        [[ address ]] = PRI
20        SREF.alt        address        [[address]] = ALT[\td]
21        SREF.S.pri        offset        [[FRM + offset]] = PRI
22        SREF.S.alt        offset        [[FRM + offset]] = ALT
23        STOR.I                        [ALT] = PRI (full cell)
25        LIDX                        PRI = [ ALT + (PRI ? cell size) ] (load what is there at calculated index)
27        IDXADDR                        PRI = ALT + (PRI ? cell size) (calculate indexed address)
39        PUSH.C            value        [STK] = value,STK = STK - cell size
31        LCTRL            index        PRI is set to the current value of any of the special registers. The index parameter must be: 0=COD, 1=DAT, 2=HEA,3=STP, 4=STK, 5=FRM, 6=CIP (of the next instruction)
32        SCTRL            index        Set the indexed special registers to the value in PRI.The index parameter must be: 2=HEA, 4=STK, 5=FRM, 6=CIP
33        MOVE.pri                    PRI = ALT
34        MOVE.alt                    ALT = PRI
35        XCHG                        Exchange contents of PRI and ALT
36        PUSH.pri                    [STK] = PRI and STK = STK - cell size
37        PUSH.alt                    [STK] = ALT and STK = STK - cell size
40        PUSH            address        [STK] = [address], STK = STK - cell size
41        PUSH.S            offset        [STK] = [FRM + offset], STK = STK - cell size
42        POP.pri                        PRI = [STK] and STK = STK + cell size
43        POP.alt                        ALT = [STK] and STK = STK + cell size
44        STACK            value        ALT = STK and STK = STK + value
47        RET                            STK = STK + cell size, FRM = [STK],STK = STK + cell size,CIP = [STK], The RET instruction cleans up the stack frame and returns from the function to the instruction after the call
48        RETN                        STK = STK + cell size, FRM = [STK],STK = STK + cell size, CIP = [STK], STK = STK + [STK]The RETN instruction removes a specifed number of bytes from the stack. The value to adjust STK with must be pushed prior to the call.
65        SHL                            PRI = PRI << ALT
66        SHR                            PRI = PRI >> ALT (without sign extension)
67        SSHR                        PRI = PRI >> ALT (with sign extension)
68        SHL.C.pri        value        PRI = PRI << value
69        SHL.C.alt        value        ALT = ALT << value
72        SMUL                        PRI = ALT * PRI (signed multiply)
73        SDIV                        PRI = ALT / PRI (signed divide),ALT = ALT mod PRI
78        ADD                            PRI = ALT + PRI
79        SUB                            PRI = ALT - PRI
81        AND                            PRI = ALT & PRI
82        OR                            PRI = ALT | PRI
83        XOR                            PRI = ALT?PRI
84        NOT                            PRI = !PRI
85        NEG                            PRI = -PRI
86        INVERT                        PRI = ~PRI
87        ADD.C            value        PRI = PRI + value
88        SMUL.C            value        PRI = PRI * value
107        INC.pri                        PRI = PRI + 1
108        INC.alt                        ALT = ALT + 1
109        INC                            [address] = [address] + 1
110        INC.S            offset        [FRM + offset] = [FRM + offset] + 1
112        DEC.pri                        PRI = PRI - 1
113        DEC.alt                        ALT = ALT - 1
116        DEC.I                        [PRI] = [PRI] - 1
119        FILL            number        Fill memory at [ALT] with value in [PRI]. The parameter specifes the number of bytes, which must be a multiple of the cell size.
123        SYSREQ.C        address        Used to call a system service or native functions
133        PUSH.ADR        offset        [STK] = FRM + ofset,STK = STK - cell size
#endinput/#endscript
Закрывает текущий файл и тем самым игнорирует весь текст, расположенный ниже директивы #endinput. Директива #endscript является синонимом #endinput.

Код:
#include <amxmodx>

public plugin_precache()
{
    log_amx("Test 1");
}

#endinput

public plugin_init()
{
    register_plugin("Test Directives", "0.0.1", "Albertio");

    log_amx("Test 2");
}

// Весь код, который находится ниже #endinput - не сработает. #endinput может быть заменён #endscript.
#error сообщение
Сигнализирует об "ошибке пользователя" с указанным сообщением. Ошибки пользователя являются фатальными ошибками, и они служат той же цели, что и директива #assert.

Код:
#define TEST
#if defined TEST
    #error "Variable TEST is defined"
#endif
// fatal error 111: user error: "Variable TEST is defined"

#define TEST
#if defined TEST
    #error Variable TEST is defined
#endif
// fatal error 111: user error: Variable TEST is defined
#if/#elseif постоянное выражение, #else, #endif

Части программы могут быть разобраны или проигнорированы в зависимости от определенных условий. PAWN парсер(компилятор или интерпретатор) генерирует код только для тех частей, для которых условие истинно.
За директивой #if должно следовать постоянное выражение. Чтобы проверить, определена ли переменная или константа, используйте оператор defined.
После начальной директивы #if может следовать ноль или более директив #elseif. Эти блоки пропускаются, если любой из предшествующих блоков #if или #elseif был разобран(т.е. не пропущен). Как и в случае с директивой #if, за выражением #elseif должно следовать постоянное выражение.
Директива #else заставляет парсер пропустить все строки до #endif, если предыдущая #if или любая из предыдущих директив #elseif были "истинными", и парсить эти строки, если все предыдущие блоки были пропущены. Директива #else может быть опущена; если она присутствует, то с каждым #if может быть связано только одно #else.
Директива #endif завершает часть программы, которая разбирается условно. Условные директивы могут быть вложенными, и каждая директива #if должна завершаться директивой #endif.

Код:
#define TEST 1
#if TEST == 1
    new g_iTest;
#endif
// Объявится переменная g_iTest

#define TEST 2
#if TEST == 1
    new g_iTest1;
#elseif TEST == 2
    new g_iTest2;
#endif
// Объявится переменная g_iTest2

#define TEST 3
#if TEST == 1
    new g_iTest1;
#elseif TEST == 2
    new g_iTest2;
#else
    new g_iTest3;
#endif
// Объявится переменная g_iTest3
#include название файла или <название файла>

Вставляет содержимое указанного файла в текущую позицию внутри текущего файла. Имя файла в угловых скобках ("<" и ">") относится к системному файлу; PAWN парсер(компилятор или интерпретатор) будет искать такие файлы только в заданном списке каталогов, но не в "текущем" каталоге. Имена файлов без кавычек или в двойных кавычках - это обычные включаемые файлы, которые PAWN парсер будет искать сначала в текущем каталоге.
PAWN парсер сначала пытается открыть файл с указанным именем. Если это не удается, он пытается добавить к имени файла расширения ".inc", ".p" и ".pawn"(в таком порядке). Предлагаемое по умолчанию расширение включаемых файлов - ".inc".
Если файл может быть успешно открыт, директива #include определяет константу с именем "inc" плюс базовое имя файла(имя файла без пути и расширения) и значением 1. Если константа уже существует, директива #include пропускает открытие и включение файла, тем самым предотвращая двойное включение. Для принудительного двойного включения удалите определение константы с помощью директивы #undef перед вторым включением файла.

Код:
#include <amxmodx>

#include <amxmodx.inc>

#include amxmodx

#include amxmodx.inc

#include "amxmodx"

#include "amxmodx.inc"
#pragma дополнительная информация

“pragma” - это крючок для парсера для указания дополнительных настроек, таких как уровни предупреждений или дополнительные возможности.

#pragma amxlimit значение

Устанавливает максимальный размер в байтах, до которого может вырасти скомпилированный скрипт. Эта прагма полезна для(встроенных) сред, где максимальный размер скрипта ограничен жестким верхним пределом. Если объем оперативной памяти для данных и стека не задан(см. #pragma amxram), это значение относится к общим требованиям к памяти; если объем оперативной памяти задан явно, это значение дает только объем памяти, необходимый для кода и статических данных.

Код:
#pragma amxlimit 1000
// Если конеченый размер файла будет больше, чем 1000 КБ, то выдаст ошибку, но скомпилируется
#pragma codepage название/значение

PAWN парсер может переводить символы в символьных константах и в распакованных строках в "широкие" символы Unicode/UCS-4. Эта #pragma указывает кодовую страницу, которая должна быть использована для перевода.

Код:
#pragma codepage 65001
// Выставит кодировку utf-8
Идентификаторы кодовой страницы
#pragma compress значение

PAWN парсер может записывать сгенерированный P-код в компактной или обычной("некомпактной") кодировке. Значение по умолчанию зависит от конфигурации парсера(и, возможно, от настроек пользователя). Эта #pragma позволяет автору сценария отменить значение по умолчанию и принудительно использовать компактную кодировку(когда значение ненулевое) или принудительно использовать обычную кодировку(когда значение нулевое). Особенно полезно отключение компактного кодирования(принудительное использование обычного кодирования), поскольку PAWN парсер может оказаться не в состоянии скомпилировать определенный скрипт в режиме "компактного кодирования".

Код:
#pragma compress 1
// Включение компактного кодирования
#pragma ctrlchar символ

Определяет символ, который будет использоваться для обозначения начала "управляющей последовательности". По умолчанию управляющим символом является "\".
Пример: #pragma ctrlchar '$'
Вы можете задать новое значение управляющего символа в виде символьной константы(между одинарными кавычками) или в виде десятичного или шестнадцатеричного значения. Если вы опустите значение нового управляющего символа, парсер вернется к управляющему символу по умолчанию.

Код:
#include <amxmodx>

#pragma ctrlchar '$'
// Изменяем стандартный escape-символ "^" на "$".

public plugin_init()
{
    register_plugin("Test Directives", "0.0.1", "Albertio");

    log_amx("TEST$tTEST");
    // Выведет в лог "TEST TEST"
}
#pragma deprecated значение

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

Код:
#include <amxmodx>

#define TEST "Testing"
#pragma deprecated TEST

public plugin_init()
{
    register_plugin("Test Directives", "0.0.1", "Albertio");

    log_amx("%s", TEST);
    // warning 233: symbol "plugin_init" is marked as deprecated: TEST
}
#pragma dynamic значение

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

Код:
#include <amxmodx>

#pragma dynamic 16404

public plugin_init()
{
    register_plugin("Test Directives", "0.0.1", "Albertio");

    new szString[16384];
    // Без использования "#pragma dynamic 16404" в консоли будет ошибка [AMXX] Run time error 3: stack error
}
#pragma library название

Задает имя(динамически связанного) модуля расширения, в котором реализованы собственные функции. Эта #pragma должна появляться над объявлениями нативных функций, которые являются частью модуля расширения.
Параметр "название" может отсутствовать, в этом случае последующие объявления нативных функций не связаны ни с каким модулем расширения.
Область действия этой #pragma - от строки, в которой она появляется, до конца файла, в котором она появляется. При типичном использовании библиотечная строка #pragma появляется в верхней части включаемого файла, в котором объявляются собственные функции для модуля расширения, а область применения библиотечной "ссылки" заканчивается в конце этого включаемого файла.

Код:
// Уставший вариант, рекомендуется использовать #pragma reqlib
#pragma reqlib <название файла>

Требует загрузки данной библиотеки.

test.inc
Код:
#pragma reqlib test
#if !defined AMXMODX_NOAUTOLOAD
    #pragma loadlib test
#endif
test.sma
Код:
#include <amxmodx>

public plugin_natives()
{
    register_library("test");
}

public plugin_init()
{
    register_plugin("Test Directives", "0.0.1", "Albertio");
}
#pragma reqclass класс

Требует, чтобы данный библиотечный класс был загружен.

Код:
#include <amxmodx>

#pragma reqclass test1
#if !defined AMXMODX_NOAUTOLOAD
    #pragma defclasslib test1 test2
#endif

public plugin_init()
{
    register_plugin("Test Directives", "0.0.1", "Albertio");
}
#pragma loadlib <название файла>

Автоматически пытается загрузить заданную библиотеку.

test.inc
Код:
#pragma reqlib test
#if !defined AMXMODX_NOAUTOLOAD
    #pragma loadlib test
#endif
test.sma
Код:
#include <amxmodx>

public plugin_natives()
{
    register_library("test");
}

public plugin_init()
{
    register_plugin("Test Directives", "0.0.1", "Albertio");
}
#pragma explib <название файла> <название файла>

Если первая библиотека не загружена, будет сделана попытка загрузить вторую(не очень полезно).

Код:
#include <amxmodx>

#pragma explib <test1> <test2>

public plugin_init()
{
    register_plugin("Test Directives", "0.0.1", "Albertio");
}
#pragma expclass класс <название файла>

Если ожидаемый класс не найден, будет сделана попытка загрузить заданную библиотеку. Это полезно для определения модуля по умолчанию, который будет загружаться с заданным классом модулей.

Код:
#include <amxmodx>

#pragma reqclass test1 <test2>

public plugin_init()
{
    register_plugin("Test Directives", "0.0.1", "Albertio");
}
#pragma defclasslib класс <название файла>

Аналогично #expclass, однако #defclasslib ждет, пока все ожидания не будут разрешены. Это позволяет подключаемым модулям отменять значения по умолчанию, добавляя свои собственные ожидания.

Код:
#include <amxmodx>

#pragma reqclass test1
#if !defined AMXMODX_NOAUTOLOAD
    #pragma defclasslib test1 test2
#endif

public plugin_init()
{
    register_plugin("Test Directives", "0.0.1", "Albertio");
}
#pragma pack значение

Если значение равно нулю, упакованные литеральные строки начинаются с "!"" (восклицательный знак + двойная кавычка), а распакованные литеральные строки - только с двойной кавычки """. Если значение ненулевое, синтаксис упакованных и неупакованных литеральных строк меняется местами: литеральные строки, начинающиеся с двойной кавычки, упаковываются, а литеральные строки, начинающиеся с "!"", распаковываются.

Код:
#pragma pack 1
// Если заданное значение равно нулю, упакованные строки объявляются знаком "!",
// а не запакованные двойными кавычками (""), без знака. Если значение равно единице,
// то литеральность строк объявляется в обратном порядке.
#pragma rational tagname(значение)

Включает поддержку рациональных чисел. tagname - это имя метки, которую будут иметь рациональные числа; обычно выбирают имена "Float:" или "Fixed:". Наличие значения в круглых скобках после tagname необязательно: если оно опущено, рациональное число хранится как значение с "плавающей точкой" в соответствии с нормой IEEE 754; если оно присутствует, рациональное число - это число фиксированной точности ("масштабированное целое число") с указанным количеством десятичных знаков.

Код:
#pragma rational Float /* формат плавающей точки */
#pragma rational Fixed(3) /* фиксированная точка, с 3 десятичными знаками */
// В float.inc уже заранее использовано "#pragma rational Float"
#pragma semicolon значение

Если значение равно нулю, то точка с запятой не требуется для завершения утверждения, если это утверждение является последним в строке. Точки с запятой по-прежнему необходимы для разделения нескольких утверждений на одной строке.
Когда точка с запятой необязательна(по умолчанию), постфиксный оператор(один из "++" и "--") не может быть первой лексемой в строке, поскольку они будут интерпретироваться как префиксные операторы.

Код:
#pragma semicolon 1
// Теперь компилятор будет ждать, что в конце каждого выражения должен стоять символ ";"
#pragma tabsize значение

Количество символов между двумя последовательными позициями TAB. Значение по умолчанию равно 8. Вам может понадобиться установить размер TAB, чтобы избежать предупреждения 217(свободный отступ), если в исходном коде отступы чередуются с пробелами и символами TAB. В качестве альтернативы, установив #pragma tabsize в ноль, парсер больше не будет выдавать предупреждение 217.

Код:
#pragma tabsize 0
// Убирает табуляцию

#pragma tabsize 2
// Выставляет табуляцию в 2 отступа
#pragma unused символ, ...

Помечает именованный символ как "используемый". Обычно PAWN парсер предупреждает о неиспользуемых переменных и неиспользуемых локальных константах. В большинстве ситуаций эти переменные и константы избыточны, и их лучше удалить ради ясности кода. Однако, особенно в случае локальных констант, может быть лучше(или требуется) сохранить определения констант. Эта #pragma позволяет пометить символ(переменную или константу) как "используемый" и избежать предупреждения парсера.
#pragma должна появляться после объявления символа - но не обязательно сразу после объявления.
В одной #pragma может быть несколько имен символов, разделенных запятыми.

Код:
public function()
{
    static const TEST[] = "STEAM_0:0:000000000";
    new iLen = sizeof(TEST);
    #pragma unused TEST
    // Компилятор не выдаст ошибку 203, что переменная TEST не используется.
}
#tryinclude название файла или <название файла>

Эта директива ведет себя так же, как директива #include, но она не выдает ошибку, если файл для включения не существует.

Код:
#tryinclude <amxmodx>

#tryinclude <amxmodx.inc>

#tryinclude amxmodx

#tryinclude amxmodx.inc

#tryinclude "amxmodx"

#tryinclude "amxmodx.inc"
// Если не будет инклюда "amxmodx", то ошибку не напишет при компиляции.
#undef название

Удаляет макрос текстовой подстановки или числовую константу, объявленную с помощью const. Параметр "название" должен быть "префиксом" макроса - буквенно-цифровой частью макроса.

Код:
public function()
{
    #define TEST "Testing"
    log_amx("%s", TEST);
    // Объявляем переменную TEST. Можем вывести в лог.

    #undef TEST
    log_amx("%s", TEST);
    // Удаляем переменную TEST. На этапе компиляции будет ошибка, т.к. пытаемся
    // использовать несуществующую переменную, но если не использовать, то всё будет ок.
}
 
Последнее редактирование:

Пользователи, просматривающие эту тему

Сейчас на форуме нет ни одного пользователя.
Сверху Снизу