#property version "5.03" /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Include debugging library // // Select operational mode: // // - #define LIB_DEBUG // -> This will disable LIB_PERF_PROFILING and enable DEBUGGING extensions // // - #define LIB_MQLAPI_TRACE // -> This will enable the MQL API tracer module. Every call to an MQL API fnction will be // logged to expert-journal. Also tracing of the _LastError is done. // // - #define LIB_PERF_PROFILING // -> This will enable PERF_PROFILING macros. Only available in "runtime"-mode compilation. // If trying to measure these metrics while "_DEBUG" or "LIB_DEBUG" is defined will not work. // // Define these before you include the library, as they define the included parts of the library. // // LIB_DEBUG, LIB_DEBUG_AUTOENABLE, LIB_DEBUG_MQLAPI_TRACE and // LIB_MQLAPI_TRACE are superior to LIB_PERF_PROFILING and will disable LIB_PERF_PROFILING. // // // Additional options for LIB_DEBUG // // LIB_DEBUG_AUTOENABLE // // This flag will make the library switch between runtime and debugging mode, depending on // the compiler target. - This will imply LIB_DEBUG automatically as appropiate. // // LIB_DEBUG_MQLAPI_TRACE // // MQL API function call tracing support. This will log calls to built-in functions. // "LIB_DEBUG_MQLAPI_TRACE" will enable this feature. YOu will get trace output, if you have // LIB_DEBUG built-in tracing for the particular function enabled. - See example below on how to // make a function traceable. // // Functions in which the tracing is activated, see description below, will now // also log calls to MQL-API functions. The error code and return type will be logged. // // If you wish this feature to be enabled regardless of function tracing, you may define the flag // "LIB_MQLAPI_TRACE". Now all calls to MQL-API get logged and checked. If this is // activated, "LIB_DEBUG_MQLAPI" is implicated automatically, if LIB_DEBUG or LIB_DEBUG_AUTOENABLE // is active. // // LIB_DEBUG_LOGFILE // // Debugging library supports output logging to file. This feature can be enabled by defining // "LIB_DEBUG_LOGFILE" as shown below. (Automatically also defines LIB_DEBUG) // Logs will be written to Common/* directory. // // This feature supports debug logging within optimizer runs of the program. // // LIB_DEBUG_NO_JOURNAL_OUTPUT // // Additionally the expert-journal output can be surpressed, separating the logs into two // targets. - This way logging debug output and logging terminal output is separated. // // Define "LIB_DEBUG_NO_JOURNAL_OUTPUT" to disable expert-journal output from this library. // // // Additional options for LIB_DEBUG_MQLAPI_TRACE and LIB_MQLAPI_TRACE // // To always print input parameters to a function, define // LIB_MQLAPI_TRACE_SHOW_PARAMS. // // This will force printing of all input parameters // which are not output parameters only. // // LIB_MQLAPI_TRACE_SHOW_RESULT // // This will force printing of results from the function call, including // current error state // // // !!! IMPORTANT NOTE !!! // // MQLAPI_TRACE and LIB_DEBUG_MQLAPI_TRACE is incompatible with the standard library from MQL. // Work to resolve this issue is underway, but might take quite a while. // // Until then, if you are using any includes from standard library, you cannot activate // the MQLAPI tracer module. - You will get compiler errors. // // Same goes for any function you created on your own, that shares a name with any of // the functions provided by MQL-API. // // The tracer uses macro substitution to replace and intercept the calls to the API. // Therefore any function with the same name as listed in the file // "MQLplus/lib_debug/lib_debug_mqlapi_tracer_overwrite_macros.mqh" // will confuse the compiler and library. You will not be able to compile your code. // // If it is mandatory for you to do so anyways, you need to setup a custom config // for your project. - See below. // // !!! END !!! // // // Customization for project specific needs // // If you wish to customize the library, this can be done easily. Procedure is described in // "MQLplus/lib_debug/lib_debug_CustomConfig.mqh" // // See details in that file to customize lib_debug to your needs. // // // /////////////////////////////////////////////////////////////////////// // // Functions defined before include of lib_debug // template int MyArraySort(T& array[]) { return(0); }; void MyPersonalVoidFunc(int arg01, int arg02, int arg03, int arg04, int arg05, int arg06, datetime arg07, string& arg08, color arg09, double arg10) { return; }; ///////////////////////////////////////// // // Include the library // //#define LIB_DEBUG //#define LIB_DEBUG_AUTOENABLE //#define LIB_DEBUG_LOGFILE //#define LIB_DEBUG_NO_JOURNAL_OUTPUT //#define LIB_DEBUG_MQLAPI_TRACE #define LIB_MQLAPI_TRACE //#define LIB_MQLAPI_TRACE_PERFORMANCE //#define LIB_MQLAPI_TRACE_SHOW_PARAMS //#define LIB_MQLAPI_TRACE_SHOW_RESULT //#define LIB_PERF_PROFILING #include "lib_debug_CustomConfig.mqh" //#include /////////////////////////////////////////////////////////////////////// // // In OnInit you can see a simple usage of the debugging macros. // // We will only use some stand-alone macros for quick debugging. // // DBG_MSG() // DBG_MSG_VAR() // DBG_MSG_VAR_IF() // DBG_MSG_PERSIST() // DBG_MSG_BITS() // DBG_MSG_ERRCODE() // // These macros support all built-in datatypes (also in form of arrays). // (unsigned) char, short, int, long // bool, datetime, color, complex, matrix(f/c), vector(f/c), // float, double, // string, enum, struct, class and interface as well as pointers. // // // And MQL structures: // MqlDateTime, MqlRates, MqlTick, MqlParam, MqlBookInfo, // MqlTradeRequest, MqlTradeCheckResult, MqlTradeResult, // MqlTradeTransaction, // MqlCalendarCountry, MqlCalendarEvent, MqlCalendarValue, // DXVector, DXVertexLayout // //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { ////////////////////////////////////////////////////////////// // // Custom function tracing, see // lib_debug/lib_debug_CustomConfig.mqh // double some_array[]; int return_val = MyArraySort(some_array); ////////////////////////////////////////////////////////////// // // Simple output macros // // Some initial debug message DBG_MSG("Example how to use lib_debug.mqh"); // Output details about current program // Available in debugging and runtime printf("%s", DBG_STR_EX45_FILEINFO); // Keep this message also in runtime builds DBG_MSG_PERSIST("Persistent message in debug and runtime (Runtime is without debug details)"); ////////////////////////////////////////////////////////////// // // All basic output macros available // // Get output from library as string ulong test_var = LONG_MAX; string test = NULL; // Macros will be preserved in runtime version but resolve to ((string)NULL) test = DBG_STR("Test A"); test = DBG_STR_VAR(_LastError); test = DBG_STR_VAR(test_var); test = DBG_STR_VAR_IF(true, test_var); test = DBG_STR_PERSIST("Some message"); test = DBG_STR_BITS(test_var); test = DBG_STR_ERRCODE(5004); // These will be completley removed from runtime builds DBG_MSG("Test A"); DBG_MSG_VAR(_LastError); DBG_MSG_VAR(test_var); DBG_MSG_VAR_IF(true, test_var); DBG_MSG_PERSIST("Some message"); DBG_MSG_BITS(test_var); DBG_MSG_ERRCODE(5004); ////////////////////////////////////////////////////////////// // // IEEE754 printing // // Verify correct printing of IEEE754 data Print("Check floats:"); Print( 0.1f + 0.6f ); Print( 0.7f ); DBG_MSG(string((float)0.1 + (float)0.6)); DBG_MSG_VAR(0.1f + 0.6f); DBG_MSG_VAR(0.7f); DBG_MSG_VAR(FLT_MIN); DBG_MSG_VAR(FLT_MAX); Print("Check doubles:"); Print( 0.1 + 0.2); Print( 0.3 ); DBG_MSG(string(0.1 + 0.2)); DBG_MSG_VAR(0.1 + 0.2); DBG_MSG_VAR(0.3); DBG_MSG_VAR(DBL_MIN); DBG_MSG_VAR(DBL_MAX); // Direct value printing DBG_MSG_BITS((uint)123); DBG_MSG_VAR(PRICE_CLOSE); DBG_MSG_VAR(1); DBG_MSG_VAR(true); DBG_MSG_VAR(TimeCurrent()); DBG_MSG_VAR(clrBlue); DBG_MSG_VAR("Print Me"); ////////////////////////////////////////////////////////////// // // The flexibility of DBG_MSG_VAR and DBG_STR_VAR // // Output primitive variables enum custom_enum { SOME_ENUM_VAL1, SOME_ENUM_VAL2 }; custom_enum my_enum = SOME_ENUM_VAL2; ENUM_APPLIED_PRICE e_val = PRICE_CLOSE; char c_val = 1; uchar uc_val = 2; short s_val = 3; int i_val = 4; long l_val = 5; bool b_val = true; datetime d_val = TimeCurrent(); float flt_val = (float)1.61; double dbl_val = 3.14; color clr_val = clrAqua; string str_val = "Some string"; complex cmplx_val = { 0.1, 0.2 }; vector v_val = { 0, 1, 2, 3 }; vectorf vf_val = { (float)0.1, (float)1.1, (float)2.1, (float)3.1 }; matrix m_val (1, 2); matrixf mf_val (1.3, 2.3); DBG_MSG_VAR(my_enum); DBG_MSG_VAR(e_val); DBG_MSG_VAR(c_val); DBG_MSG_VAR(uc_val); DBG_MSG_VAR(s_val); DBG_MSG_VAR(i_val); DBG_MSG_VAR(l_val); DBG_MSG_VAR(b_val); DBG_MSG_VAR(d_val); DBG_MSG_VAR(flt_val); DBG_MSG_VAR(dbl_val); DBG_MSG_VAR(clr_val); DBG_MSG_VAR(str_val); DBG_MSG_VAR(_LastError); #ifdef __MQL5__ complex c1 = { DBL_MIN, 0.2 }; complex c2 = { 0.3, DBL_MAX }; complex c3 = { 0.5, 0.6 }; complex c4 = { 0.6, 0.7 }; matrixc mc_val {{c1, c2}, {c3, c4}}; vectorc vc_val = { c1, c2, c3, c4 }; DBG_MSG_VAR(cmplx_val); DBG_MSG_VAR(m_val); DBG_MSG_VAR(mf_val); DBG_MSG_VAR(mc_val); DBG_MSG_VAR(v_val); DBG_MSG_VAR(vf_val); DBG_MSG_VAR(vc_val); #endif ////////////////////////////////////////////////////////////// // // Print unknown objects, see function at the end. // // Example of printing objects call_obj_function(); ////////////////////////////////////////////////////////////// // // Conditional execution of DBG_MSG() and DBG_MSG_VAR() macro // // Use DBG_MSG_IF() as shown below. // Use DBG_MSG_VAR_IF() as shown below. // // NOTE: The fist statement is a boolean evaluation. // It is encapuslated properly and therefore can // be anything that evaluates to "true" or "false". // Same rules apply as for an "if()" statement. bool printme = true; DBG_MSG_IF(printme, "This will only show, if a condition is true!"); DBG_MSG_VAR_IF(printme, TimeLocal()); ////////////////////////////////////////////////////////////// // // Print arrays // // Output arrays int i_arr[50]; for(int cnt = NULL; (cnt < 50) && !_StopFlag; cnt++) { i_arr[cnt] = MathRand(); } DBG_MSG_VAR(i_arr); string str_val_arr[3]; DBG_MSG_VAR(str_val_arr); #ifdef __MQL5__ matrixc mc_val1 {{c1, c2}, {c3, c4}}; matrixc mc_val2 {{c2, c1}, {c3, c4}}; matrixc mc_val3 {{c1, c2}, {c4, c3}}; matrixc mc_val_arr[3]; mc_val_arr[0] = mc_val1; mc_val_arr[1] = mc_val2; mc_val_arr[2] = mc_val3; DBG_MSG_VAR(mc_val_arr); #endif // Output two arrays side by side double d_arr[50]; for(int cnt = NULL; (cnt < 50) && !_StopFlag; cnt++) { d_arr[cnt] = MathRand(); } DBG_MSG_LISTDUMP(i_arr, d_arr); ///////////////////////////////////// // // Access an array // // Check if it is accessible // // These macros will be replaced // for runtime environment to their // normal usage. - No overhead applied. // DBG_MSG_ARRAY_OUT_OF_RANGE(i_arr, 50); ////////////////////////////////////////////////////////////// // // Print structures and arrays of structures // // Output MQL structures MqlDateTime mql_dtm; MqlRates mql_rates; MqlTick mql_tick; MqlParam mql_param; MqlBookInfo mql_book; DBG_MSG_VAR(mql_dtm); DBG_MSG_VAR(mql_rates); DBG_MSG_VAR(mql_tick); DBG_MSG_VAR(mql_param); DBG_MSG_VAR(mql_book); #ifdef __MQL5__ MqlTradeRequest mql_trade_request; MqlTradeCheckResult mql_tradecheckresult; MqlTradeResult mql_trade_result; MqlTradeTransaction mql_transaction; MqlCalendarCountry mql_cal_cntry; MqlCalendarEvent mql_cal_event; MqlCalendarValue mql_cal_value; DXVector mql_dxvector; DXVertexLayout mql_dxvertex; DBG_MSG_VAR(mql_trade_request); DBG_MSG_VAR(mql_tradecheckresult); DBG_MSG_VAR(mql_trade_result); DBG_MSG_VAR(mql_transaction); DBG_MSG_VAR(mql_cal_cntry); DBG_MSG_VAR(mql_cal_event); DBG_MSG_VAR(mql_cal_value); DBG_MSG_VAR(mql_dxvector); DBG_MSG_VAR(mql_dxvertex); #endif // See the difference in type printed DBG_MSG_VAR(EnumToString(mql_param.type)); DBG_MSG_VAR(mql_param.type); DBG_MSG_VAR(mql_param.integer_value); DBG_MSG_VAR(mql_param.double_value); // Output arrays of structures MqlDateTime arr_mql_dtm[3]; MqlRates arr_mql_rates[3]; MqlTick arr_mql_tick[3]; MqlParam arr_mql_param[3]; MqlBookInfo arr_mql_book[3]; DBG_MSG_VAR(arr_mql_dtm); DBG_MSG_VAR(arr_mql_rates); DBG_MSG_VAR(arr_mql_tick); DBG_MSG_VAR(arr_mql_param); DBG_MSG_VAR(arr_mql_book); #ifdef __MQL5__ MqlTradeRequest arr_mql_trade_request[3]; MqlTradeCheckResult arr_mql_tradecheckresult[3]; MqlTradeResult arr_mql_trade_result[3]; MqlTradeTransaction arr_mql_transaction[3]; MqlCalendarCountry arr_mql_cal_cntry[3]; MqlCalendarEvent arr_mql_cal_event[3]; MqlCalendarValue arr_mql_cal_value[3]; DBG_MSG_VAR(arr_mql_trade_request); DBG_MSG_VAR(arr_mql_tradecheckresult); DBG_MSG_VAR(arr_mql_trade_result); DBG_MSG_VAR(arr_mql_transaction); DBG_MSG_VAR(arr_mql_cal_cntry); DBG_MSG_VAR(arr_mql_cal_event); DBG_MSG_VAR(arr_mql_cal_value); #endif ////////////////////////////////////////////////////////////// // // Analyze condition statements // // Any complexity is supported. // Applicable to all condition evaluations. // if(), while(), for(), () ? : ; .... // if(DBG_MSG_EVAL(0.1 + 0.2 == 0.3)) { Print("true"); } else { Print("false"); } if(DBG_MSG_EVAL_IF(printme, 0.1 + 0.2 == 0.3)) { Print("true"); } else { Print("false"); } if(DBG_MSG_EVAL_CMNT("Check equality", 0.1 + 0.2 == 0.3)) { Print("true"); } else { Print("false"); } if(DBG_MSG_EVAL_IF_CMNT(printme, "Check equality", 0.1 + 0.2 == 0.3)) { Print("true"); } else { Print("false"); } printme = false; if(DBG_MSG_EVAL_IF(printme, 0.1 + 0.2 == 0.3)) { Print("true"); } else { Print("false"); } if(DBG_MSG_EVAL_IF_CMNT(printme, "Check equality", 0.1 + 0.2 == 0.3)) { Print("true"); } else { Print("false"); } // Return return(INIT_SUCCEEDED); } /////////////////////////////////////////////////////////////////////// // // In OnDeinit we will use full tracing of the function call. // // First we define some helpers for compile time code // inclusion selection. This will make it easy to enable/disable // function tracing as needed. // // To do this, first a macro is defined which will be used as a // switch (defined = trace function) (not defined = disable tracing) // // We can collect these "switches" at top of file for a better overview // Also we could wrap them into a condition, so that they will only be // defined when debugging is enabled. // // Example: // #ifdef LIB_DEBUG // #define DBG_TRACE_EXPERT_MAIN_ONDEINIT // // #endif // En/Disable function tracing by commenting following macro: #define DBG_TRACE_EXPERT_MAIN_ONDEINIT // Now we define the helper macros which will be used inside // the functions body. //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ ///////////////////////////////////// // Function debug trace code #ifdef DBG_TRACE_EXPERT_MAIN_ONDEINIT #undef DBG_TRACE_EXPERT_MAIN_ONDEINIT #define DBG_TRACE_EXPERT_MAIN_ONDEINIT(x) x #define DBG_TRACE_EXPERT_MAIN_ONDEINIT_RETURN DBG_MSG_TRACE_RETURN #else #define DBG_TRACE_EXPERT_MAIN_ONDEINIT(x) #define DBG_TRACE_EXPERT_MAIN_ONDEINIT_RETURN DBG_MSG_NOTRACE_RETURN #endif ///////////////////////////////////// void OnDeinit(const int reason) { ///////////////////////////////////// // // TOP of function body // // Here the tracing code gets included, this is mandatory for tracing to work. // The switching macro can also be used to include additional // debugging code, if any is required, see example below. DBG_TRACE_EXPERT_MAIN_ONDEINIT( DBG_MSG_TRACE_BEGIN; // Additional details can be added here, as required. // For example to trace the inputs to this function, we can add DBG_MSG_VAR(reason); // Or whatever we would like to add. ); // Additionally the macro "PERF_COUNTER_BEGIN" must be added. // It is mandatory to complete the "return" macro, irrespective of it // being used or not. (Any insertions will be removed for runtime builds, // if "LIB_PERF_PROFILING" is not defined) PERF_COUNTER_BEGIN; // // This concludes the functions head // control code required for tracing. // If tracing is disabled for this function, // all additional code will be stripped at // compile time. // // Now follows the actual functions body: ///////////////////////////////////// // Local init int some_int_val = 3; string some_string = "I dont know"; ///////////////////////////////////// // // As an example, here the local variables // are printed, but only if tracing is enbaled. // In contrast to the usage of macros in OnInit, where // the macros were not wrapped in the // Trace-Enable-Disable macro. // DBG_TRACE_EXPERT_MAIN_ONDEINIT( DBG_MSG_VAR(some_int_val); DBG_MSG_VAR(some_string); ); // Destroy timer EventKillTimer(); ///////////////////////////////////// // // EXITING/RETURNING from the fucntions body. // // A return statement is mandatory, no matter the functions signature. // This will inform the debug library about the functions ending/exiting. // // The special function "OnDeinit" will auto-resolve the deinit reason // and show the state of the _StopFlag/IsStopped() variable. // This is automatically inserted and needs no extra care taking. // DBG_TRACE_EXPERT_MAIN_ONDEINIT_RETURN; } /////////////////////////////////////////////////////////////////////// // // In OnTick we will use full tracing of the function call as // shown with OnDeinit, and we will integrate global performance // counters as well. This will give output of the runtime used by // this function. // // Since the tracing has already been exlained, here is a // stripped version, showing the raw requirements for a full trace. // // Short outline. // - First define the enable/disable macros. // - Then include the head required inside the body. // - Use the return macro at all exit points of the function. // // To enable tracing for this function, same procedure as // with OnDeinit applies. // Again, this could be collected at top of file for better oversight. // Turn on tracing #define DBG_TRACE_EXPERT_MAIN_ONTICK //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ ///////////////////////////////////// // Function debug trace code #ifdef DBG_TRACE_EXPERT_MAIN_ONTICK #undef DBG_TRACE_EXPERT_MAIN_ONTICK #define DBG_TRACE_EXPERT_MAIN_ONTICK(x) x #define DBG_TRACE_EXPERT_MAIN_ONTICK_RETURN DBG_MSG_TRACE_RETURN #else #define DBG_TRACE_EXPERT_MAIN_ONTICK(x) #define DBG_TRACE_EXPERT_MAIN_ONTICK_RETURN DBG_MSG_NOTRACE_RETURN #endif ///////////////////////////////////// void OnTick() { DBG_TRACE_EXPERT_MAIN_ONTICK( DBG_MSG_TRACE_BEGIN; ); PERF_COUNTER_BEGIN; // Local init int some_var = MathRand(); // Some random check operation if((some_var % 2) == 0) { call_sub_function(); call_sub2_function(); call_obj_function(); } // Some eventual exit at annother point inside the functions body if((some_var % 2048) == 0) { DBG_TRACE_EXPERT_MAIN_ONTICK_RETURN; } // Return DBG_TRACE_EXPERT_MAIN_ONTICK_RETURN; } /////////////////////////////////////////////////////////////////////// // // In OnTimer we will use only the performance metrics macros. // // For ease of use, a return macro will be defined. This is the // only requirement for performance metrics to be displayed in the // experts journal. // // Performance macros are enabled only in release versions // of the code, they are disabled inside of debugging environments. // // Debugging environment is defined by the flag LIB_DEBUG. // To enable performance macros, define LIB_PERF_PROFILING. // // NOTE: // The macro "DBG_MSG_TRACE_RETURN" or "DBG_MSG_NOTRACE_RETURN" // must be used, so the performance control block will be properly closed. // Therefore we define relevant macros again, as shown in other examples. // // Enable/disable tracing does not influence the perfomrance macros, as tracing // is only enabled when "LIB_DEBUG" is defined and "PERF_*"-macros are disabled. // You will get performance measurements when the global macro "LIB_PERF_PROFILING" // is defined and the global macro "LIB_DEBUG" is not defined. // // // We can now define additional, custom performance blocks on a global level. // These work accross functions and can be used to trace certain parts of the // code. - Each block has its own call counter, therefore it is not advised // to reuse the blocks for different code sections, except of course thats // exactly what you want to measure. // // These blocks may overlap, if required. - It is necessary t ounderstand, // when doing so, you will also measure the times each macro takes. The execution // of these macros would be part of your measurement. // // See example below. // // Create additional performance counters // IDs can be specified freely PERF_COUNTER_DEFINE_ID(on_timer_for_loop); // Additional example (these are not used) PERF_COUNTER_DEFINE_ID(if_g_func); PERF_COUNTER_DEFINE_ID(call_f3_func); PERF_COUNTER_DEFINE_ID(printf_f1); PERF_COUNTER_DEFINE_ID(printf_f2); // Performance block IDs can be created on a global scope as well // as on function local scope and even in code blocks inside of // functions scope. //+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ ///////////////////////////////////// // Function performance trace code #define DBG_TRACE_EXPERT_MAIN_ONTIMER_RETURN DBG_MSG_NOTRACE_RETURN ///////////////////////////////////// void OnTimer() { PERF_COUNTER_BEGIN; // Measuring a loop double sum = NULL; PERF_COUNTER_BEGIN_ID(on_timer_for_loop); for(int cnt = NULL; (cnt < 1000000) && !_StopFlag; cnt++) { sum += 1.0; } PERF_COUNTER_END_ID(on_timer_for_loop); ///////////////////////////////////////////////////////////////////////////// // // Here an example of possible nested performance blocks. // This example is very theoretical, but shows how blocks can be applied. // // Local scope { // These definitions can be on any scope in // the hirarchy above this scope of the function all // the way up to the global scope. PERF_COUNTER_DEFINE_ID(a_local_scope); PERF_COUNTER_DEFINE_ID(nested_level_1); PERF_COUNTER_DEFINE_ID(overlapping_block_a); PERF_COUNTER_DEFINE_ID(overlapping_block_b); // Begin measurement of most outer block PERF_COUNTER_BEGIN_ID(a_local_scope); // Have some code here (not mandatory) // Nested performance block 1 PERF_COUNTER_BEGIN_ID(nested_level_1); // Have more code here // Overlapping execution block PERF_COUNTER_BEGIN_ID(overlapping_block_a); // test unbraced if statements if(!MathIsValidNumber(1.23)) Print("number is non valid"); else Print("valid"); // test nested api function calls if(MathIsValidNumber(MathRand())) Print("number is valid"); // test unary operators if(!MathIsValidNumber(MathRand())) Print("number is non valid"); // test void api function srand(GetTickCount()); // test complex expressions + operator precedence rules Print(MathRand() + MathRand() * GetTickCount()); // test unbraced for statements + explicit typecast for(int i = 0; i < 2; i++) (int) floor(log10(rand())); // test accuracy of printing doubles Print(MathAbs(0.3)); Print(MathAbs(0.1 + 0.2)); // Open next block PERF_COUNTER_BEGIN_ID(overlapping_block_b); // test accuracy of printing floats Print(MathAbs(0.7f)); Print(MathAbs(0.1f + 0.6f)); // test typename bug for namespace identifiers. Print(typename((GetTickCount()))); Print(sizeof(GetTickCount())); // test error codes from unsuccessful api function calls FileOpen("file.xyz", FILE_READ); int hdbase = DatabaseOpen("db.sqlite", DATABASE_OPEN_READONLY); DatabaseClose(hdbase); // test correct type promotion of ternary operator. #define tern_expr (true ? MathMax(101, 201) : MathMax("a", "b")) Print(tern_expr); // compiler warning: implicit conversion from 'number' to 'string' (This is correct behaviour!) Print(typename(tern_expr)); // test api function that returns an enumerated value int period = Period(); // test overloads of one api function double ask = SymbolInfoDouble(NULL, SYMBOL_ASK); SymbolInfoDouble(NULL, SYMBOL_ASK, ask); // Now closing block A PERF_COUNTER_END_ID(overlapping_block_a); // Some code for block B // Now closing block B PERF_COUNTER_END_ID(overlapping_block_b); // Maybe more code here for nested level 1 // Close nested block 1 PERF_COUNTER_END_ID(nested_level_1); // Optionally have code here as well.... // Close most outer block PERF_COUNTER_END_ID(a_local_scope); } // The above example will also measure the performance counters itself. // Although they are held as short as possible, code-wise, it will // have some influence on the pure code thats being measured. // // The impact can vary, depending on the memory-paging that happens // by accessing different areas of memory. - The variations will be // within the margins of error and therefore can (mostly) be ignored. // ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // // Standalone performance macros example // // To measure certain parts of code in a standalone environment, // following macros are implemented to do this. // // There are three variations of this macro available. Depending on // the return value of the function to be measured, either having // a return value, a return object or not having a return value, you // need to use the appropiate macro. // // PERF_COUNTER_TIMEIT_V (V = void) // This macro is used to measure functions or function sequences // that do not have a return value. // // PERF_COUNTER_TIMEIT_R (R = return) // This macro will return the resulting value (return value) // of the function being called, or their combination of such. // For a better understanding, see the examples below. // // PERF_COUNTER_TIMEIT_O (O = Object) // This macro will return the object (returned by the function) // of the function being called. // // NOTE: // // PERF_COUNTER_TIMEIT_V may not be mentioned more than once per line of code. // Internally the macro uses the __LINE__ macro for identifying the call, // therefore it is mandatory to have only one call per code line. // Also be aware, if including this macro inside of another macro, all // statements will be on one line of code. // // PERF_COUNTER_TIMEIT_R uses the complier macro __COUNTER__ to identify // unique its callings. Be aware, in case you rely on the sequence of // counting provided by __COUNTER__ within your code. Each mentioning of // the macro "PERF_COUNTER_TIMEIT_R" will increase the __COUNTER__ by one. // // PERF_COUNTER_TIMEIT_O notes are same as for PERF_COUNTER_TIMEIT_R, with // one addition: The Copy-Constructor of the returned object will be called // twice. The first call contributes to the measurement results, the second // consequently does not. In order to encapusalte the function calls, an // additional copy of the object is required within the process of returning // values from the function. // // For pointers as return value, either macro "PERF_COUNTER_TIMEIT_R" or // "PERF_COUNTER_TIMEIT_O" can be used. - Both work the same way in this case. // // A single function call PERF_COUNTER_TIMEIT_V( printf("Some text A"); ); // A block sequence of function calls PERF_COUNTER_TIMEIT_V( printf("Some text B"); printf("Some text C"); ); // Example of complex measurements b_start(); // Return DBG_TRACE_EXPERT_MAIN_ONTIMER_RETURN; } int f1() { Sleep(100); return(1); } int f2() { Sleep(200); return(2); } int f3() { Sleep(300); return(3); } int g() { return(PERF_COUNTER_TIMEIT_R(f1() + f2() + f3() == 6) ? PERF_COUNTER_TIMEIT_R(f1() * f2()) : 0); } //+------------------------------------------------------------------+ //| Service program start function | //+------------------------------------------------------------------+ void b_start() { if (PERF_COUNTER_TIMEIT_R(g()) == 2) { PERF_COUNTER_TIMEIT_V( Print(f3() == 3); ); } PERF_COUNTER_TIMEIT_V( Print(f1()); ); PERF_COUNTER_TIMEIT_V( Print(f2()); ); return; }; /////////////////////////////////////////////////////////////////////// // // In call_sub_function we will use full tracing of the function call. // // This example shows the difference for a function with // a return value. // // Take notice of the used macros. - It is required to use a // different return macro for this type of function call. // // Instead of using the macro "DBG_MSG_TRACE_RETURN" and "DBG_MSG_NOTRACE_RETURN" // here the macros "DBG_MSG_TRACE_RETURN_VAR(x)" and "DBG_MSG_NOTRACE_RETURN_VAR(x)" // are used. // // For use of objects of type "struct", "class" or "interface", a separate // return value macro must be used. - (This implementation is new in MQL5) // // Returning objects from functions, use following macros: // "DBG_MSG_TRACE_RETURN_OBJ(x)" and "DBG_MSG_NOTRACE_RETURN_OBJ(x)" // // For use with pointers as return value, either macro can be used, preferably use // "DBG_MSG_TRACE_RETURN_VAR(x)". // // IMPORTANT NOTICE: // Calling the macro with the parameter "NULL" requires!!! a cast operation. // (This functions return value is "int", "const int" or "int" doesnt make a difference) // // Example: // DBG_MSG_TRACE_RETURN_VAR((int)NULL); // // This cast operation can be included in the macro definition itself, like this: // #define DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION_RETURN(x) DBG_MSG_TRACE_RETURN_VAR((int)x) // #define DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION_RETURN(x) DBG_MSG_NOTRACE_RETURN_VAR(x) // // or inside the function using the custom defined macro: // DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION_RETURN((int)NULL); // // See second function example below. // Here the cast operation is inside the functions body. // NOTE: The runtime macro does not require the cast operation. // // Again the enbale trace macro needs to be defined, so tracing // is enabled. // Enable tracing of this function #define DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION //+------------------------------------------------------------------+ //| Example function with return value | //+------------------------------------------------------------------+ ///////////////////////////////////// // Function debug trace code #ifdef DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION #undef DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION #define DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION(x) x #define DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION_RETURN(x) DBG_MSG_TRACE_RETURN_VAR(x) #else #define DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION(x) #define DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION_RETURN(x) DBG_MSG_NOTRACE_RETURN_VAR(x) #endif ///////////////////////////////////// const int call_sub_function() { DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION( DBG_MSG_TRACE_BEGIN; ); PERF_COUNTER_BEGIN; // Do some stuff if(MathRand() == 5) { DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION_RETURN(5); } // Return DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION_RETURN((int)NULL); } // Enable tracing of this function #define DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION //+------------------------------------------------------------------+ //| Example function with return value | //+------------------------------------------------------------------+ ///////////////////////////////////// // Function debug trace code #ifdef DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION #undef DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION #define DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION(x) x #define DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION_RETURN(x) DBG_MSG_TRACE_RETURN_VAR((int)x) #else #define DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION(x) #define DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION_RETURN(x) DBG_MSG_NOTRACE_RETURN_VAR(x) #endif ///////////////////////////////////// const int call_sub2_function() { DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION( DBG_MSG_TRACE_BEGIN; ); PERF_COUNTER_BEGIN; // Do some stuff if(MathRand() == 12) { DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION_RETURN(12); } // Return DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION_RETURN(NULL); } class CObj { public: int test; CObj() : test(NULL) { }; CObj(const CObj& p_in) { test = p_in.test; printf("%s", "COPY"); }; }; // Enable tracing of this function #define DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION //+------------------------------------------------------------------+ //| Example function with return object | //+------------------------------------------------------------------+ ///////////////////////////////////// // Function debug trace code #ifdef DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION #undef DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION #define DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION(x) x #define DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION_RETURN(x) DBG_MSG_TRACE_RETURN_VAR(x) #else #define DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION(x) #define DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION_RETURN(x) DBG_MSG_NOTRACE_RETURN_VAR(x) #endif ///////////////////////////////////// CObj call_obj_function() { DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION( DBG_MSG_TRACE_BEGIN; ); PERF_COUNTER_BEGIN; // Local init CObj test_obj; CObj* test_ptr = new CObj(); Print("1"); //DBG_TO_STRING(PERF_COUNTER_TIMEIT_O(test_obj)); CObj retval = PERF_COUNTER_TIMEIT_O(test_obj); Print("2"); CObj* ret_ptr1 = PERF_COUNTER_TIMEIT_O(test_ptr); Print("3"); CObj* ret_ptr2 = PERF_COUNTER_TIMEIT_R(test_ptr); Print("4"); // Output arrays int i_arr[50]; for(int cnt = NULL; (cnt < 50) && !_StopFlag; cnt++) { i_arr[cnt] = MathRand(); } DBG_MSG_VAR(i_arr); DBG_MSG_VAR(1); DBG_MSG_VAR(test_obj); DBG_MSG_VAR(test_ptr); // Do some stuff if(MathRand() == 12) { DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION_RETURN(test_obj); } // Return DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION_RETURN(test_obj); }