1. ¿Por qué macros? Son incómodas, no se les pueden aplicar todas las condiciones y es extremadamente difícil depurarlas si algo va mal. Era más fácil implementar procedimientos triviales.
2. Algún truco demasiado "sucio" con el array. ¿No se podría dividir por cero?
1. Para no ser insustancial, muéstrame un ejemplo de una condición que no pueda ser alimentada a mi macro (no estoy siendo sarcástico, es realmente importante para mí conocer todos los puntos sutiles, ya que utilizo esta macro todo el tiempo). Así que, por favor explique ¿cuáles son las dificultades de depuración?
Y en general, sí, se puede hacer con un procedimiento, mostré sólo dos ejemplos posibles. Pero para ser justos, no conozco una manera elegante de obtener todos estos datos en un procedimiento:
- El texto de la expresión pasada a la comprobación(#condición).
- El nombre del fichero de código fuente desde el que se llamó a la macro(__FILE__).
- Firma de la función o método desde el que se llamó a la macro(__FUNCSIG__).
- Número de línea del archivo de código fuente en el que se encuentra la llamada a la macro(__LINE__).
Te agradecería mucho (y seguramente no soy el único) que me mostraras tu variante en forma de procedimiento que implementara todo esto (por supuesto, "out of the box" y en la máquina, y no pasando manualmente todo esto como parámetros). En principio, 2...4 se puede pasar como parámetros de entrada, y será más o menos universal (en el sentido de que siempre será lo mismo pasado, no tendrá que configurar manualmente algo), pero ¿cómo conseguir que el elemento. 1 para obtener en el procedimiento no tengo ninguna idea en absoluto.
Además de todo lo mismo por lo general, como en el mismo C ++, las declaraciones están escritas en macros, de la misma manera y me fui de la misma manera. El único punto débil que veo: si se declara un parámetro de entrada o una variable llamada x en el procedimiento/función donde usamos dicha macro, nos saldrá un aviso. La solución es sencilla: en la macro nombrar el array algo más único, por ejemplo assertionFailedArray.
2. No veo la diferencia. Un error de ejecución es un error de ejecución, bloqueará el programa y no se seguirá ejecutando. Sin embargo, te respondo por qué lo hice así: al principio era división por cero, pero cuando estaba probando dicha macro, por alguna razón la ejecución del código no se interrumpía al llamarla en métodos. Si se llamaba en OnTick, OnInit, etc., entonces sí, la ejecución se detenía. Si dentro de algún método de una clase arbitraria, entonces no. Si era un error de MQL5, no me molesté en mirarlo, simplemente empecé a llamar a otro error de ejecución :).
Voy a tratar de ver lo que está mal con la división por cero en los métodos.
#property copyright "Copyright 2015, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property strict //+------------------------------------------------------------------+ //|| //+------------------------------------------------------------------+ struct CFormatOutEol { uchar dummy; }; struct CFormatOutFmtDigits { int digits; }; struct CFormatOutFmtSpace { bool space; }; //+------------------------------------------------------------------+ //|| //+------------------------------------------------------------------+ class CFormatOut { string m_line; string m_dbl_fmt; bool m_auto_space; public: //---- constructor CFormatOut(int dbl_fmt_digits=4,bool auto_space=false):m_dbl_fmt("%."+(string)dbl_fmt_digits+"f") { } //--- datos de salida CFormatOut *operator<<(double x) { auto_space(); m_line+=StringFormat(m_dbl_fmt,x); return(GetPointer(this)); } CFormatOut *operator<<(string s) { auto_space(); m_line+=s; return(GetPointer(this)); } CFormatOut *operator<<(long l) { auto_space(); m_line+=(string)l; return(GetPointer(this)); } //--- salida final de línea (salida real/llamada a la función Imprimir) CFormatOut *operator<<(CFormatOutEol &eol) { Print(m_line); m_line=NULL; return(GetPointer(this)); } //--- cambiar el formato de salida para números reales CFormatOut *operator<<(CFormatOutFmtDigits &fmt) { m_dbl_fmt="%."+(string)fmt.digits+"f"; return(GetPointer(this)); } //--- add/remove auto space insetring CFormatOut *operator<<(CFormatOutFmtSpace &fmt) { m_auto_space=fmt.space; return(GetPointer(this)); } protected: void auto_space() { if(m_line!=NULL && m_auto_space) m_line+=" "; } }; CFormatOut OUT; //--- objeto especial para insertar EndOfLine en la salida CFormatOutEol EOL; //--- configuración de dígitos para la salida de números CFormatOutFmtDigits DBL_FMT_DIGITS(int digits) { CFormatOutFmtDigits fmt; fmt.digits=digits; return(fmt); } //--- activar/desactivar la inserción de espacios entre las salidas CFormatOutFmtSpace AUTO_SPACE(bool enable) { CFormatOutFmtSpace fmt; fmt.space =enable; return(fmt); } //--- función corta para convertir enums a cadena template<typename T> string EN(T enum_value) { return(EnumToString(enum_value)); }Uso:
OUT << AUTO_SPACE(true) << M_PI << "Test" << DBL_FMT_DIGITS(6) << M_PI << EN(PERIOD_M1) << EOL;Resultado:
2015.09.01 18:04:49.060 Test EURUSD,H1: 3.1416 Test 3.141593 PERIOD_M1
ATENCIÓN: Los parámetros de la expresión OUT << ... en orden inverso, de derecha a izquierda, ¡es posible un efecto secundario!
No sé por qué (estamos hablando de depuración), así que dejaré este código aquí:
Si en tu código puedes especificar por dónde salir (al log vía Print, a la alerta, a un fichero, etc.), puede ser aún más útil, me parece a mí. Sobre todo porque no es nada difícil hacerlo.
P.D. ¿Puedo criticar/elogiar el artículo? :)
Si en tu código puedes especificar por dónde salir (al log vía Print, a la alerta, a un fichero, etc.), puede ser aún más útil, me parece a mí. Sobre todo porque no es nada difícil hacerlo.
P.D. ¿Puedo criticar/elogiar el artículo? :)
Pero, ¡IMHO! Para los usuarios (uso a gran escala del material del artículo) no sólo necesitas DEBUG ASSERT, sino también un logger.
Un buen logger debería tener un nivel de logging:
- FATAL - ошибка, дальнейшее выполнение программы невозможно
- ERR - ошибка, выполнение программы можно продолжить
- ATT - предупреждение
- MSG - сообщение
Cuando el programa está depurando, DebugBreak se llama en el registrador - usted puede parar y mirar el medio ambiente(estado) del programa MQL.
Cuando el programa se está ejecutando en el usuario final, los mensajes del registrador se guardan en un archivo(print/alert).
Por defecto, el logger emite solo errores ERR y FATAL y el usuario siempre puede cambiar el nivel de logging al ejecutar el programa para ver todos los mensajes del programa (ATT y MSG).
Si se usa correctamente, el log puede ser usado para identificar/encontrar un error en el programa.
#property script_show_inputs enum EnLogLevel { __LOG_LEVEL_FATAL, // sólo errores fatales __LOG_LEVEL_ERR, // sólo errores __LOG_LEVEL_ATT, // advertencias y errores __LOG_LEVEL_MSG, // todos los mensajes }; input EnLogLevel LogLevel=__LOG_LEVEL_MSG; // nivel de registro //+------------------------------------------------------------------+ //|| //+------------------------------------------------------------------+ #define __LOG_OUT(params) ExtTrueLogger.Out params #define __LOG(level,params) do{ if(level<=LogLevel) __LOG_OUT(params); }while(0) #define LOG_MSG(msg) __LOG(__LOG_LEVEL_MSG,(__FUNCSIG__,__FILE__,__LINE__,msg)) #define LOG_ATT(msg) __LOG(__LOG_LEVEL_ATT,(__FUNCSIG__,__FILE__,__LINE__,msg)) #define LOG_ERR(msg) __LOG(__LOG_LEVEL_ERR,(__FUNCSIG__,__FILE__,__LINE__,msg)) #define LOG_FATAL(msg) __LOG(__LOG_LEVEL_FATAL,(__FUNCSIG__,__FILE__,__LINE__,msg)) //+------------------------------------------------------------------+ //|| //+------------------------------------------------------------------+ class CTrueLogger { public: void Out(string func,string file,int line,string msg) { Print(func," ",func," ",file," ",line," ",msg); } } ExtTrueLogger; //+------------------------------------------------------------------+ //| Función de inicio del programa de script| //+------------------------------------------------------------------+ void OnStart() { LOG_MSG("Hello MSG world!"); LOG_ATT("Hello ATT world!"); LOG_ERR("Hello ERR world!"); LOG_FATAL("Hello FATAL world!"); }
El artículo sólo habla de DEBUG ASSERT, tenerlo a mano es bueno. El tema está cubierto.
Pero, ¡IMHO! Para los usuarios (uso a gran escala del material del artículo) no sólo necesitas DEBUG ASSERT, sino también un logger.
Un buen logger debe tener un nivel de logging:
- FATAL - ошибка, дальнейшее выполнение программы невозможно
- ERR - ошибка, выполнение программы можно продолжить
- ATT - предупреждение
- MSG - сообщение
Cuando el programa está depurando, DebugBreak se llama en el registrador - se puede detener y mirar el medio ambiente(estado) del programa MQL.
Cuando el programa se está ejecutando en el usuario final, los mensajes del registrador se guardan en un archivo(print/alert).
Por defecto, el logger solo genera errores ERR y FATAL y el usuario siempre puede cambiar el nivel de logging al ejecutar el programa para ver todos los mensajes del programa (ATT y MSG).
Si se usa correctamente, el log puede usarse para identificar/encontrar errores en el programa.
Justo en el próximo artículo (si Rashid lo aprueba) estoy planeando el tratamiento de los errores "esperados" ya en las versiones de lanzamiento del software (como una continuación lógica después de las aprobaciones), que incluirá la divulgación de la cuestión del registro.
Muchas gracias por estos dos comentarios, si no te importa, los utilizaré para este artículo.
En el próximo artículo (si Rashid lo aprueba) voy a cubrir los errores "esperados" en las versiones de lanzamiento del software (como una continuación lógica después de las aprobaciones), que también cubrirá el tema del registro.
Muchas gracias por estos dos comentarios, si no te importa, los utilizaré para este artículo.
Tema interesante.
Justo poco antes de leer este artículo estaba pensando por mi mismo en formas de detectar por precondiciones e interrumpir inmediatamente la ejecución del programa en caso de un posible bucle de código en uno de sus bloques.
Opps.
Al mismo tiempo, habiendo descargado el archivo assert.mqh, añadí una línea allí:
#define TEST_TEXT "Line: ",__LINE__,", ",__FUNCTION__,", "
Y luego en el código queda así:
Print(TEST_TEXT,"a = ",a);
Es decir, que y simplemente al construir el código para aplicar la salida de la información con la expectativa de que al final del trabajo en el código a continuación, esta salida de la información "de trabajo" puede ser fácilmente eliminado (como muchos, supongo, probablemente hizo y hace con la salida de la información en las etapas de construcción de código).
Tema interesante.
Justo poco antes de leer este artículo estaba pensando por mi mismo en formas de detectar por precondiciones e interrumpir inmediatamente la ejecución del programa en caso de un posible bucle de código en uno de sus bloques.
Opps.
Al mismo tiempo, habiendo descargado el archivo assert.mqh, añadí una línea allí:
Y luego en el código queda así:
Es decir, que y simplemente al construir el código para aplicar la salida de la información con la expectativa de que al final del trabajo en el código a continuación, esta salida de la información "de trabajo" puede ser fácilmente eliminado (como muchos, creo, probablemente hizo y hace con la salida de la información en las etapas de construcción de código).
Gracias por los comentarios.
Para que TEST_TEXT sea realmente fácil de eliminar mediante compilación condicional, yo consideraría poner Print dentro de la macro. En la versión actual, creo que es fácil de quitar TEST_TEXT, pero no los propios Prints.
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso
Artículo publicado Aserciones en los programas MQL5:
Este artículo explica cómo utilizar aserciones en el lenguaje MQL5. Proporciona dos mecanismos de aserción a modo de ejemplo, así como una guía para implementarlas correctamente.
Una aserción es una construcción especial que permite comprobar suposiciones especiales en lugares arbitrarios del programa. Suelen embeberse en el código, generalmente en una función separada o en una macro. El código comprueba el valor de una expresión determinada. Si es falso, se muestra el mensaje correspondiente y el programa se detiene de acuerdo a la implementación proporcionada. De igual modo, si la expresión es cierta, es decir, si la suposición se cumple, quiere decir que todo funciona según lo previsto. En caso contrario el programará detectará errores y avisará de ello.
Por ejemplo, si se espera que un determinado valor X sea menor que cero, en todos los casos, entonces podemos formar este predicado: "confirmo que el valor de X es mayor o igual a cero". Si resulta que X es menor que cero, mostramos un mensaje relevante para que el programador pueda ajustar el programa.
Las aserciones son especialmente útiles en los proyectos grandes, donde hay muchos componentes que se pueden reutilizar o modificar con el tiempo.
Las aserciones contemplan aquellas situaciones que no deberían ocurrir durante la ejecución normal del programa. Como norma general solo se aplican en las fases de desarrollo y depuración del programa, esto es, no tienen que estar presentes en la versión final. Las aserciones tienen que eliminarse durante la compilación de la versión final. Esto se consigue con la compilación condicional.
Fig. 1. Ejemplo de aserción
Autor: Sergey Eremin