502 lines
38 KiB
MQL5
502 lines
38 KiB
MQL5
#ifndef LIB_ERROR_HANDLER_MQH_INCLUDED
|
|
#define LIB_ERROR_HANDLER_MQH_INCLUDED
|
|
#property version "4.0";
|
|
/**********************************************************************************
|
|
* Copyright (C) 2010-2020 Dominik Egert <dominik.egert@freie-netze.de>
|
|
*
|
|
* 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
|