Germany | Finland | Saint Petersburg | Drive

Lua assert() + m4

Опубликовано в M4

В языке lua для контроля нестандартных ситуаций имеется базовая функция assert(). Очень полезная функция, однако имеет и одно неприятное свойство.

 

Функция assert() выдает сообщение об ошибке, если значение её первого параметра равно false (то есть nil или false).

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

Как использовать? Например, для проверки правильности переданных в функцию параметров.

Напишем простейшую функцию, которая делит одно число на другое. Работать она может неправильно в случае если второй параметр равен 0. Интерпретатор Lua поделит на 0 без затруднений и получит бесконечность, однако нас вряд ли устроит такой ход событий. Такие ситуации обычно нужно отслеживать сразу же, не позволяя ошибкам накапливаться и всплывать уже где-то в другом месте. Вставляем assert()

function divide(x,y)
   assert(y ~= 0, "Деление на ноль")
   return x/y
end

Всё чудесно, стоит передать вторым параметром 0 - и мы получим диагностику в процессе исполнения этого кода.

Однако ничто не даётся даром. Такой подход имеет 2 серьёзных недостатка.

  • assert() выполняется всегда, даже в отлаженной программе.
  • замедляет быстродействие (довольно значительно)

Можно смириться с этим. Также можно в отлаженной программе руками каждый раз убирать все лишние вызовы assert(). Третий способ - использовать препроцессор. Пишем простейший макрос:

define(`m4_assert',`ifdef(`DEBUG',`assert(`$1',"Assert failed: __file__ line __ line__")')')

Теперь наша функция будет выглядеть так:

function divide(x,y)
   m4_assert(y ~= 0)
   return x/y
end

Теперь мы легко управляем наличием или отсутствием проверок. При отладке программы проверки должны быть, в конечном варианте они не требуются. Если собрать готовую программу с объявленным флагом DEBUG (то есть вставим в начало программы строчку define(`DEBUG') ), то на выходе получим код на Lua:

function divide(x,y)
    assert(y ~= 0, "Assert failed: d:/test.lua line 2)
    return x/y
end

Если флаг DEBUG для окончательной сборки не задавать, то вызова функции assert() в результирующем коде не будет.

function divide(x,y)
     return x/y
end

Эти конструкции я использую во всех своих библиотечных функциях. В основном благодаря такому подходу отпадает необходимость пользоваться отладчиками для поиска ошибок в отлаживаемых программах на Lua и в конечную версию программы не попадает уже ненужный код проверок.

 

Для вывода в сообщение стека вызовов нужно слегка модифицировать макрос m4_assert: 

define(`m4_ifval', `ifelse(`$1',`',`$3',`$2')')
define(`m4_assert', `ifdef(`DEBUG',`assert(`$1',"Assert failed: __file__ line __line__`'\n" .. m4_ifval(`$2',`$2'.."\n","") .. debug.traceback())')')

 

Добавить комментарий


Библиотека