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

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

    Лайфхак с функциями для Pawn AMX

    Привет всем.

    Не так давно в одном из своих личных проектов мне понадобилось сделать нативные функции для Pawn AMX.
    Для примера возьмём одну из таких функций: она должна была принимать 2 аргумента (ID участка памяти, в котором нужно отсортировать значения, как в массиве, и порядок сортировки) и нужно было сделать проверку на то, что этих аргументов передано нужное количество (кто знает, может быть в скрипте неправильный заголовок функции с 1 или даже 0 параметрами вместо 2?)
    Выглядел код примерно так:
    PHP код:
    static cell AMX_NATIVE_CALL n_MemSort(AMX *amx, const cell *params)
    {
        
    // CheckArgs проверяет кол-во аргументов и, если их меньше, чем нужно, поднимает ошибку.
        
    if (!CheckArgs(2))
            return 
    0;
        
    // Если есть нужное кол-во аргументов - отсортировать значения в участке памяти.
        
    return MemoryPool_Sort(amxparams[1], params[2]);

    Меня этот результат не устроил сразу по нескольким причинам.
    Во-первых, чтобы понять, что кроется в 1-м и 2-м аргументе, нужно открывать заголовочный файл на Pawn (*.inc).
    Во-вторых, чем больше таких аргументов в функции, тем легче их перепутать.
    В-третьих, последний параметр в CheckArgsNumber нужно подсчитывать вручную. ИЧСХ, с ним тоже легко ошибиться (например, указать 3 вместо 2), особенно из-за copy-paste.

    Мне захотелось переложить работу по контролю аргументов функции на компилятор, поэтому я придумал небольшой трюк с перечислением ID аргументов в enum:
    PHP код:
    static cell AMX_NATIVE_CALL n_MemSort(AMX *amx, const cell *params)
    {
        
    enum
        
    {
            
    args_size,
            
    arg_pointer_id,
            
    arg_sort_type,
            
    __dummy_elem_num_args_expected __dummy_elem_ 1
        
    };
        if (!
    CheckArgs())
            return 
    0;
        return 
    MemoryPool_Sort(amxparams[arg_pointer_id], params[arg_sort_type]);

    С таким вариантом у каждого из параметров есть своё название, перепутать params[arg_pointer_id] и params[arg_sort_type] куда сложнее, чем params[1] и params[2], а в CheckArgsNumber не нужно самостоятельно считать количество параметров функции - оно само подсчитывается в args_expected_number.

    P.S. Если кому-нибудь будет интересно, вот сама функция CheckArgs, вернее, макрос:
    PHP код:
    #define CheckArgs() pluginutils::CheckNumberOfArguments(amx, params, num_args_expected) 
    В нём, в свою очередь, используется функция pluginutils::CheckNumberOfArguments, которую можно найти в файлах pluginutils.h/.cpp из моего шаблона для плагинов:

    Автор: Daniel_Cortez

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

  2. #2
    Аватар для ziggi
    Проверенный

    Статус
    Оффлайн
    Регистрация
    14.05.2015
    Сообщений
    1,181
    Репутация:
    790 ±
    Годно, надо попробовать. Для полноты картины не помешало бы выложить функцию CheckArgsNumber (понятно, что функция проста, но всё же).

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

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±
    Не то, чтобы много откликов, но и отрицательных отзывов тоже нет, поэтому, полагаю, можно переместить это в подраздел "Разработки".
    Заодно добавил функцию CheckArgsNumber, она очень простая на самом деле.
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

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

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

  5. #5
    Аватар для ziggi
    Проверенный

    Статус
    Оффлайн
    Регистрация
    14.05.2015
    Сообщений
    1,181
    Репутация:
    790 ±
    Есть ли возможность узнать имя функции в CheckArgsNumber как-то через AMX?
    Я пока придумал только это:
    PHP код:
    inline bool CheckArgsNumber(AMX *amx, const char *func, const cell *paramscell num_expected)
    {
        if (
    params[0] / (cell)sizeof(cell) < num_expected) {
            
    logprintf(" * " PLUGIN_NAME ": Incorrect parameter count for \"%s\", %d != %d\n"funcparams[0] / 4num_expected);
            
    amx_RaiseError(amxAMX_ERR_PARAMS);
            return 
    false;
        }
        return 
    true;

    PHP код:
    if (!CheckArgsNumber(amx__func__paramsargs_expected_number)) {
        return 
    0;

    UPD.
    Хотя лучше, наверное, применять конструкцию вида:
    PHP код:
    inline bool CheckArgsNumber(AMX *amx, const cell *paramscell num_expected)
    {
        if (
    params[0] / (cell)sizeof(cell) < num_expected) {
            
    amx_RaiseError(amxAMX_ERR_PARAMS);
            return 
    false;
        }
        return 
    true;
    }
    #define PRINT_PARAM_ERROR() \
        
    logprintf(" * " PLUGIN_NAME ": Incorrect parameter count for \"%s\", %d != %d\n"__func__params[args_size] / 4args_expected_number
    PHP код:
    if (!CheckArgsNumber(amxparamsargs_expected_number)) {
        
    PRINT_PARAM_ERROR();
        return 
    0;

    Последний раз редактировалось ziggi; 19.11.2016 в 11:31.

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

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±
    По идее это хост-приложение должно выводить информацию об ошибках времени выполнения в интерпретаторе. Так что, ИМХО, настоящий вопрос здесь - "почему сервер SA:MP этого не делает, оставляя все ошибки скрытыми?"
    К слову, достаточно добавить всего лишь пару-тройку строк кода, чтобы, как минимум, отобразить суть ошибки (например, "stack/heap collision" или "array index out of bounds"), но в SA:MP даже этого нет (и не будет, по очевидным причинам).

    Если же говорить о том, как узнать имя текущей нативной функции, в этом может помочь регистр CIP (amx->cip).
    Чуть позже я выложу код, но в отдельной теме, заодно постараюсь объяснить всю теорию.

    UPD: http://pro-pawn.ru/showthread.php?14522
    Последний раз редактировалось Daniel_Cortez; 21.11.2016 в 19:17. Причина: updated
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

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

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±
    Статья обновлена. Теперь функция CheckNumberOfArguments используется через макрос CheckArgs, а сама функция перемещена в шаблон плагина для SA-MP. Кроме того, в сообщении об ошибке выводится имя нативной функции, которое узнаётся с помощью GetCurrentNativeFunctionName.
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

 

 

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

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

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

Ваши права

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