MQLplus/lib_error.mqh
super.admin 466f9ca5c5 convert
2025-05-30 16:09:52 +02:00

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