#ifndef LIB_DBG_DEBUG_PRINTF_MQH_INCLUDED #define LIB_DBG_DEBUG_PRINTF_MQH_INCLUDED #property version "5.10" /********************************************************************************** * Copyright (C) 2020 Dominik Egert * * This file is the debugger printf replacement include file. * * Lisence applied: GPLv2 * https://www.gnu.org/licenses/old-licenses/gpl-2.0.html * * Author Dominik Egert / Freie Netze UG. ********************************************************************************** * * Version: 5.10 * State: public * * File information * ================ * */ //*********************************************************************************************************************************************************/ // Begin Debugging support functions // // Overloaded functions #ifndef LIB_DEBUG_NAMESPACE #ifdef __MQL5__ namespace dbg_lib { #endif //#define _DBG_CODE_LOCATION_STRING file, function, line // Debug printf composer void LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_trace_begin)(const string file, const string function, const int line, ulong& call_counter) { // Update lib_debug state LIB_DBG_NAMESPACE(dbg_lib, trace_api_calls) = LIB_DBG_API_CALL_TRACE_DEFAULT; LIB_DBG_NAMESPACE(dbg_lib, trace_call_depth)++; call_counter++; // Construct trace details string trace_details = StringFormat(DBG_OUTPUT_TRACE_BEGIN_FORMAT, LIB_DBG_NAMESPACE(dbg_lib, dbg_ChartID)(), LIB_DBG_NAMESPACE(dbg_lib, trace_call_depth), function, call_counter); // Print details LIB_DBG_NAMESPACE(dbg_lib, dbg_printf)("\r\n%"+ IntegerToString(LIB_DBG_NAMESPACE(dbg_lib, trace_call_depth) * 2) +"s%s", "", StringFormat("%s" + DBG_OUTPUT_STRING, DBG_OUTPUT_PREFIX, DBG_TRACE_BEGIN_MSG_TXT, DBG_CODE_LOCATION_STRING, trace_details)); }; void LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_trace_end)(const string file, const string function, const int line, const string value_msg = NULL) { // Return value string msg = NULL; if(value_msg != NULL) { int split_1 = StringFind(value_msg, "("); msg = StringSubstr(value_msg, 0, split_1) + ">>> return" + StringSubstr(value_msg, split_1); } else { msg = ">>> return(void)"; } LIB_DBG_NAMESPACE(dbg_lib, dbg_printf)("%s", LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_context_msg)(DBG_OUTPUT_PREFIX, file, function, line, msg, "=", DBG_MSG_FORMAT_RIGHT_COLUMN_SPACER - 10)); // Close trace string trace_details = StringFormat(DBG_OUTPUT_TRACE_END_FORMAT, LIB_DBG_NAMESPACE(dbg_lib, trace_call_depth), function); LIB_DBG_NAMESPACE(dbg_lib, dbg_printf)("%s\r\n", LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_context_msg)(DBG_TRACE_END_MSG_TXT, file, function, line, trace_details)); // Check uninit state if(_StopFlag) { LIB_DBG_NAMESPACE(dbg_lib, dbg_printf)("%s", LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_context_msg)(DBG_OUTPUT_PREFIX, file, function, line, LIB_DBG_NAMESPACE(dbg_lib, var_out)(__DBG_STRINGIFY_MACRO((_StopFlag)), _StopFlag), "=", DBG_MSG_FORMAT_RIGHT_COLUMN_SPACER)); } msg = LIB_DBG_NAMESPACE(dbg_lib, uninit_text)(function, -1); if(msg != NULL) { LIB_DBG_NAMESPACE(dbg_lib, dbg_printf)("%s", LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_context_msg)(DBG_OUTPUT_PREFIX, file, function, line, msg)); } // Update lib_debug state LIB_DBG_NAMESPACE(dbg_lib, trace_api_calls) = false; LIB_DBG_NAMESPACE(dbg_lib, trace_call_depth)--; DBG_TRACE_EXEC_DELAY; }; const string LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_context_msg)(const string prefix, const string file, const string function, const int line, const string _msg, const string msg2_token = NULL, const int msg2_column = NULL) { // Prevalidate input if(_msg == NULL) { return(NULL); } // Check for multiline output string result = NULL; string ml_out[]; string msg = NULL; int lines = StringSplit(_msg, 0x0A, ml_out); // Rejoin string containing linebreaks for(int out_msgs = 0; (out_msgs < lines); out_msgs++) { int is_str = StringFind(ml_out[out_msgs], "[str]"); if( (is_str > 0) && (StringFind(ml_out[0], "'") > is_str) && (StringFind(ml_out[0], "(length: ") < 0) ) { for(int cnt = 1; (cnt < lines) && (StringFind(ml_out[out_msgs], "(length: ") < 0); cnt++) { ml_out[out_msgs] += "\n" + ml_out[cnt]; ml_out[cnt] = NULL; } int ptr = NULL; for(int cnt = NULL; (cnt < lines); cnt++) { ml_out[ptr] = ml_out[cnt]; ptr += (ml_out[cnt] != NULL); } lines = ptr + (ml_out[ptr] != NULL); } } // First line string insert = (msg2_token == NULL) ? ml_out[0] : "%s"; string pre_out = StringFormat("%"+ IntegerToString(LIB_DBG_NAMESPACE(dbg_lib, trace_call_depth) * 2) +"s%s" + DBG_OUTPUT_STRING, "", DBG_OUTPUT_PREFIX, prefix, DBG_CODE_LOCATION_STRING, insert); string out = ""; if( (msg2_token != NULL) && (msg2_column != NULL) ) { string substr = StringSubstr(ml_out[0], 0, StringFind(ml_out[0], msg2_token)); #ifdef __MQL5__ StringTrimRight(substr); StringTrimLeft(substr); #else substr = StringTrimRight(substr); substr = StringTrimLeft(substr); #endif out = StringFormat(pre_out, substr + "%s "); int len = StringLen(out); out = StringFormat(out, "%" + IntegerToString(msg2_column - len) + "s%s"); substr = StringSubstr(ml_out[0], StringFind(ml_out[0], msg2_token)); #ifdef __MQL5__ StringTrimRight(substr); StringTrimLeft(substr); #else substr = StringTrimRight(substr); substr = StringTrimLeft(substr); #endif result += StringFormat(out + "\n", "", substr); } else { result += pre_out + "\n"; } // Consecutive lines bool mtx_hdr = false; bool mtx_out = (lines > 2) && (StringFind(result, "[mtx]") > -1); bool new_set = (lines < 2) || (StringLen(ml_out[2]) < 2); int pos = -1; string substr = NULL; string _shift = StringFormat("%"+ IntegerToString(MathMax(0, StringFind(result, StringSubstr(ml_out[0], 0, MathMin(5, StringLen(ml_out[0])))))) +"s", " "); // Clearings for matrix output if(mtx_out) { StringReplace(result, "=", ""); StringReplace(result, "}", ""); } // Process each line for(int cnt = 1; (cnt < lines); cnt++) { msg = ml_out[cnt]; if(StringLen(msg) < 2) { new_set = true; continue; } // Add new set marker pre_out = StringFormat("%"+ IntegerToString(LIB_DBG_NAMESPACE(dbg_lib, trace_call_depth) * 2) +"s%s", "", insert); out = ""; if( (msg2_token != NULL) && (msg2_column != NULL) ) { substr = StringSubstr(msg, 0, StringFind(msg, msg2_token)); mtx_hdr = ((StringFind(msg, "][") > -1) && (StringFind(msg, "{") == -1)); if(mtx_hdr) { substr = StringSubstr(substr, 1); } else #ifdef __MQL5__ { StringTrimLeft(substr); } StringTrimRight(substr); #else { substr = StringTrimLeft(substr); } substr = StringTrimRight(substr); #endif out = StringFormat(pre_out, substr + "%s "); pos = StringLen(out); out = StringFormat(out, "%" + IntegerToString(msg2_column - pos - StringLen(_shift) - ((new_set) ? 2 : 0)) + "s%s"); substr = StringSubstr(msg, StringFind(msg, msg2_token)); #ifdef __MQL5__ StringTrimRight(substr); StringTrimLeft(substr); #else substr = StringTrimRight(substr); substr = StringTrimLeft(substr); #endif out = StringFormat("%s" + out, _shift, "", (StringLen(substr) < 2) ? "" : substr); #ifdef __MQL5__ StringTrimRight(out); #else out = StringTrimRight(out); #endif result += out + "\n"; } else { result += pre_out + "\n"; } // Trace new set state new_set = (new_set) ? false : new_set; } return(StringSubstr(result, 0, StringLen(result) - 1)); }; // Self destructing file handle #ifdef LIB_DBG_LOG_TO_FILE struct __dbg_file_handle { int obj; __dbg_file_handle() : obj(INVALID_HANDLE) { obj = FileOpen(DBG_LOG_FILENAME, FILE_COMMON | FILE_TXT | FILE_ANSI | FILE_WRITE | FILE_SHARE_READ, CP_ACP); }; ~__dbg_file_handle() { if(obj != INVALID_HANDLE) { FileClose(obj); } } } __dbg_f_h; void LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_print_to_file)(const string out) { FileWriteString(__dbg_f_h.obj, out + "\r\n", StringLen(out)); FileFlush(__dbg_f_h.obj); } #endif // Debug get true system time const datetime LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_true_system_time)() { if(FileIsExist("time.tmp", FILE_COMMON)) { FileDelete("time.tmp", FILE_COMMON); } const int f_h = FileOpen("time.tmp", FILE_COMMON | FILE_WRITE, CP_ACP); const datetime tm = (f_h != INVALID_HANDLE) ? (datetime)FileGetInteger(f_h, FILE_CREATE_DATE) : LIB_DBG_NAMESPACE(dbg_lib, dbg_TimeCurrent)(); FileClose(f_h); FileDelete("time.tmp", FILE_COMMON); return(tm); } // Debug printf functions void LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_mq4_ml_printf)(string p1) { string ml_out[]; const int lines = StringSplit(p1, 0x0A, ml_out); for(int cnt = NULL; (cnt < lines); cnt++) { printf("%s", ml_out[cnt]); } } void LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_printf)(const string p1) { #ifdef LIB_DBG_LOG_TO_FILE LIB_DBG_NAMESPACE(dbg_lib, dbg_print_to_file)(p1); #endif #ifndef LIB_DBG_NO_JOURNAL if((p1 != NULL) && (p1 != "")) #ifdef __MQL5__ { printf("%s", p1); } #else { LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_mq4_ml_printf)(p1); } #endif #endif } template void LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_printf)(const string p1, const T p2) { #ifdef LIB_DBG_LOG_TO_FILE LIB_DBG_NAMESPACE(dbg_lib, dbg_print_to_file)(StringFormat(p1, p2)); #endif #ifndef LIB_DBG_NO_JOURNAL if((p1 != NULL) && (p1 != "")) #ifdef __MQL5__ { printf(p1, p2); } #else { LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_mq4_ml_printf)(StringFormat(p1, p2)); } #endif #endif } template void LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_printf)(const string p1, const T p2, const U p3) { #ifdef LIB_DBG_LOG_TO_FILE LIB_DBG_NAMESPACE(dbg_lib, dbg_print_to_file)(StringFormat(p1, p2, p3)); #endif #ifndef LIB_DBG_NO_JOURNAL if((p1 != NULL) && (p1 != "")) #ifdef __MQL5__ { printf(p1, p2, p3); } #else { LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_mq4_ml_printf)(StringFormat(p1, p2, p3)); } #endif #endif } template void LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_printf)(const string p1, const T p2, const U p3, const V p4) { #ifdef LIB_DBG_LOG_TO_FILE LIB_DBG_NAMESPACE(dbg_lib, dbg_print_to_file)(StringFormat(p1, p2, p3, p4)); #endif #ifndef DBG_NO_JOURNAL_OUTPUT if((p1 != NULL) && (p1 != "")) #ifdef __MQL5__ { printf(p1, p2, p3, p4); } #else { LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_mq4_ml_printf)(StringFormat(p1, p2, p3, p4)); } #endif #endif } template void LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_printf)(const string p1, const T p2, const U p3, const V p4, const W p5) { #ifdef LIB_DBG_LOG_TO_FILE LIB_DBG_NAMESPACE(dbg_lib, dbg_print_to_file)(StringFormat(p1, p2, p3, p4, p5)); #endif #ifndef DBG_NO_JOURNAL_OUTPUT if((p1 != NULL) && (p1 != "")) #ifdef __MQL5__ { printf(p1, p2, p3, p4, p5); } #else { LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_mq4_ml_printf)(StringFormat(p1, p2, p3, p4, p5)); } #endif #endif } template void LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_printf)(const string p1, const T p2, const U p3, const V p4, const W p5, const X p6) { #ifdef LIB_DBG_LOG_TO_FILE LIB_DBG_NAMESPACE(dbg_lib, dbg_print_to_file)(StringFormat(p1, p2, p3, p4, p5, p6)); #endif #ifndef DBG_NO_JOURNAL_OUTPUT if((p1 != NULL) && (p1 != "")) #ifdef __MQL5__ { printf(p1, p2, p3, p4, p5, p6); } #else { LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_mq4_ml_printf)(StringFormat(p1, p2, p3, p4, p5, p6)); } #endif #endif } template void LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_printf)(const string p1, const T p2, const U p3, const V p4, const W p5, const X p6, const Y p7) { #ifdef LIB_DBG_LOG_TO_FILE LIB_DBG_NAMESPACE(dbg_lib, dbg_print_to_file)(StringFormat(p1, p2, p3, p4, p5, p6, p7)); #endif #ifndef DBG_NO_JOURNAL_OUTPUT if((p1 != NULL) && (p1 != "")) #ifdef __MQL5__ { printf(p1, p2, p3, p4, p5, p6, p7); } #else { LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_mq4_ml_printf)(StringFormat(p1, p2, p3, p4, p5, p6, p7)); } #endif #endif } template void LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_printf)(const string p1, const T p2, const U p3, const V p4, const W p5, const X p6, const Y p7, const Z p8) { #ifdef LIB_DBG_LOG_TO_FILE LIB_DBG_NAMESPACE(dbg_lib, dbg_print_to_file)(StringFormat(p1, p2, p3, p4, p5, p6, p7, p8)); #endif #ifndef DBG_NO_JOURNAL_OUTPUT if((p1 != NULL) && (p1 != "")) #ifdef __MQL5__ { printf(p1, p2, p3, p4, p5, p6, p7, p8); } #else { LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_mq4_ml_printf)(StringFormat(p1, p2, p3, p4, p5, p6, p7, p8)); } #endif #endif } template void LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_printf)(const string p1, const T p2, const U p3, const V p4, const W p5, const X p6, const Y p7, const Z p8, const Z2 p9) { #ifdef LIB_DBG_LOG_TO_FILE LIB_DBG_NAMESPACE(dbg_lib, dbg_print_to_file)(StringFormat(p1, p2, p3, p4, p5, p6, p7, p8, p9)); #endif #ifndef DBG_NO_JOURNAL_OUTPUT if((p1 != NULL) && (p1 != "")) #ifdef __MQL5__ { printf(p1, p2, p3, p4, p5, p6, p7, p8, p9); } #else { LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_mq4_ml_printf)(StringFormat(p1, p2, p3, p4, p5, p6, p7, p8, p9)); } #endif #endif } template void LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_printf)(const string p1, const T p2, const U p3, const V p4, const W p5, const X p6, const Y p7, const Z p8, const Z2 p9, const Z3 p10) { #ifdef LIB_DBG_LOG_TO_FILE LIB_DBG_NAMESPACE(dbg_lib, dbg_print_to_file)(StringFormat(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)); #endif #ifndef DBG_NO_JOURNAL_OUTPUT if((p1 != NULL) && (p1 != "")) #ifdef __MQL5__ { printf(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); } #else { LIB_DBG_NAMESPACE_DEF(dbg_lib, dbg_mq4_ml_printf)(StringFormat(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)); } #endif #endif } #ifdef __MQL5__ }; #endif #endif // // END Debugging support //*********************************************************************************************************************************************************/ #endif // LIB_DBG_DEBUG_PRINTF_MQH_INCLUDED