Lua a prueba de errores (I)

Tutoriales Avanzados Homebrewes

A la hora de lanzar un juego o una aplicación, una de las cosas más frustantes tanto para el Coder como para el usuario, es ver el típico mensaje de error. En este tutorial, veremos cómo evitar la interrupción del error en el programa y la forma de modificar ese mensaje.

 

El típico mensaje de error es parecido a este:
error: fichero.lua:239: attempt to perform arithmetic on a nil value

Y que la aplicación finalice de forma brusca.

Desde luego, hay errores, los llamados "fatales", que por mucho que nos empeñemos harán que no podamos seguir nuestra aplicación, pero otros, aunque el intérprete lua haga terminar el script, nosotros sí podríamos volver a tomar el control y continuar con la aplicación o volver a un punto anterior.

Errores "fatales" (no podemos retornar a la aplicación después de ellos):

  •     Errores de sintaxis al teclear el código.
  •     Falta de memoria RAM.
  •     Fallo físico del disco que impide leer el script.

Errores en los que sería posible retomar la aplicación:

  •     Fallo al hacer una operación con nil. Por ejemplo sumar o mostrar un nil. Si ese nil se ha realizado por leer algo de un fichero (una canción vacía, una imagen con mal formato, etc.), se le puede dar la oportunidad al usuario de que seleccione otra canción u otra imagen y volver a intentarlo).
  •     Errores al conectar a una unidad de disco. Se puede pedir al usuario que vuelva a intentarlo.

Espero que hayáis captado la idea.

Lenguajes compilados como C++, FreePascal, Visual Basic, y otros interpretados como PHP o Python, tienen una forma más elegante y efectiva de tratar las excepciones (se le suele llamar así a los manejos de errores), Lua lo hace de una forma poco usual y bastante "cutre" a mi modo de ver, pero es lo que toca, así que no nombraré las técnicas "normales" y me centraré en la técnica "a lo Lua", para no confundir (y daros pena xD). Comencemos pues:

function LanzarError(num)
    local total = num + nil
    screen:print(0,0,"LanzarError: El total es: "..total),blanco)
end
 
LanzarError(33)
screen:print (0, 15, "continuamos la marcha...", blanco)

Si ejecutamos este código, fallará en la linea 2, y nunca imprimirá "continuamos la marcha...", porque estamos sumando 33 + nil, lo que nos daría el siguiente error:

error: script.lua:2: attempt to perform arithmetic on a nil value

Bueno, vamos a suponer que nosotros sabemos que aunque ese total esté erróneo, podemos continuar la aplicación. Recordad que DEBÉIS TENER UN PLAN ALTERNATIVO en caso de error, sino, de nada sirve continuar si 3 lineas después vamos a usar esa variable que está mal y nos va a dar de nuevo error.

Ahora es cuando la sentencia pcall(), llega al rescate. Mismo código usándola:

function LanzarError(num)
    local total = num + nil
    screen:print(0,0,"LanzarError: El total es: "..total, blanco)
end
 
local ok 
ok = pcall(LanzarError, 33)
if not ok then
    screen:print(0,15,"Hubo un error al sumar el total. Disfruten las molestias.", blanco)
end
screen:print(0,30,"continuamos la marcha...",blanco)

Ahora tenemos la siguiente salida:

Hubo un error al sumar el total. Disfruten las molestias.
continuamos la marcha...

Si os fijáis, el programa ha continuado y lo mejor de todo es que no se ha visto "el típico mensaje de error", sino uno que le hemos puesto. Esto da a nuestras aplicaciones un acabado más profesional. Es decir También hemos personalizado el error, es más, no podríamos haber puesto ningún mensaje.

Comentando el código anterior,  si os fijáis, pcall(), tiene como primer parámetro la función a la cual se llama y los siguientes parámetros son los que se le envían a la función llamada, en este caso, sólo enviamos un parámetro, el núm. 33. pcall() retorna más de un parámetro, pero eso lo vamos a ver un poco más adelante, de momento quedaros con que envía como primer parámetro true si todo fue bien y false si hubo un error.

Personalizando MEJOR nuestros errores:

Lo cierto, es que más que el mensaje de "disfruten las molestias", es conveniente indicar el script, la linea y la descripción del error, tal y como lo hace Lua por defecto, esto nos permite localizar rápidamente los errores. Pero vamos a ver la forma de hacer esto "más bonito" y no como una simple linea.

Como dije anteriormente, pcall() devuelve más de un valor. Si no hay error, devuelve true como primer parámetro y el siguiente parámetro o parámetros, son los que enviaría la función normalmente si es que devuelve alguno. En caso de error, el primer parámetro será false, y el segundo (IMPORTANTE), es el texto con el error. Si volvemos al ejemplo inicial, este sería el texto que contendría el segundo parámetro (se omite el primer "error: ").

script.lua:2: attempt to perform arithmetic on a nil value  

Ojo con ese segundo valor que unas veces puede ser esa cadena y otra lo que devuelva vuestra función, tenedlo en cuenta.

He creado una función que podéis usar en vuestros proyectos, descompone esa cadena en una tabla con 3 elementos:

  •     script: Contiene el nombre del script, en este caso script.lua
  •     line: La linea del error, en este caso 2
  •     text: El texto del error. "attempt to perform arithmetic on a nil value"

   
Teniendo estos datos separados, podéis presentar los errores de forma "bonita", con vuestra fuente, color, un fondo especial para errores, etc.

function extractError(err)
--[[ 1.0.1 : 2010-06. By GorristeR. Under GPL3 License.
Desglosa en una tabla un mensaje de error devuelvo por pcall(). Dicha tabla
3 elementos:
    script: Nombre del script donde se produjo el error
    line: Linea del error
    text: Texto descriptivo del error
--]]
    local result = {}
    local pos1, pos2
    pos1 = string.find(err, ":")
    pos2 = string.find(err, ":", pos1+1)
    result.script = string.sub(err, 1, pos1-1)
    result.line = string.sub(err, pos1+1, pos2-1)
    result.text = string.gsub(string.sub(err, pos2+1), "^%s*(.-)%s*$", "%1")
    return result
end

Esta es la función extractError():

Y aquí está el último ejemplo:

function LanzarError(num)
    local total = num + nil
    screen:print(0,0,"LanzarError: El total es: "..total,blanco)
end
 
ok, msgError = pcall(LanzarError, 33)
if not ok then
    myError = extractError(msgError)
    screen:print(0,15,"Hubo un error al sumar el total. Disfruten las molestias.",blanco)
    screen:print(0,30,"Script : "..myError.script,blanco)
    screen:print(0,45,"Linea  : "..myError.line,blanco)
    screen:print(0,60,"Error  : "..myError.text,blanco)
    screen:print(0,75,"Consulte manual online para buscar una solución",blanco)
end
print(0,90,"continuamos la marcha...",blanco)

No creo que el código requiera mucha explicación. Como veis podéis personalizar un error como queráis.

Hubo un error al sumar el total. Disfruten las molestias.
Script : script.lua
Linea  : 2
Error  : attempt to perform arithmetic on a nil value
Consulte manual online para buscar una solución
continuamos la marcha...

En la siguiente entrega, Os enseñaré una forma un poco diferente, y más "elegante" de tratar los errores en Lua, seguramente uséis esa nueva forma en vez de ésta, pero siempre es bueno saber de más.

Comentarios se agradecen y motivan a seguir haciendo tutoriales ;-).

4.43299
Tu voto: Ninguno Votos totales: 4.4 (97 votos)

Anuncios Google

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de Migueliziosop

Me encantó.

Sin palabras, esto yo ya me lo sabía pero de esta forma se me hace muchísimo màs fàcil.

Yo utilizaba solamente la función "error()" u otras formas para que me dijiera si había otro error pero este es muchísimo mejor.

Muchas gracias Gorrister.

Imagen de Monty - Calabato64

Icon0 sin linkear

Icon0 sin linkear

Imagen de moikop

Solucionado.

Gracias.

Imagen de Arbër

Je, je

La verdad es que ya había un tutorial de Pipagerardo que explicaba más o menos el uso de la funcion pcall y el manejo de los errores, aunque aqui está todo mucho más claro y mejor explicado, viendo el proceso por pasos y ejemplos. Como siempre se agradecen mucho tus aportes de Lua ;)

Un saludo.


-----[[7 años en Scenebeta, con la misma ilusión que la del primer día]]----

Imagen de unai.

Extraordinario ;)

Extraordinario ;)

Imagen de NEKERAFA

Que bien

Una cosa, la funcion es para todos los ''luas''

Imagen de pspgorrister

Sí, todo el código usado aquí

Sí, todo el código usado aquí vale para cualquier Lua, la diferencia es el uso de "print", para PSP hay que usar "screen:print(columna, fila, mensaje, color)", y para Windows, Linux etc. tan sólo "print(mensaje)", el resto es "Lua estándar".

Un saludo.


LuaDiE: Crea en Lua sin teclear código. Compatible HM7, HMv2, LuaPlayer, LuaDEV y PGE.

Imagen de Lord Hades

Tremendos turoriales que haces!!!

WOW!!, estos ultimos tutos que haz hecho ya hacian falta ;)

Muchas Gracias GorristeR :D

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.