Добро пожаловать на Pro Pawn - Портал о PAWN-скриптинге.
Показано с 1 по 5 из 5
  1. #1
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±

    Использование static enum

    Внимание: Данная статья предназначена для "продвинутых" пользователей.
    Статья содержит много теории и изобилует костылями, а потому не рекомендуется к прочтению обычным скриптерам.
    Я вас предупредил.


    Как некоторые из вас могли заметить, год назад в компиляторе от Zeex'а версии 3.10.2 появилась возможность объявлять статические перечисления (#136, #141).
    1. static enum eMyInfo
    2. {
    3. miField1,
    4. miField2
    5. };

    Отличие статических перечислений от обычных в том, что их область видимости ограничена только файлом, в котором они объявлены (как переменными или функциями, тоже объявленными с классификатором static). Такие перечисления идеально подходят для написания инклудов, когда нужно объявить перечисление, предназначенное для использования только внутри инклуда.
    Но что, если при использовании этой фичи нужно сохранить совместимость со старыми версиями компилятора, в которых фичи static enum нет?
    Следует сначала убедиться, что компилятор поддерживает эту фичу, если нет - использовать просто enum, без static.

    Для начала разберёмся, как различить разные версии компилятора. Допустим, у нас компилятор версии "3.10.2". Мажорная и минорная составляющие "3" и "10" будут закодированы в константе __Pawn (0x3A), номер билда "2" - в __PawnBuild.
    Константа __PawnBuild появилась всего год назад в версии 3.10.1, до этого номер билда никак не обозначался, во всех версиях в __Pawn было записано значение 0x3A и не было возможности различать разные билды (собственно, для решения этой проблемы и добавили константу __PawnBuild).
    При использовании __PawnBuild нужно сначала убедиться в том, что константа объявлена (это делается с помощью оператора defined), иначе компилятор выдаст ошибку "undefined symbol".

    Попробуем объявить макрос static_enum, который будет раскрываться в "static enum" для версий, на которых поддерживаются статические перечисления, и в "enum" для остальных версий.
    Вы наверняка подумаете, что это очень просто:
    1. #if defined __PawnBuild && __PawnBuild >= 2
    2. #define static_enum static enum
    3. #else
    4. // Если более старая версия компилятора - откатываемся до обычного enum.
    5. #define static_enum enum
    6. #endif

    Но не тут-то было! При попытке собрать это стандартным компилятором от SA:MP Team получаем:
    error 017: undefined symbol "__PawnBuild"
    Ошибка козникает на строке
    1. #if defined __PawnBuild && __PawnBuild >= 2

    Поскольку оператор defined не "выплёвывает" ошибку, а возвращает 0, если его операнд не объявлен, ошибка явно выдаётся на второй части выражения, после знака "&&".
    Но как такое возможно? Мы же с помощью defined проверили, объявлена ли константа __PawnBuild - если не объявлена, то подвыражение после "&&" не должно выполняться, ведь уже заранее известно, что результатом всего выражения будет ложь (0).
    ... или должно?

    Оказывается, в директивах компилятор обрабатывает выражения "ленивым" методом, т.е. даже когда одно из подвыражений делает заведомо ложным всё выражение (как в случае выше с __PawnBuild), он всё равно вычисляет оставшиеся подвыражения.

    Тогда как же поступить с объявлением макроса static_enum? Легко, используем две директивы #if: в одной проверим существование константы __PawnBuild, а в другой - её значение.
    1. #if defined __PawnBuild
    2. #if __PawnBuild >= 2 // Компилятор не станет обрабатывать эту строку, если константа
    3. // __PawnBuild не объявлена, следовательно, не будет и ошибки.
    4. #define static_enum static enum
    5. #endif
    6. #endif
    7. #if !defined static_enum
    8. // Если макрос static_enum не был объявлен выше - мы имеем дело
    9. // со старой версией компилятора; откатываемся до обычного enum.
    10. #define static_enum enum
    11. #endif

    Остаётся только один изъян: мажорная и минорная версии компилятора не проверяются, учитывается только номер билда.
    Если в будущем версия компилятора увеличится (например, константа __Pawn будет равна не 0x3A, а 0x3B, что будет соответствовать версии "3.11" вместо нынешней "3.10"), макрос static_enum на будущих версиях 3.11.0 и 3.11.1 будет раскрываться в "enum", как на старых версиях.
    Учтём эту возможность:
    1. #if __Pawn >= 0x3A && defined __PawnBuild
    2. #if __Pawn > 0x3A || __PawnBuild >= 2
    3. #define static_enum static enum
    4. #endif
    5. #endif
    6. #if !defined static_enum
    7. #define static_enum enum
    8. #endif

    И после этого можно смело использовать static_enum в своих инклудах:
    1. static_enum my_inc_ExampleInfo // префикс "my_inc_" лучше оставить на случай, если используется
    2. { // старый компилятор, чтобы избежать коллизии имён
    3. eiField1,
    4. eiField2
    5. };


     Бонус
    Небольшой бонус для тех, кто дочитал статью: с помощью всё тех же статических перечислений можно решить ещё одну проблему.
    Если использовать static const для ограничении зоны видимости константы внутри инклуда, после компиляции константа будет вести себя, как переменная: при каждом обращении к ней будет тратиться лишнее время на то, чтобы считать её значение из секции данных.
    Но со static enum тоже можно объявлять константы, которые технически будут именно константами, подставленными на места обращения во время компиляции, а не переменными со статусом read-only. Пусть оператор enum и не предназначен для объявления одиночных констант, но это всяко лучше, чем отсутствие альтернативы.
    1. #if __Pawn >= 0x3A && defined __PawnBuild
    2. #if __Pawn > 0x3A || __PawnBuild >= 2
    3. #define static_const(%0) static enum { %0 }
    4. #endif
    5. #endif
    6. #if !defined static_const
    7. #define static_const(%0) static const %0
    8. #endif

    Пример использования:
    1. static_const(MY_CONST = 0);




    Автор статьи: Daniel_Cortez
    Благодарности:
    • VVWVV - реализация static enum.


    Специально для Pro-Pawn.ru
    Копирование данной статьи на других ресурсах без разрешения автора запрещено!
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

  2. 6 пользователя(ей) сказали cпасибо:
    #Djuga (28.04.2018) Kurbanoff (15.04.2018) Seviel (15.04.2018) SooBad (21.04.2018) whale (16.04.2018) ziggi (15.04.2018)
  3. #2
    Аватар для whale
    Пользователь

    Статус
    Оффлайн
    Регистрация
    10.04.2014
    Сообщений
    74
    Репутация:
    17 ±
    Отличная статья, очень доступно преподнёс материал :)

    P.S Не постесняюсь спросить: случайно не после проблем с моего релиза фикса пикапов решил написать статью? Просто там была проблема, описанная тобою по поводу совместимости.
    Последний раз редактировалось whale; 16.04.2018 в 00:44.

  4. #3
    Аватар для VVWVV
    ?

    Статус
    Оффлайн
    Регистрация
    09.07.2015
    Сообщений
    731
    Репутация:
    353 ±
    Вообще рациональным решением для создания статической переменной, я думаю, является оптимизация static const.

  5. #4
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±
    Цитата Сообщение от Twixyck Посмотреть сообщение
    P.S Не постесняюсь спросить: случайно не после проблем с моего релиза фикса пикапов решил написать статью? Просто там была проблема, описанная тобою по поводу совместимости.
    Отчасти да, но идея статьи и примеры кода давно уже были на уме (ту же идею про константы уже приходилось высказывать на GitHub) - было бы пустой тратой идей не поделиться ими на форуме.

    Цитата Сообщение от VVWVV Посмотреть сообщение
    Вообще рациональным решением для создания статической переменной, я думаю, является оптимизация static const.
    Мало того, что такое изменение нарушит работу кода, в котором значения из "константных переменных" считываются с помощью #emit, так ещё и реализация изменения будет неоправданно сложной (см. обсуждение по ссылке выше). Точно не в этой жизни.
    P.S.: Про твою последнюю статью не забыл.
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

  6. #5
    Аватар для VVWVV
    ?

    Статус
    Оффлайн
    Регистрация
    09.07.2015
    Сообщений
    731
    Репутация:
    353 ±
    Цитата Сообщение от Daniel_Cortez Посмотреть сообщение
    Мало того, что такое изменение нарушит работу кода, в котором значения из "константных переменных" считываются с помощью #emit, так ещё и реализация изменения будет неоправданно сложной (см. обсуждение по ссылке выше). Точно не в этой жизни.
    P.S.: Про твою последнюю статью не забыл.
    Я подозреваю, что снова YSI палки в колеса вставляет. Я бы не сказал, что она будет сложной. Насколько я помню, там нужно будет добавить пару проверок в функции, которые используются для обычных переменных. Кстати, что за статья?

 

 

Информация о теме

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

Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •