Lua throw error

Error handling is quite critical since real-world operations often require the use of complex operations, which includes file operations, database transactions and web service calls.

Need for Error Handling

Error handling is quite critical since real-world operations often require the use of complex operations, which includes file operations, database transactions and web service calls.

In any programming, there is always a requirement for error handling. Errors can be of two types which includes,

  • Syntax errors
  • Run time errors

Syntax Errors

Syntax errors occur due to improper use of various program components like operators and expressions. A simple example for syntax error is shown below.

a == 2

As you know, there is a difference between the use of a single «equal to» and double «equal to». Using one instead of the other can lead to an error. One «equal to» refers to assignment while a double «equal to» refers to comparison. Similarly, we have expressions and functions having their predefined ways of implementation.

Another example for syntax error is shown below −

for a= 1,10
   print(a)
end

When we run the above program, we will get the following output −

lua: test2.lua:2: 'do' expected near 'print'

Syntax errors are much easier to handle than run time errors since, the Lua interpreter locates the error more clearly than in case of runtime error. From the above error, we can know easily that adding a do statement before print statement is required as per the Lua structure.

Run Time Errors

In case of runtime errors, the program executes successfully, but it can result in runtime errors due to mistakes in input or mishandled functions. A simple example to show run time error is shown below.

function add(a,b)
   return a+b
end

add(10)

When we build the program, it will build successfully and run. Once it runs, shows a run time error.

lua: test2.lua:2: attempt to perform arithmetic on local 'b' (a nil value)
stack traceback:
	test2.lua:2: in function 'add'
	test2.lua:5: in main chunk
	[C]: ?

This is a runtime error, which had occurred due to not passing two variables. The b parameter is expected and here it is nil and produces an error.

Assert and Error Functions

In order to handle errors, we often use two functions − assert and error. A simple example is shown below.

local function add(a,b)
   assert(type(a) == "number", "a is not a number")
   assert(type(b) == "number", "b is not a number")
   return a+b
end

add(10)

When we run the above program, we will get the following error output.

lua: test2.lua:3: b is not a number
stack traceback:
	[C]: in function 'assert'
	test2.lua:3: in function 'add'
	test2.lua:6: in main chunk
	[C]: ?

The error (message [, level]) terminates the last protected function called and returns message as the error message. This function error never returns. Usually, error adds some information about the error position at the beginning of the message. The level argument specifies how to get the error position. With level 1 (the default), the error position is where the error function was called. Level 2 points the error to where the function that called error was called; and so on. Passing a level 0 avoids the addition of error position information to the message.

pcall and xpcall

In Lua programming, in order to avoid throwing these errors and handling errors, we need to use the functions pcall or xpcall.

The pcall (f, arg1, …) function calls the requested function in protected mode. If some error occurs in function f, it does not throw an error. It just returns the status of error. A simple example using pcall is shown below.

function myfunction ()
   n = n/nil
end

if pcall(myfunction) then
   print("Success")
else
	print("Failure")
end

When we run the above program, we will get the following output.

Failure

The xpcall (f, err) function calls the requested function and also sets the error handler. Any error inside f is not propagated; instead, xpcall catches the error, calls the err function with the original error object, and returns a status code.

A simple example for xpcall is shown below.

function myfunction ()
   n = n/nil
end

function myerrorhandler( err )
   print( "ERROR:", err )
end

status = xpcall( myfunction, myerrorhandler )
print( status)

When we run the above program, we will get the following output.

ERROR:	test2.lua:2: attempt to perform arithmetic on global 'n' (a nil value)
false

As a programmer, it is most important to ensure that you take care of proper error handling in the programs you write. Using error handling can ensure that unexpected conditions beyond the boundary conditions are handled without disturbing the user of the program.

Because Lua functions may return multiple values, they can return error strings
together with the status code. The classic example is io,open which usually
returns a file object, but will return both a nil and an error message if the file
cannot be opened:

> = io.open("hello.txt")
nil	hello.txt: No such file or directory	2

However, the Lua libraries aren’t always consistent in error handling. The table functions
throw errors:

> t = {'h',{}}
> = table.concat(t)
stdin:1: invalid value (table) at index 2 in table for 'concat'
stack traceback:
    [C]: in function 'concat'
    stdin:1: in main chunk

Even in the most careful programs, something will go wrong, and you do not
want users to witness a crash with a stack traceback. Also, it is often possible
to recover from the situation. Languages with exception handling
encourage the ‘throwing’ or ‘raising’ of errors because then the error handling
code is kept away from the ‘normal’ logic — what is sometimes called
‘the happy path’.

There is no try/except statement in Lua, but there is a function pcall
(or ‘protected call’) which will call a Lua function safely. It will return the status
and anything returned by the function. If the status is false, then the second
return value is the error message.

> = pcall(table.concat,t)
false	invalid value (table) at index 2 in table for 'concat'
> = pcall(table.concat,{'one','two'})
true	onetwo

Anonymous functions are useful for blocks that can raise errors:

local status,err = pcall(function()
    do_something()
    do_another()
    and_again()
end)
if not status then
    print('error',err)
end

You can raise your own errors, using error. In its simplest form it will indicate
that the error came from the current line:

--> error is reported at this line; program will give a stacktrace
if not right then error("was not right!") end

There can also be a second parameter that indicates where the error came from:

function not_right_error()
    error("was not right",2) -- i.e, _calling_ function raised this
end

--> error still reported at this line
if not right then not_right_error() end

The function assert raises an error if a condition fails; it may be given an optional message.

--> message 'assertion failed...."
assert(n > 0)
--> message "size must be positive"
assert(n > 0,"size must be positive")

An equivalent Lua version looks like this:

function assert(value,message)
    if not value then
        if message == nil then message = "assertion failed" end
        error(message,2)
    else
        return value
    end
end

Using pcall

pcall stands for «protected call». It is used to add error handling to functions. pcall works similar as try-catch in other languages. The advantage of pcall is that the whole execution of the script is not being interrupted if errors occur in functions called with pcall. If an error inside a function called with pcall occurs an error is thrown and the rest of the code continues execution.


Syntax:

pcall( f , arg1,···)

Return Values:

Returns two values

  1. status (boolean)
  • Returns true if the function was executed with no errors.
  • Returns false if an error occured inside the function.
  1. return value of the function or error message if an error occurred inside the function block.

pcall may be used for various cases, however a common one is to catch errors from the function which has been given to your function. For instance, lets say we have this function:

local function executeFunction(funcArg, times) then
    for i = 1, times do
        local ran, errorMsg = pcall( funcArg )
        if not ran then
            error("Function errored on run " .. tostring(i) .. "n" .. errorMsg)
        end
    end
end

When the given function errors on run 3, the error message will be clear to the user that it is not coming from your function, but from the function which was given to our function. Also, with this in mind a fancy BSoD can be made notifying the user. However, that is up to the application which implements this function, as an API most likely won’t be doing that.

Example AExecution without pcall

function square(a)
  return a * "a"    --This will stop the execution of the code and throws an error, because of the attempt to perform arithmetic on a string value
end

square(10);

print ("Hello World")    -- This is not being executed because the script was interrupted due to the error

Example BExecution with pcall

function square(a)
  return a * "a"
end

local status, retval = pcall(square,10);

print ("Status: ", status)        -- will print "false" because an error was thrown.
print ("Return Value: ", retval)  -- will print "input:2: attempt to perform arithmetic on a string value"
print ("Hello World")    -- Prints "Hello World"

ExampleExecution of flawless code

function square(a)
  return a * a
end

local status, retval = pcall(square,10);

print ("Status: ", status)        -- will print "true" because no errors were thrown 
print ("Return Value: ", retval)  -- will print "100"
print ("Hello World")    -- Prints "Hello World"

Handling errors in Lua

Assuming we have the following function:

function foo(tab)
  return tab.a
end -- Script execution errors out w/ a stacktrace when tab is not a table

Let’s improve it a bit

function foo(tab)
  if type(tab) ~= "table" then
    error("Argument 1 is not a table!", 2)
  end
  return tab.a
end -- This gives us more information, but script will still error out

If we don’t want a function to crash a program even in case of an error, it is standard in lua to do the following:

function foo(tab)
    if type(tab) ~= "table" then return nil, "Argument 1 is not a table!" end
    return tab.a
end -- This never crashes the program, but simply returns nil and an error message

Now we have a function that behaves like that, we can do things like this:

if foo(20) then print(foo(20)) end -- prints nothing
result, error = foo(20)
if result then print(result) else log(error) end

And if we DO want the program to crash if something goes wrong, we can still do this:

result, error = foo(20)
if not result then error(error) end

Fortunately we don’t even have to write all that every time; lua has a function that does exactly this

result = assert(foo(20))

Notes on the Implementation of Lua 5.3

Exceptions

Exceptions in Lua are emulated with LUAI_THROW, LUAI_TRY, and luai_jmpbuf.

Interestingly, if you compile Lua with a C++ compiler, standard C++ exceptions will be used. If a C compiler is used, they will be emulated with setjmp and longjmp.

/*
** LUAI_THROW/LUAI_TRY define how Lua does exception handling. By
** default, Lua handles errors with exceptions when compiling as
** C++ code, with _longjmp/_setjmp when asked to use them, and with
** longjmp/setjmp otherwise.
*/
#if !defined(LUAI_THROW)				/* { */

#if defined(__cplusplus) && !defined(LUA_USE_LONGJMP)	/* { */

/* C++ exceptions */
#define LUAI_THROW(L,c)		throw(c)
#define LUAI_TRY(L,c,a) 
    try { a } catch(...) { if ((c)->status == 0) (c)->status = -1; }
#define luai_jmpbuf		int  /* dummy variable */

#elif defined(LUA_USE_POSIX)				/* }{ */

/* in POSIX, try _longjmp/_setjmp (more efficient) */
#define LUAI_THROW(L,c)		_longjmp((c)->b, 1)
#define LUAI_TRY(L,c,a)		if (_setjmp((c)->b) == 0) { a }
#define luai_jmpbuf		jmp_buf

#else							/* }{ */

/* ISO C handling with long jumps */
#define LUAI_THROW(L,c)		longjmp((c)->b, 1)
#define LUAI_TRY(L,c,a)		if (setjmp((c)->b) == 0) { a }
#define luai_jmpbuf		jmp_buf

#endif							/* } */

#endif							/* } */

An errorjmp is defined in Lua state. It’s a linked-list for recording a series of jump location of the current error.

/* chain list of long jump buffers */
struct lua_longjmp {
  struct lua_longjmp *previous;
  luai_jmpbuf b;
  volatile int status;  /* error code */
};

// error codes
/* thread status */
#define LUA_OK		0
#define LUA_YIELD	1
#define LUA_ERRRUN	2
#define LUA_ERRSYNTAX	3
#define LUA_ERRMEM	4
#define LUA_ERRGCMM	5
#define LUA_ERRERR	6

lua_longjmp is used in the luaD_rawrunprotected:

int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
  unsigned short oldnCcalls = L->nCcalls;
  struct lua_longjmp lj;
  lj.status = LUA_OK;
  lj.previous = L->errorJmp;  /* chain new error handler */
  L->errorJmp = &lj;
  LUAI_TRY(L, &lj,
    (*f)(L, ud);
  );
  L->errorJmp = lj.previous;  /* restore old error handler */
  L->nCcalls = oldnCcalls;
  return lj.status;
}

luaD_rawrunprotected is used in the luaD_pcall.

int luaD_pcall (lua_State *L, Pfunc func, void *u,
                ptrdiff_t old_top, ptrdiff_t ef) {
  int status;
  CallInfo *old_ci = L->ci;
  lu_byte old_allowhooks = L->allowhook;
  unsigned short old_nny = L->nny;
  ptrdiff_t old_errfunc = L->errfunc;
  L->errfunc = ef;
  status = luaD_rawrunprotected(L, func, u);
  if (status != LUA_OK) {  /* an error occurred? */
    StkId oldtop = restorestack(L, old_top);
    luaF_close(L, oldtop);  /* close possible pending closures */
    seterrorobj(L, status, oldtop);
    L->ci = old_ci;
    L->allowhook = old_allowhooks;
    L->nny = old_nny;
    luaD_shrinkstack(L);
  }
  L->errfunc = old_errfunc;
  return status;
}

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

В любом программировании всегда есть требование для обработки ошибок. Ошибки могут быть двух типов, которые включают в себя,

  • Синтаксические ошибки
  • Ошибки во время выполнения

Синтаксические ошибки

Синтаксические ошибки возникают из-за неправильного использования различных компонентов программы, таких как операторы и выражения. Простой пример синтаксической ошибки показан ниже.

a == 2

Как вы знаете, существует разница между использованием одного «равно» и двойного «равно». Использование одного вместо другого может привести к ошибке. Одно «равно» относится к присвоению, а двойное «равно» относится к сравнению. Точно так же у нас есть выражения и функции, имеющие предопределенные способы реализации.

Другой пример синтаксической ошибки показан ниже –

Live Demo

for a= 1,10
   print(a)
end

Когда мы запустим вышеупомянутую программу, мы получим следующий вывод –

lua: test2.lua:2: 'do' expected near 'print'

Синтаксические ошибки гораздо легче обрабатывать, чем ошибки времени выполнения, поскольку интерпретатор Lua обнаруживает ошибку более четко, чем в случае ошибки времени выполнения. Из вышеприведенной ошибки мы можем легко узнать, что добавление оператора do перед оператором print требуется согласно структуре Lua.

Ошибки времени выполнения

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

Live Demo

function add(a,b)
   return a+b
end

add(10)

Когда мы соберем программу, она будет успешно собрана и запущена. Как только он запускается, показывает ошибку времени выполнения.

lua: test2.lua:2: attempt to perform arithmetic on local 'b' (a nil value)
stack traceback:
	test2.lua:2: in function 'add'
	test2.lua:5: in main chunk
	[C]: ?

Это ошибка времени выполнения, которая произошла из-за отсутствия передачи двух переменных. Ожидается параметр b, и здесь он равен нулю и выдает ошибку.

Функции подтверждения и ошибки

Для обработки ошибок мы часто используем две функции – assert и error . Простой пример показан ниже.

Live Demo

local function add(a,b)
   assert(type(a) == "number", "a is not a number")
   assert(type(b) == "number", "b is not a number")
   return a+b
end

add(10)

Когда мы запустим вышеуказанную программу, мы получим следующий вывод об ошибке.

lua: test2.lua:3: b is not a number
stack traceback:
	[C]: in function 'assert'
	test2.lua:3: in function 'add'
	test2.lua:6: in main chunk
	[C]: ?

Ошибка (сообщение [, уровень]) завершает последнюю вызванную защищенную функцию и возвращает сообщение как сообщение об ошибке. Эта ошибка функции никогда не возвращается. Обычно ошибка добавляет некоторую информацию о позиции ошибки в начале сообщения. Аргумент уровня указывает, как получить позицию ошибки. На уровне 1 (по умолчанию) позиция ошибки – это то место, где была вызвана функция ошибки. Уровень 2 указывает на ошибку, где была вызвана функция, которая вызвала ошибку; и так далее. Пропуск уровня 0 позволяет избежать добавления информации о позиции ошибки в сообщение.

pcall и xpcall

В программировании на Lua, чтобы избежать появления этих ошибок и обработки ошибок, нам нужно использовать функции pcall или xpcall.

Функция pcall (f, arg1, …) вызывает запрошенную функцию в защищенном режиме. Если какая-то ошибка возникает в функции f, она не выдает ошибку. Он просто возвращает статус ошибки. Простой пример использования pcall показан ниже.

Live Demo

function myfunction ()
   n = n/nil
end

if pcall(myfunction) then
   print("Success")
else
	print("Failure")
end

Когда мы запустим вышеуказанную программу, мы получим следующий вывод.

Failure

Функция xpcall (f, err) вызывает запрошенную функцию, а также устанавливает обработчик ошибок. Любая ошибка внутри f не распространяется; вместо этого xpcall перехватывает ошибку, вызывает функцию err с исходным объектом ошибки и возвращает код состояния.

Простой пример для xpcall показан ниже.

Live Demo

function myfunction ()
   n = n/nil
end

function myerrorhandler( err )
   print( "ERROR:", err )
end

status = xpcall( myfunction, myerrorhandler )
print( status)

Когда мы запустим вышеуказанную программу, мы получим следующий вывод.

ERROR:	test2.lua:2: attempt to perform arithmetic on global 'n' (a nil value)
false

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


Понравилась статья? Поделить с друзьями:
  • Lua error что это
  • Lua error unexpected symbol near
  • Lua error table index is nil
  • Lua error string serious sam
  • Lua error stdin 1 attempt to call field alarm a nil value