Tratamiento de errores en tiempo de ejecución

Cualquier programa escrito correctamente para compilar sin errores no es, a pesar de todo, inmune a los errores de ejecución. Dichos errores pueden producirse tanto por un descuido del desarrollador como por circunstancias imprevistas surgidas en el entorno del software (como perder la conexión a Internet, quedarse sin memoria, etc.). Pero no menos probable es la situación en la que el error se produce debido a una aplicación incorrecta del programa. En todos estos casos, el programa debe ser capaz de analizar la esencia del problema y procesarlo adecuadamente.

Cada sentencia MQL5 es una fuente potencial de errores en tiempo de ejecución. Si se produce un error de este tipo, el terminal guarda un código descriptivo en la variable especial _LastError. Asegúrese de analizar el código inmediatamente después de cada sentencia, ya que posibles errores en sentencias posteriores pueden sobrescribir este valor.

Tenga en cuenta que hay una serie de errores críticos que abortarán inmediatamente la ejecución del programa cuando se produzcan:

  • División por cero
  • Índice fuera de rango
  • Puntero de objeto incorrecto

Para obtener una lista completa de los códigos de error y su significado, consulte la documentación.

En la sección Abrir y cerrar archivos hemos abordado ya el problema del diagnóstico de errores como parte de la escritura de una macro PRTF útil. Allí, en particular, hemos visto un archivo de encabezado auxiliar MQL5/Include/MQL5Book/MqlError.mqh, en el que la enumeración MQL_ERROR permite convertir fácilmente el código de error numérico en un nombre utilizando EnumToString.

enum MQL_ERROR
{
   SUCCESS = 0
   INTERNAL_ERROR = 4001
   WRONG_INTERNAL_PARAMETER = 4002
   INVALID_PARAMETER = 4003
   NOT_ENOUGH_MEMORY = 4004
   ...
   // start of area for errors defined by the programmer (see next section)
   USER_ERROR_FIRST = 65536
};
#define E2S(XEnumToString((MQL_ERROR)(X))

Aquí, como parámetro X de la macro E2S, deberíamos tener la variable _LastError o su función equivalente GetLastError.

int GetLastError() ≡ int _LastError

La función devuelve el código del último error que se ha producido en las sentencias del programa MQL. Inicialmente, mientras no haya errores, el valor es 0. La diferencia entre leer _LastError y llamar a la función GetLastError es puramente sintáctica (elija la opción adecuada según el estilo preferido).

Debe tenerse en cuenta que la ejecución regular de sentencias sin errores no restablece el código de error, y tampoco lo hará llamar a GetLastError.

Por lo tanto, si hay una secuencia de acciones en la que sólo una activará una bandera de error, esta bandera será devuelta por la función para las acciones posteriores (con éxito). Por ejemplo:

// _LastError = 0 by default
action1// ok, _LastError doe not change
action2// error, _LastError = X
action3// ok, _LastError does not change, i.e. is still equal to X
action4// another error, _LastError = Y
action5// ok, _LastError does not change, that is, it is still equal to Y
action6// ok, _LastError does not change, that is, it is still equal to Y

Este comportamiento dificultaría la localización de la zona problemática. Para evitarlo, existe una función ResetLastError independiente que restablece la variable _LastError a 0.

void ResetLastError()

La función pone a cero el valor de la variable integrada _LastError.

Se recomienda llamar a la función antes de cualquier acción que pueda dar lugar a un error y después de la cual usted va a analizar los errores por medio de GetLastError.

Un buen ejemplo de uso de ambas funciones es la macro PRTF ya mencionada (archivo PRTF.mqh). Su código se muestra a continuación:

#include <MQL5Book/MqlError.mqh>
   
#define PRTF(AResultPrint(#A, (A))
   
template<typename T>
T ResultPrint(const string sconst T retval = NULL)
{
   const int snapshot = _LastError// recording _LastError at input
   const string err = E2S(snapshot) + "(" + (string)snapshot + ")";
   Print(s"="retval" / ", (snapshot == 0 ? "ok" : err));
   ResetLastError(); // clear the error flag for the next calls
   return retval;
}

El propósito de la macro y de la función ResultPrint incluida en ella es registrar el valor pasado, que es el código de error actual, y borrar inmediatamente el código de error. Así, la aplicación sucesiva de PRTF sobre una serie de sentencias garantiza siempre que el error (o la indicación de éxito) que se imprime en el registro corresponde a la última sentencia con la que se obtuvo el valor del parámetro retval.

Necesitamos guardar _LastError en la variable local intermedia snapshot porque _LastError puede cambiar su valor en casi cualquier punto de la evaluación de una expresión si falla alguna operación. En este ejemplo concreto, la macro E2S utiliza la función EnumToString, que puede generar su propio código de error si se pasa como argumento un valor que no está en la enumeración. A continuación, en las subsiguientes partes de la misma expresión, al formar una cadena, no veremos el error inicial sino el planteado.

Puede haber varios lugares en cualquier sentencia en los que _LastError cambie de repente. A este respecto, conviene registrar el código de error inmediatamente después de la acción deseada.