#ifndef LIB_ERROR_HANDLER_MQH_INCLUDED #define LIB_ERROR_HANDLER_MQH_INCLUDED #property version "4.0"; /********************************************************************************** * Copyright (C) 2010-2020 Dominik Egert * * This file is part of lib_bali * * lib_error.mqh may be copied and/or distributed at free will * Dominik Egert / Freie Netze UG. ********************************************************************************** * * Version 4.0 * State: public * * * File information * ================ * * Use: resolve error codes to text * * Intention: * * Extend the basic MQL error handling functions to cover custom codes. * Easy and portable usage. * * The basic idea is to extend and not replace the original implementation * of the functions ResetLastError(), GetLastError(), SetUserError() * All functions may be used paralell and interswitched, although the lib_err * will loose its up to date state when intermixing calls with each other. * This especially affects the comment function. Elsewise, you can simply resolve * error codes by calling err_ResolveLastError(). This works with predefined * error codes as well as with user defined error codes. * * * General usage * * Three functions are supported: * void err_SetUserError(const ushort err_code, const string ...) * void err_ResetUserError() * const string err_ResolveLastError() * const string err_ResolveLastError(int& error_code) * const int err_ResolveLastError(string& error_msg) * * The functions err_SetUserError() simply replace the original functions from MQL. * Original and this extended version do not interfere with each other. Although, the * MQL version will not retrieve a value, if called after err_ResolveLastError(). * * The additional function is used to retrieve last error code and corresponding text. * This works with all defined error codes, as well as predefined system codes. * * * Defining own error codes * * Error codes are split up into groupds, so first a unique group id must be defined. * This ID must be unique within the same compilation project and may overlap if the * target binaries are different. * * For error codes to be assigned convenient, it is needed to define * a set and a return code, the set code will have a prefix. Every error code gets * defined by helper macros. * * All definitions must be inclosed within the beginning and ending macros. * * Here is an example of how it will look like: * * #define EA_ERRGROUP_CODE_EXAMPLE 0x01 * * // Define return codes * #define EXMPL_SUCCESS LIB_ERROR_CODE(EA_ERRGROUP_CODE_EXAMPLE, 0x00) * #define EXMPL_ERR_INVALID_VALUE LIB_ERROR_CODE(EA_ERRGROUP_CODE_EXAMPLE, 0x01) * #define EXMPL_INFO_CORE_READY LIB_ERROR_CODE_HANDLED(EA_ERRGROUP_CODE_EXAMPLE, 0x01) * #define EXMPL_WARN_PENDING_OPERATION LIB_ERROR_CODE_WARNING(EA_ERRGROUP_CODE_EXAMPLE, 0x01) * * // Define message resolver * LIB_ERR_REGISTER_RESOLVER_BEGIN(EA_ERRGROUP_CODE_EXAMPLE) * LIB_ERR_REGISTER_RESOLVER_MSGCODE(EXMPL_SUCCESS, "EXMPL_SUCCESS", "Operation success. All values could be calculated within given parameters.") * LIB_ERR_REGISTER_RESOLVER_MSGCODE(EXMPL_ERR_INVALID_VALUE, "EXMPL_ERR_INVALID_VALUE", "Operation aborted. Invalid function input.") * LIB_ERR_REGISTER_RESOLVER_MSGCODE(EXMPL_INFO_CORE_READY, "CORE_INFO_CORE_READY", "Expert advisor: Core ready.") * LIB_ERR_REGISTER_RESOLVER_MSGCODE(EXMPL_WARN_PENDING_OPERATION, "CORE_WARN_PENDING_OPERATION", "A pending operation is awaiting confirmation.") * LIB_ERR_REGISTER_RESOLVER_END(EA_ERRGROUP_CODE_EXAMPLE) * * * IMPORTANT!! * * The fisrt defined code MUST be the success code for routing to be correct. * It will be, no matter what you define here, always treated as success code. * The success code is also equal to the group id. * * * Reserved codes * * To use reserved error codes, which is nothing different than splitting up the available * error groups into two types, reserved and user defined, the defining macros in the example * above get exchanged with * * LIB_ERROR_RESERVED_CODE() * LIB_ERR_RESERVED_REGISTER_RESOLVER_BEGIN() * LIB_ERR_RESERVED_REGISTER_RESOLVER_END() * * Everything else stays the same. This enables to use internal or predefined error codes and * still be able to define new codes, not interfering with each other due to duplicates. * * * This now can be used anywhere in the code by calling... * * SetUserError(EXMPL_SUCCESS, "Some additional comment!"); * SetUserError(EXMPL_SUCCESS, DBG_STR_PERSIST("Some additional comment! Will persist into release build.")); * * * ... and anywhere else be retrieved by * * const string error_msg = err_ResolveLastError(); * * ... or ... * * int error_code = NULL; * const string error_msg = err_ResolveLastError(error_code); * * ... or ... * * string error_msg = NULL * const int error_code = err_ResolveLastError(error_msg); * * * Hints: * * It should be noted, retrieving the last error by calling err_ResolveLastError() will also reset the error state. * There are a limited amount of user defined error codes available. * * Current limit: ushort -> 16 Bit -> 65536 possible error codes. * The error code is split up into upper byte and lower byte. The upper byte is used as group id * while the lower byte is used as error code id. * The group size geometry and the amount of internal group ids can be set below by altering the * appropiate defines. * * In debug mode, the code will produce exits as any group id shows to be invalid. It may give false alarms * in case group ids are not ascendending defined. The runtime version will ignore such errors and simply * failroute to another router, not resolve at all and respond with "unknown error" or it simply works. * In case all definitions are proper set up and the order is wrong at compile time, this will still result * in a working message router. * * */ //*********************************************************************************************************************************************************/ // Global error handler configuration // ///////////////////////////////////////////////////////////////////////////////////////////////////// // // Setup Lib Error // // Configure for binary mode //#define LIB_ERR_BINARY_LIBRARY // Configure for binary library include //#define LIB_ERR_LIB_IMPORT // User supplied OnError handler (unavailable in binary library mode) //#define LIB_ERR_USER_ERROR_HANDLER // Generate unified function macro substitution //#define LIB_ERROR_API_FUNCTION_SHADOWING // Use CSV file as error description source //#define LIB_ERROR_DESCRIPTION_FROM_FILE // ///////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////// // // Define error group id // This ID must be unique within // the given project. // // Valid values for Group ID are // between 0x00 and 0xEF // 0xF0 to 0xFF is reserved. // // Error codes categories // 0x00 = Succes // 0x01 ... 0xDF = Errors, // 0xE0 ... 0xEF = Warnings // 0xF0 ... 0xFF = Handled Errors // // Define the size each error // router can handle. (0x0100 = 256 here) // #define LIB_ERR_LEVEL_WARNING 0x00E0 #define LIB_ERR_LEVEL_HANDLED_ERROR 0x00F0 #define LIB_ERR_INTERNAL_GROUP_IDS 0xF000 #define LIB_ERR_GROUP_SIZE 0x0100 // ///////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////// // // Define error messages // output format // // Define error output format #define LIB_ERR_OUTPUT_FORMAT(_err_code_, _code_name_, _msg_) StringFormat("(%i - %s) %s", _err_code_, _code_name_, _msg_) // ///////////////////////////////////////////////////////////////////////////////////////////////////// // // END Global error handler configuration //*********************************************************************************************************************************************************/ ///////////////////////////////////////////////////////////////////////////////////////////////////// // // Include lib error // // Include definitions #include "lib_error/lib_error_definitions.mqh" // ///////////////////////////////////////////////////////////////////////////////////////////////////// //*********************************************************************************************************************************************************/ // User defined OnError() handler // #ifdef LIB_ERR_USER_ERROR_HANDLER // Namespace handling #ifndef __MQL4_COMPATIBILITY_CODE__ namespace LibError { #ifdef LIB_ERR_LIB_IMPORT namespace LibError { #endif #endif ///////////////////////////////////////////////////////// // // OnError() Function // // Generic error handling function. // Some return codes are not of interest, so // they get sorted out here and are // surpressed of being journaled. // const bool UserOnError(int& err, const string prefix = NULL) { ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Adjust your error handling behaviour here as you need. // Do not forget to enbale the user supplied error handler macro. (LIB_ERR_USER_ERROR_HANDLER) // /////////////////////////////////////// // // Treat certain environments // differently than others. // const static bool non_reporting_exec_env = ( MQLInfoInteger(MQL_PROFILER) // Is in profiler mode || MQLInfoInteger(MQL_FORWARD) // Is forward strategy tester || MQLInfoInteger(MQL_OPTIMIZATION) // Is running in optimization environment || MQLInfoInteger(MQL_FRAME_MODE)) // Is running as optimization result receiver && ( !MQLInfoInteger(MQL_VISUAL_MODE) // And is not strategy tester visual mode || !MQLInfoInteger(MQL_DEBUG)); // Or not in debug mode /////////////////////////////////////// // // Get and return current error code // err = _LastError; /////////////////////////////////////// // // Use a bitmask to filter error // codes for their severity. // // You may design this as you like. // It all depends on how you set up // your error codes and what they mean. // // Keep in mind, error code 0xxxxxxx00 // is always a success code. // const int err_mask = 0x000000FF | (0x0000FF00 * (((err & 0xFFFF0000) == NULL) & 0x01)); /////////////////////////////////////// // // Success code, treat warnings // as success. // if( ((err & err_mask) == ERR_SUCCESS) || ((err_mask == 0xFF) && ((err & LIB_ERR_LEVEL_WARNING) == LIB_ERR_LEVEL_WARNING)) ) { ResetUserErrorCode(); return(false); } /////////////////////////////////////// // // Handled error codes return as // non error. Governed by the flag // LIB_ERR_LEVEL_WARNING_AS_ERROR // else if((err_mask == 0xFF) && ((err & LIB_ERR_LEVEL_HANDLED_ERROR) == LIB_ERR_LEVEL_HANDLED_ERROR)) #ifndef LIB_ERR_LEVEL_WARNING_AS_ERROR { return(false); } #else { return(true); } #endif /////////////////////////////////////// // // Skip output for certain runtime // environments. // if(non_reporting_exec_env) { return(true); } /////////////////////////////////////// // // Default action to perform on error // // This is not very compatible with // mql4, as printf does not respect // new lines. // // Calling ResolveLastErrorCode with // an error code of -1 will read last // occured error from API. // #ifndef __MQL4_COMPATIBILITY_CODE__ printf("%s", ResolveLastErrorCode(-1, prefix)); #else // MQL4 compatible multi line output string out = ResolveLastErrorCode(-1, prefix); string multipart[]; StringReplace(out, "\r", ""); const int lines = StringSplit(out, 0x0A, multipart); // Multiline journal output support for(int cnt = lines - 1; (cnt >= NULL) && !_StopFlag; cnt--) { printf("%s", multipart[cnt]); }; #endif // Return error state return(true); // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// } // ///////////////////////////////////////////////////////// // End namespace handling #ifndef __MQL4_COMPATIBILITY_CODE__ }; #ifdef LIB_ERR_LIB_IMPORT }; // Namespace LibError #endif #endif // End UserOnError() definition #endif // // END User defined OnError() handler //*********************************************************************************************************************************************************/ ///////////////////////////////////////////////////////////////////////////////////////////////////// // // Function name macro substitutions // // Declare MQL4 function names as macros #ifdef __MQL4_COMPATIBILITY_CODE__ #define err_SetUserError LIB_ERR_NAMESPACE(LibError, SetUserErrorCode) #define err_ResetLastError LIB_ERR_NAMESPACE(LibError, ResetUserErrorCode) #define err_GetLastError LIB_ERR_NAMESPACE(LibError, GetLastErrorCode) #define SetMqlError LIB_ERR_NAMESPACE(LibError, SetMqlErrorCode) #define ResetUserError LIB_ERR_NAMESPACE(LibError, ResetUserErrorCode) #define ResolveLastError LIB_ERR_NAMESPACE(LibError, ResolveLastErrorCode) #define GetLastResolvedError LIB_ERR_NAMESPACE(LibError, GetLastResolvedErrorCode) #define OnError LIB_ERR_NAMESPACE(LibError, OnErrorFunction) #ifdef LIB_ERROR_API_FUNCTION_SHADOWING #define SetUserError LIB_ERR_NAMESPACE(LibError, SetUserErrorCode) #define ResetLastError LIB_ERR_NAMESPACE(LibError, ResetUserErrorCode) #define GetLastError LIB_ERR_NAMESPACE(LibError, GetLastErrorCode) #endif #endif #ifndef LIB_ERR_LIB_IMPORT #ifndef __MQL4_COMPATIBILITY_CODE__ #ifdef LIB_ERROR_API_FUNCTION_SHADOWING #define SetUserError LIB_ERR_NAMESPACE(LibError, SetUserErrorCode) #define ResetLastError LIB_ERR_NAMESPACE(LibError, ResetUserErrorCode) #define GetLastError LIB_ERR_NAMESPACE(LibError, GetLastErrorCode) #define err_SetUserError LIB_ERR_NAMESPACE(LibError, SetUserErrorCode) #define err_ResetLastError LIB_ERR_NAMESPACE(LibError, ResetUserErrorCode) #define err_GetLastError LIB_ERR_NAMESPACE(LibError, GetLastErrorCode) #define SetMqlError LIB_ERR_NAMESPACE(LibError, SetMqlErrorCode) #define ResetUserError LIB_ERR_NAMESPACE(LibError, ResetUserErrorCode) #define ResolveLastError LIB_ERR_NAMESPACE(LibError, ResolveLastErrorCode) #define GetLastResolvedError LIB_ERR_NAMESPACE(LibError, GetLastResolvedErrorCode) #define OnError LIB_ERR_NAMESPACE(LibError, OnErrorFunction) #else #define err_SetUserError LibError::SetUserErrorCode #define err_ResetLastError LibError::ResetUserErrorCode #define err_GetLastError LibError::GetLastErrorCode #define SetMqlError SetMqlErrorCode #define ResetUserError ResetUserErrorCode #define ResolveLastError ResolveLastErrorCode #define GetLastResolvedError GetLastResolvedErrorCode #define OnError OnErrorFunction #endif #endif #endif #ifdef LIB_ERR_LIB_IMPORT #ifndef __MQL4_COMPATIBILITY_CODE__ #ifdef LIB_ERROR_API_FUNCTION_SHADOWING #define SetUserError LibError::LibError::SetUserErrorCode #define ResetLastError LibError::LibError::ResetUserErrorCode #define GetLastError LibError::LibError::GetLastErrorCode #define err_SetUserError LibError::LibError::SetUserErrorCode #define err_ResetLastError LibError::LibError::ResetUserErrorCode #define err_GetLastError LibError::LibError::GetLastErrorCode #define SetMqlError LibError::LibError::SetMqlErrorCode #define ResetUserError LibError::LibError::ResetUserErrorCode #define ResolveLastError LibError::LibError::ResolveLastErrorCode #define GetLastResolvedError LibError::LibError::GetLastResolvedErrorCode #define OnError LibError::LibError::OnErrorFunction #else #define err_SetUserError LibError::SetUserErrorCode #define err_ResetLastError LibError::ResetUserErrorCode #define err_GetLastError LibError::GetLastErrorCode #define SetMqlError LibError::SetMqlErrorCode #define ResetUserError LibError::ResetUserErrorCode #define ResolveLastError LibError::ResolveLastErrorCode #define GetLastResolvedError LibError::GetLastResolvedErrorCode #define OnError LibError::OnErrorFunction #endif #endif #endif // ///////////////////////////////////////////////////////////////////////////////////////////////////// #endif // LIB_ERROR_HANDLER_MQH_INCLUDED