Diskussion zum Artikel "Die Verwendung der Behauptung (assertions) bei der Entwicklung der Programme in MQL5"
1. Warum Makros? Sie sind unbequem, nicht alle Bedingungen können ihnen zugeführt werden, und es ist extrem schwierig, sie zu debuggen, wenn etwas schief geht. Es war einfacher, triviale Prozeduren zu implementieren.
2. Ein zu "schmutziger Trick" mit dem Array. Könnte man es nicht durch Null dividieren?
1. Warum Makros? Sie sind unbequem, nicht alle Bedingungen können ihnen zugeführt werden, und es ist extrem schwierig, sie zu debuggen, wenn etwas schief geht. Es war einfacher, triviale Prozeduren zu implementieren.
2. Ein zu "schmutziger Trick" mit dem Array. Könnte man es nicht durch Null dividieren?
1. Um nicht unsubstantiiert zu sein, zeigen Sie mir ein Beispiel für eine Bedingung, die nicht in mein Makro eingespeist werden kann (ich bin nicht sarkastisch, es ist wirklich wichtig für mich, über alle Feinheiten Bescheid zu wissen, da ich dieses Makro ständig benutze). Erläutern Sie also bitte, worin die Schwierigkeiten bei der Fehlersuche bestehen?
Und im Allgemeinen, ja, man kann es mit einer Prozedur machen, ich habe nur zwei mögliche Beispiele gezeigt. Aber um fair zu sein, ich kenne keinen eleganten Weg, um all diese Daten in einer Prozedur zu erhalten:
- Der Text des Ausdrucks, der an die Prüfung übergeben wird(#condition).
- Der Name der Quellcode-Datei, aus der das Makro aufgerufen wurde(__FILE__).
- Die Signatur der Funktion oder Methode, aus der das Makro aufgerufen wurde(__FUNCSIG__).
- Zeilennummer in der Quellcodedatei, in der sich der Makroaufruf befindet(__LINE__).
Ich wäre Ihnen sehr dankbar (und ich bin wahrscheinlich nicht der Einzige), wenn Sie mir Ihre Variante in Form einer Prozedur zeigen könnten, die all dies implementieren würde (natürlich "out of the box" und auf der Maschine, und nicht durch manuelle Übergabe all dieser Parameter). Im Prinzip kann 2...4 als Eingabeparameter übergeben werden , und es wird mehr oder weniger universell sein (in dem Sinne, dass es immer das Gleiche sein wird, was übergeben wird, man wird nicht manuell etwas einstellen müssen), aber wie bekommt man Element. 1 in der Prozedur zu bekommen, habe ich überhaupt keine Ideen
Plus alle die gleichen in der Regel, wie in der gleichen C + +, Anweisungen sind auf Makros geschrieben, auf die gleiche Weise und ich ging den gleichen Weg. Der einzige Schwachpunkt, den ich sehe: wenn ein Eingabeparameter oder eine Variable mit dem Namen x in der Prozedur/Funktion deklariert wird, in der wir ein solches Makro verwenden, erhalten wir eine Warnung. Die Lösung ist einfach: im Makro benennen Sie das Array etwas eindeutiger, zum Beispiel assertionFailedArray.
2. Ich sehe den Unterschied nicht. Ein Ausführungsfehler ist ein Ausführungsfehler, er führt zum Absturz des Programms und es wird nicht weiter ausgeführt. Aber ich sage Ihnen, warum ich diesen Weg gegangen bin: Zuerst war es Division durch Null, aber als ich ein solches Makro getestet habe, wurde die Codeausführung aus irgendeinem Grund nicht unterbrochen, wenn es in Methoden aufgerufen wurde. Wenn es in OnTick, OnInit, etc. aufgerufen wurde, dann ja, wurde die Ausführung angehalten. Wenn es innerhalb einer Methode einer beliebigen Klasse aufgerufen wurde, dann nicht. Ob es sich um einen MQL5-Fehler handelte, habe ich nicht untersucht, ich habe einfach einen anderen Ausführungsfehler aufgerufen :).
Ich werde versuchen zu sehen, was mit der Division durch Null in Methoden falsch ist.
#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: //---- Konstruktor CFormatOut(int dbl_fmt_digits=4,bool auto_space=false):m_dbl_fmt("%."+(string)dbl_fmt_digits+"f") { } //--- Ausgabedaten 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)); } //--- Ausgabe am Ende der Zeile (echte Ausgabe/Aufruf der Druckfunktion) CFormatOut *operator<<(CFormatOutEol &eol) { Print(m_line); m_line=NULL; return(GetPointer(this)); } //--- Ausgabeformat für reelle Zahlen ändern CFormatOut *operator<<(CFormatOutFmtDigits &fmt) { m_dbl_fmt="%."+(string)fmt.digits+"f"; return(GetPointer(this)); } //--- Einfügen/Entfernen der automatischen Leerzeicheneinfügung 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; //--- Spezielles Objekt zum Einfügen von EndOfLine in die Ausgabe CFormatOutEol EOL; //--- Einstellung der Ziffern für die Zahlenausgabe CFormatOutFmtDigits DBL_FMT_DIGITS(int digits) { CFormatOutFmtDigits fmt; fmt.digits=digits; return(fmt); } //--- Ein/Ausschalten des Einfügens von Leerzeichen zwischen den Ausgaben CFormatOutFmtSpace AUTO_SPACE(bool enable) { CFormatOutFmtSpace fmt; fmt.space =enable; return(fmt); } //--- Shorty-Funktion zur Konvertierung von Enums in String template<typename T> string EN(T enum_value) { return(EnumToString(enum_value)); }Verwendung:
OUT << AUTO_SPACE(true) << M_PI << "Test" << DBL_FMT_DIGITS(6) << M_PI << EN(PERIOD_M1) << EOL;Ergebnis:
2015.09.01 18:04:49.060 Test EURUSD,H1: 3.1416 Test 3.141593 PERIOD_M1
ACHTUNG: Die Parameter des Ausdrucks OUT << ... in umgekehrter Reihenfolge, von rechts nach links, ist ein Nebeneffekt möglich!
Ich weiß nicht, warum (wir reden hier über Debugging), also lasse ich diesen Code einfach hier stehen:
Wenn Sie in Ihrem Code angeben können, wohin die Ausgabe erfolgen soll (in das Protokoll über Print, in den Alert, in eine Datei usw.), könnte dies noch nützlicher sein, wie mir scheint. Zumal es gar nicht so schwierig ist, dies zu tun.
P.S. Darf ich den Artikel kritisieren/lobend erwähnen? :)
Wenn Sie in Ihrem Code angeben können, wohin die Ausgabe erfolgen soll (in das Protokoll über Print, in den Alert, in eine Datei usw.), scheint mir dies noch nützlicher zu sein. Zumal es gar nicht so schwierig ist, dies zu tun.
P.S. Darf ich den Artikel kritisieren/lobend erwähnen? :)
Aber, IMHO! Für Benutzer (die das Material des Artikels im großen Stil nutzen) brauchen Sie nicht nur DEBUG ASSERT, sondern auch einen Logger.
Ein guter Logger sollte einen Logging-Level haben:
- FATAL - ошибка, дальнейшее выполнение программы невозможно
- ERR - ошибка, выполнение программы можно продолжить
- ATT - предупреждение
- MSG - сообщение
Wenn das Programm debuggt, wird DebugBreak im Logger aufgerufen - man kann anhalten und sich die Umgebung (den Zustand) des MQL-Programms ansehen.
Wenn das Programm beim Endbenutzer läuft, werden die Logger-Meldungen in einer Datei gespeichert (print/alert).
Standardmäßig gibt der Logger nur ERR- und FATAL-Fehler aus. Der Benutzer kann die Logging-Ebene bei der Ausführung des Programms jederzeit ändern, um alle Meldungen des Programms (ATT und MSG) zu sehen.
Bei richtiger Verwendung kann der Logger zur Identifizierung/Findung eines Fehlers im Programm verwendet werden.
#property script_show_inputs enum EnLogLevel { __LOG_LEVEL_FATAL, // nur schwerwiegende Fehler __LOG_LEVEL_ERR, // nur Fehler __LOG_LEVEL_ATT, // Warnungen und Fehler __LOG_LEVEL_MSG, // alle Meldungen }; input EnLogLevel LogLevel=__LOG_LEVEL_MSG; // Logger-Ebene //+------------------------------------------------------------------+ //|| //+------------------------------------------------------------------+ #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; //+------------------------------------------------------------------+ //| Skript-Programmstartfunktion| //+------------------------------------------------------------------+ void OnStart() { LOG_MSG("Hello MSG world!"); LOG_ATT("Hello ATT world!"); LOG_ERR("Hello ERR world!"); LOG_FATAL("Hello FATAL world!"); }
Der Artikel bespricht nur DEBUG ASSERT, es ist gut, es zur Hand zu haben. Das Thema wird behandelt.
Aber, IMHO! Für Benutzer (die das Material des Artikels in großem Umfang nutzen) brauchen Sie nicht nur DEBUG ASSERT, sondern auch einen Logger.
Ein guter Logger muss einen Logging-Level haben:
- FATAL - ошибка, дальнейшее выполнение программы невозможно
- ERR - ошибка, выполнение программы можно продолжить
- ATT - предупреждение
- MSG - сообщение
Wenn das Programm debuggt, wird DebugBreak im Logger aufgerufen - Sie können anhalten und die Umgebung (den Zustand) des MQL-Programms betrachten.
Wenn das Programm beim Endbenutzer läuft, werden die Logger-Meldungen in einer Datei gespeichert (print/alert).
Standardmäßig erzeugt der Logger nur ERR- und FATAL-Fehler, und der Benutzer kann die Logging-Ebene bei der Ausführung des Programms jederzeit ändern, um alle Meldungen des Programms (ATT und MSG) zu sehen.
Bei richtiger Verwendung kann das Log verwendet werden, um Fehler im Programm zu identifizieren/zu finden.
Gerade im nächsten Artikel (wenn Rashid zustimmt) plane ich die Verarbeitung von "erwarteten" Fehlern bereits in Release-Versionen von Software (als logische Fortsetzung nach der Freigabe), die die Offenlegung des Protokollierungsproblems beinhalten wird.
Vielen Dank für diese beiden Kommentare, wenn es Ihnen nichts ausmacht, werde ich sie für diesen Artikel verwenden.
Im nächsten Artikel (wenn Rashid zustimmt) werde ich die "erwarteten" Fehler in den Release-Versionen der Software behandeln (als logische Fortsetzung nach den Genehmigungen), die auch die Frage der Protokollierung abdecken werden.
Vielen Dank für diese beiden Kommentare, wenn es Ihnen nichts ausmacht, werde ich sie für diesen Artikel verwenden.
Interessantes Thema.
Kurz bevor ich diesen Artikel gelesen habe, habe ich selbst über Möglichkeiten nachgedacht, wie man eine mögliche Codeschleife in einem der Blöcke anhand von Vorbedingungen erkennen und die Programmausführung sofort unterbrechen kann.
Opps.
Gleichzeitig habe ich die Datei assert.mqh heruntergeladen und dort eine Zeile eingefügt:
#define TEST_TEXT "Line: ",__LINE__,", ",__FUNCTION__,", "
Und dann sieht es im Code so aus:
Print(TEST_TEXT,"a = ",a);
Das heißt, dass und einfach bei der Konstruktion des Codes, um die Ausgabe von Informationen mit der Erwartung, dass bis zum Ende der Arbeit an den Code dann diese Ausgabe von "Arbeits"-Informationen kann leicht entfernt werden (wie viele, nehme ich an, wahrscheinlich tat und tut mit der Ausgabe von Informationen in den Stadien der Code-Konstruktion) gelten.
Interessantes Thema.
Kurz bevor ich diesen Artikel gelesen habe, habe ich selbst über Möglichkeiten nachgedacht, wie man eine mögliche Codeschleife in einem der Blöcke anhand von Vorbedingungen erkennen und die Programmausführung sofort unterbrechen kann.
Opps.
Gleichzeitig habe ich die Datei assert.mqh heruntergeladen und dort eine Zeile eingefügt:
Und dann sieht es im Code so aus:
Das heißt, dass und einfach bei der Konstruktion des Codes, um die Ausgabe von Informationen mit der Erwartung, dass bis zum Ende der Arbeit an den Code dann diese Ausgabe von "Arbeits"-Informationen kann leicht entfernt werden (wie viele, ich glaube, wahrscheinlich tat und tut mit der Ausgabe von Informationen in den Stadien der Code-Konstruktion) gelten.
Vielen Dank für das Feedback!
Damit TEST_TEXT wirklich leicht durch bedingte Kompilierung entfernt werden kann, würde ich in Erwägung ziehen, Print in das Makro aufzunehmen. In der aktuellen Version ist es meiner Meinung nach einfach, TEST_TEXT zu entfernen, aber nicht die Prints selbst.

- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.
Neuer Artikel Die Verwendung der Behauptung (assertions) bei der Entwicklung der Programme in MQL5 :
In diesem Artikel wird Behauptung (assertions) im Rahmen der Sprache MQL5 betrachtet. Es werden zwei Beispiele für die Realisierung des Behauptungsmechanismus geben, sowie allgemeine Empfehlungen für die Verwendung der Behauptungen.
Eine voll funktionsfähige Makro assert wird nach folgender Art und Weise gebaut. Zunächst wird ein eingehender Ausdruck condition überprüft. Wenn es falsch ist, dann wird eine Nachricht fullMessage gebildet und angezeigt. Die Nachricht fullMessage wird aus den folgenden Elementen aufgebaut:
Nach der Lieferung der Meldung(Alert) wird in dem zweiten Makrotyp versucht, einen Wert zu einem nicht existierenden Array-Element zuzuweisen, dies führt zu einem Fehler bei der Ausführungszeit und bewirkt so, dass das Programm direkt abstürzt.
Dieses Verfahren, das Programm zu beenden hat Nebenwirkungen für die Indikatoren, die in ihren Unterfenster arbeiten: ihre Unterfenster bleiben im Terminal, und deshalb müssen sie manuell geschlossen werden. Außerdem können Artefakte in Form von nicht entfernten grafischen Objekten, globale Variablen des Terminals, Dateien, etc. sein, die während der Arbeit des Programms erstellt wurden, bis das abgestürzt ist. Wenn dieses Verhalten vollständig inakzeptabel ist, dann sollte die erste Makro verwendet werden.
Autor: Sergey Eremin