1. Why macros? They are inconvenient, not all conditions can be fed to them, and it is extremely difficult to debug them if something goes wrong. It was easier to implement trivial procedures.
2. Some too "dirty trick" with the array. Couldn't you divide it by zero?
1. Why macros? They are inconvenient, not all conditions can be fed to them, and it is extremely difficult to debug them if something goes wrong. It was easier to implement trivial procedures.
2. Some too "dirty trick" with the array. Couldn't you divide it by zero?
1. In order not to be unsubstantiated, show me an example of a condition that cannot be fed to my macro (I'm not being sarcastic, it's really important for me to know about all the subtle points, as I use this macro all the time). So, please explain what are the difficulties of debugging?
And in general, yes, you can do it with a procedure, I showed only two possible examples. But to be fair, I don't know a graceful way to get all this data in a procedure:
- The text of the expression passed to the check(#condition).
- The name of the source code file from which the macro was called(__FILE__).
- Signature of the function or method from which the macro was called(__FUNCSIG__).
- Line number in the source code file on which the macro call is located(__LINE__).
I would be very grateful (and I am probably not the only one) if you could show me your variant in the form of a procedure that would implement all this (of course, "out of the box" and on the machine, and not by manually passing all this as parameters). In principle, 2...4 can be passed as input parameters, and it will be more or less universal (in the sense that it will always be the same thing passed, you will not need to manually set something), but how to get item. 1 to get in the procedure I have no ideas at all
Plus all the same usually, as in the same C++, statements are written on macros, on the same way and I went the same way. The only weak point I see: if an input parameter or variable named x is declared in the procedure/function where we use such a macro, we will get a warning. The solution is simple: in the macro name the array something more unique, for example assertionFailedArray.
2. I don't see the difference. An execution error is an execution error, it will crash the programme and it will not be executed further. However, I'll answer why I went this way: at first it was division by zero, but when I was testing such a macro, for some reason code execution was not interrupted when calling it in methods. If it was called in OnTick, OnInit, etc., then yes, the execution stopped. If inside some method of an arbitrary class, then no. Whether it was an MQL5 error, I did not bother to look into it, I just started calling another execution error :).
I will try to see what is wrong with division by zero in methods.
#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") { } //--- output data 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)); } //--- output end of line (real output/call Print function) CFormatOut *operator<<(CFormatOutEol &eol) { Print(m_line); m_line=NULL; return(GetPointer(this)); } //--- change output format for real numbers 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; //--- specal object for inserting EndOfLine to output CFormatOutEol EOL; //--- setting digits for numbers output CFormatOutFmtDigits DBL_FMT_DIGITS(int digits) { CFormatOutFmtDigits fmt; fmt.digits=digits; return(fmt); } //--- on/off inserting spaces between outputs CFormatOutFmtSpace AUTO_SPACE(bool enable) { CFormatOutFmtSpace fmt; fmt.space =enable; return(fmt); } //--- shorty function to convert enums to string template<typename T> string EN(T enum_value) { return(EnumToString(enum_value)); }Usage:
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
CAUTION: The parameters of the expression OUT << ... in reverse order, from right to left, a side effect is possible!
I don't know why (we're talking about debugging), so I'll just leave this code here:
If in your code you can specify where to output (to the log via Print, to the alert, to a file, etc.), it may be even more useful, it seems to me. Especially since it is not difficult to do it at all.
P.S. can I criticise/praise the article? :)
If in your code you can specify where to output (to the log via Print, to the alert, to a file, etc.), it may be even more useful, it seems to me. Especially since it is not difficult to do it at all.
P.S. can I criticise/praise the article? :)
But, IMHO! For users (large-scale use of the article material) you need not only DEBUG ASSERT, but also a logger.
A good logger should have a logging level:
- FATAL - ошибка, дальнейшее выполнение программы невозможно
- ERR - ошибка, выполнение программы можно продолжить
- ATT - предупреждение
- MSG - сообщение
When the program is debugging, DebugBreak is called in the logger - you can stop and look at the environment(state) of the MQL program.
When the program is running at the end user, the logger messages are saved to a file(print/alert).
By default, the logger outputs only ERR and FATAL errors and the user can always change the logging level when running the programme to see all the messages of the programme (ATT and MSG).
If used correctly, the log can be used to identify/find an error in the programme.
#property script_show_inputs enum EnLogLevel { __LOG_LEVEL_FATAL, // fatal errors only __LOG_LEVEL_ERR, // errors only __LOG_LEVEL_ATT, // warnings and errors __LOG_LEVEL_MSG, // all messages }; input EnLogLevel LogLevel=__LOG_LEVEL_MSG; // logger level //+------------------------------------------------------------------+ //|| //+------------------------------------------------------------------+ #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; //+------------------------------------------------------------------+ //| Script programme start function| //+------------------------------------------------------------------+ void OnStart() { LOG_MSG("Hello MSG world!"); LOG_ATT("Hello ATT world!"); LOG_ERR("Hello ERR world!"); LOG_FATAL("Hello FATAL world!"); }
The article only discusses DEBUG ASSERT, having it on hand is good. The topic is covered.
But, IMHO! For users (large-scale use of the article's material) you need not only DEBUG ASSERT, but also a logger.
A good logger must have a logging level:
- FATAL - ошибка, дальнейшее выполнение программы невозможно
- ERR - ошибка, выполнение программы можно продолжить
- ATT - предупреждение
- MSG - сообщение
When the program is debugging, DebugBreak is called in the logger - you can stop and look at the environment(state) of the MQL program.
When the program is running at the end user, the logger messages are saved to a file(print/alert).
By default, the logger generates only ERR and FATAL errors and the user can always change the logging level when running the programme to see all the messages of the programme (ATT and MSG).
If used correctly, the log can be used to identify/find errors in the programme.
Just in the next article (if Rashid approves) I am planning the processing of "expected" errors already in release versions of software (as a logical continuation after approvals), which will include the disclosure of the logging issue.
Thank you very much for these two comments, if you don't mind, I will use them for this article.
In the next article (if Rashid approves) I am going to cover "expected" errors in the release versions of the software (as a logical continuation after the approvals), which will also cover the issue of logging.
Thank you very much for these two comments, if you don't mind, I'll use them for this article.
Interesting topic.
Just shortly before reading this article I was thinking for myself about ways of detecting by preconditions and immediately interrupting program execution in case of a possible code loop in one of its blocks.
Opps.
At the same time, having downloaded the assert.mqh file, I added a line there:
#define TEST_TEXT "Line: ",__LINE__,", ",__FUNCTION__,", "
And then in the code it looks like this:
Print(TEST_TEXT,"a = ",a);
That is, that and simply when constructing the code to apply the output of information with the expectation that by the end of work on the code then this output of "working" information can be easily removed (as many, I suppose, probably did and do with the output of information at the stages of code construction).
Interesting topic.
Just shortly before reading this article I was thinking for myself about ways of detecting by preconditions and immediately interrupting program execution in case of a possible code loop in one of its blocks.
Opps.
At the same time, having downloaded the assert.mqh file, I added a line there:
And then in the code it looks like this:
That is, that and simply when constructing the code to apply the output of information with the expectation that by the end of work on the code then this output of "working" information can be easily removed (as many, I believe, probably did and do with the output of information at the stages of code construction).
Thanks for the feedback!
For TEST_TEXT to be really easy to remove by conditional compilation, I would consider putting Print inside the macro. In the current version, I think it is easy to remove TEST_TEXT, but not the Prints themselves.

- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
New article Using Assertions in MQL5 Programs has been published:
This article covers the use of assertions in MQL5 language. It provides two examples of the assertion mechanism and some general guidance for implementing assertions.
Assertion is a special construction that enables checking arbitrary assumptions in the program's arbitrary places. They are typically embodied in the form of a code (mostly as a separate function or macro). This code checks a true value of a certain expression. If it appears to be false, then a relevant message is displayed, and the program is stopped, given the implementation provides for it. Accordingly, if the expression is true, it implies that everything operates as intended - the assumption is met. Otherwise, you can be certain, that the program has located errors and is clearly notifying about it.
For example, if it's expected that a certain value X within the program under no circumstance should be less than zero, then the following statement can be made: "I confirm that a value of X exceeds or equals zero". If X happens to be less than zero, then a relevant message will be displayed, and a programmer will be able to adjust the program.
Assertions are particularly useful in big projects, where their component parts may be reused or modified with time.
Assertions should cover only those situations that shouldn't occur during the program's regular operation. As a rule, assertions can be applied only at the program's development and debugging stages, i.e. they shouldn't be present in the final version. All assertions must be removed during the final version's compilation. This is usually achieved through conditional compilation.
Fig. 1. Example of an assertion
Author: Sergey Eremin