1.为什么要使用宏?宏很不方便,不是所有的条件都可以输入宏,而且如果宏出了问题,调试起来也非常困难。实现琐碎的程序更容易。
2.对数组使用了一些太 "肮脏的手段"。不能除以零吗?
1.为什么要使用宏?宏很不方便,不是所有的条件都可以输入宏,而且如果宏出了问题,调试起来也非常困难。实现琐碎的程序更容易。
2.对数组使用了一些太 "肮脏的手段"。不能除以零吗?
1.为了避免证据不足,请举例说明我的宏无法输入的条件(我不是在讽刺你,了解所有细微之处对我来说真的很重要,因为我一直在使用这个宏)。那么,请解释一下调试的困难在哪里?
一般来说,是的,你可以用存储过程来调试,我只举了两个例子。但公平地说,我不知道有什么优美的方法可以在存储过程中获取所有这些数据:
- 传递给检查的表达式文本(#condition)。
- 调用宏的源代码文件名(__FILE__)。
- 调用宏的函数或方法的签名(__FUNCSIG__)。
- 宏调用所在源代码文件的行号(__LINE__)。
如果您能以存储过程的形式向我展示您的变体,以实现所有这些功能(当然,是在机器上 "开箱即用",而不是手动将所有这些作为参数传递),我将不胜感激(我可能不是唯一一个这样做的人)。原则上,2...4可以 作为输入参数传递,而且或多或少具有通用性(也就是说,传递的总是同一件事,不需要手动设置什么),但如何在我的程序中获得项目 1 呢?1,我完全不知道。
另外,所有的语句通常都是一样的,就像在 C++ 中一样,语句都是写在宏上的,写法和我走的路是一样的。我发现的唯一弱点是:如果在使用宏的存储过程/函数中声明了名为x 的输入参数或变量,就会收到警告。解决方法很简单:在宏中给数组取一个更独特的名字,例如assertionFailedArray。
2.我看不出有什么区别。执行错误就是执行错误,它会导致程序崩溃,无法继续执行。不过,我还是要回答一下为什么要这样做:起初是除以 0,但我在测试这样一个宏时,由于某种原因,在方法中调用它时代码执行不会中断。如果是在 OnTick、OnInit 等方法中调用,那么是的,执行会停止。如果在任意类的某个方法中调用,则不会。至于是否是 MQL5 的错误,我没有费心去研究,我只是开始调用另一个执行错误 :).
我会试着看看在方法中除以 0 有什么问题。
#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: //---- 构造函数 CFormatOut(int dbl_fmt_digits=4,bool auto_space=false):m_dbl_fmt("%."+(string)dbl_fmt_digits+"f") { } //--- 输出数据 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)); } //--- 行尾输出(实际输出/调用打印函数) CFormatOut *operator<<(CFormatOutEol &eol) { Print(m_line); m_line=NULL; return(GetPointer(this)); } //--- 更改实数的输出格式 CFormatOut *operator<<(CFormatOutFmtDigits &fmt) { m_dbl_fmt="%."+(string)fmt.digits+"f"; return(GetPointer(this)); } //--- 添加/删除自动空格嵌入符 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; //--- 在输出中插入结束行的特定对象 CFormatOutEol EOL; //--- 设置数字输出的位数 CFormatOutFmtDigits DBL_FMT_DIGITS(int digits) { CFormatOutFmtDigits fmt; fmt.digits=digits; return(fmt); } //--- 开/关在输出之间插入空格 CFormatOutFmtSpace AUTO_SPACE(bool enable) { CFormatOutFmtSpace fmt; fmt.space =enable; return(fmt); } //--- 将枚举转换为字符串的简易函数 template<typename T> string EN(T enum_value) { return(EnumToString(enum_value)); }使用:
OUT << AUTO_SPACE(true) << M_PI << "Test" << DBL_FMT_DIGITS(6) << M_PI << EN(PERIOD_M1) << EOL;Result:
2015.09.01 18:04:49.060 Test EURUSD,H1: 3.1416 Test 3.141593 PERIOD_M1
注意: 表达式 OUT << ... 的参数顺序相反,从右向左,可能会产生副作用的参数从右到左顺序相反,可能会产生副作用!
如果您能在代码中指定输出位置(通过 Print 输出到日志、警报、文件等),在我看来可能会更有用。尤其是这样做并不难。
附:我能批评/赞扬一下这篇文章吗?:)
但是,IMHO!
一个好的日志记录器应该有一个日志记录级别:
- FATAL - ошибка, дальнейшее выполнение программы невозможно
- ERR - ошибка, выполнение программы можно продолжить
- ATT - предупреждение
- MSG - сообщение
当程序正在调试 时,日志记录器会调用 DebugBreak- 您可以停止并查看 MQL 程序的环境(状态)。
当程序在终端用户处运行时,日志记录器信息会保存到文件(打印/警报)中。
默认情况下,日志只输出 ERR 和 FATAL 错误,用户可以在运行程序时更改日志级别,以查看程序的所有信息(ATT 和 MSG)。
如果使用得当,日志可用于识别/查找程序中的错误。
#property script_show_inputs enum EnLogLevel { __LOG_LEVEL_FATAL, // 只有致命错误 __LOG_LEVEL_ERR, // 只有错误 __LOG_LEVEL_ATT, // 警告和错误 __LOG_LEVEL_MSG, // 所有信息 }; input EnLogLevel LogLevel=__LOG_LEVEL_MSG; // 记录仪级别 //+------------------------------------------------------------------+ //|| //+------------------------------------------------------------------+ #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; //+------------------------------------------------------------------+ //| 脚本程序启动功能| //+------------------------------------------------------------------+ void OnStart() { LOG_MSG("Hello MSG world!"); LOG_ATT("Hello ATT world!"); LOG_ERR("Hello ERR world!"); LOG_FATAL("Hello FATAL world!"); }
这篇文章只讨论了 DEBUG ASSERT,有它在手是件好事。 但是,IMHO! 一个好的日志记录器必须有日志记录级别。日志记录级别可由 MQL 程序参数控制:
- FATAL - ошибка, дальнейшее выполнение программы невозможно
- ERR - ошибка, выполнение программы можно продолжить
- ATT - предупреждение
- MSG - сообщение
当程序正在调试 时,日志记录器会调用 DebugBreak- 您可以停止并查看 MQL 程序的环境(状态)。
当程序在最终用户处运行时,日志记录器信息会保存到文件(打印/警报)中。
默认情况下,日志只生成 ERR 和 FATAL 错误,用户可以在运行程序时更改日志级别,以查看程序的所有信息(ATT 和 MSG)。
如果使用得当,日志可用于识别/查找程序中的错误。
就在下一篇文章中(如果 Rashid 同意),我计划对已发布版本软件中的 "预期 "错误进行处理(作为批准后的逻辑延续),其中将包括日志问题的披露。
非常感谢你的这两条评论,如果你不介意的话,我将把它们用于这篇文章。
在下一篇文章中(如果拉希德同意),我将介绍软件发布版本中的 "预期 "错误(作为批准后的逻辑延续),其中也将涉及日志问题。
非常感谢你的这两条意见,如果你不介意的话,我将把它们用于这篇文章。
新文章 在MQL5程序中使用断言已发布:
本文介绍MQL5语言中断言的使用。给出了关于断言机制的两个例子以及实现断言的一些总体指导。
断言是一种特殊的结构,使程序能够在任何地方对任意的假设进行检查。它们通常以代码的形 式被包含在程序中(大多数情况下作为独立的函数或者宏)。代码检查特定的表达式是否为真。如果为假,则显示一条相应的消息,如果有需要可使程序停止运行。 如果表达式为真,这说明所有的操作都在计划中 — 假设被实现。否则,你可以肯定程序出现错误,并且有关于此次报错的清晰提示。
例如,如果预期特定的值X在任何情况下都不应该小于零,则可以做出下面的声明:“我确定X的值超过或者等于零”。如果X小于零,那么一个相关信息将会显示,程序员就可以根据其来调整程序。
断言在大型项目中尤其有用,其组成部件可以重复使用或修改。
断言应仅包括在正常运行过程中程序不应该出现的情况。作为一个共识,断言只能存在于程序的开发和调试阶段,例如,它们不能出现在程序的最终版本中。所有断言必须在程序的最终版编译时删除。这一般通过条件编译来实现。
图 1. 一个断言的例子
作者:Sergey Eremin