简介
本文介绍在执行期间创建一个调用栈的方法之一。本文介绍了以下功能:
- 创建使用的类、函数和文件的结构。
- 保持以前所有的栈创建调用栈。调用的顺序。
- 执行期间查看 Watch(监视)参数的状态。
- 代码的逐步式执行。
- 分组和排列获得的栈,获取“极端”信息。
开发的主要原则
选择一种常见方法作为表示结构的方法 – 以树的形式显示。为此,我们需要两个信息类。CNode - 用于写入有关栈的所有信息的“节点”。CTreeCtrl - 处理所有节点的“树”。以及用于处理树的跟踪器本身 - CTraceCtrl。
依据以下层次结构实施类:
CNodeBase 和 CTreeBase 类描述处理节点和树的基本属性和方法。
继承的类 CNode 扩展 CNodeBase 的基本功能,CTreeBase 类处理派生类 CNode。由于 CNodeBase 是其他标准节点的父节点,并且出于层次结构和继承的方便性,它被分出来作为一个独立的类,这样就完成了。
与来自标准库的 CTreeNode 不同,CNodeBase 类包含一个指向节点的指针数组,因此从这个节点发出的“分支”是没有数量限制的。
CNodeBase 和 CNode 类
class CNode; // forward declaration //------------------------------------------------------------------ class CNodeBase class CNodeBase { public: CNode *m_next[]; // list of nodes it points to CNode *m_prev; // parent node int m_id; // unique number string m_text; // text public: CNodeBase() { m_id=0; m_text=""; } // constructor ~CNodeBase(); // destructor }; //------------------------------------------------------------------ class CNode class CNode : public CNodeBase { public: bool m_expand; // expanded bool m_check; // marked with a dot bool m_select; // highlighted //--- run-time information int m_uses; // number of calls of the node long m_tick; // time spent in the node long m_tick0; // time of entering the node datetime m_last; // time of entering the node tagWatch m_watch[]; // list of name/value parameters bool m_break; // debug-pause //--- parameters of the call string m_file; // file name int m_line; // number of row in the file string m_class; // class name string m_func; // function name string m_prop; // add. information public: CNode(); // constructor ~CNode(); // destructor void AddWatch(string watch,string val); };
您可以在附带的文件中找到所有类的实施。在本文中,我们将只介绍它们的头部和重要的函数。
依据接受的分类,CTreeBase 表示一个有向非循环图。派生类 CTreeCtrl 使用 CNode 并为其所有功能服务:添加、更改和删除 CNode 节点。
CTreeCtrl 和 CNode 可以成功地代替标准库中的对应类,因为它们具有稍多一些的功能。
CTreeBase 和 CTreeCtrl 类
//------------------------------------------------------------------ class CTreeBase class CTreeBase { public: CNode *m_root; // first node of the tree int m_maxid; // counter of ID //--- base functions public: CTreeBase(); // constructor ~CTreeBase(); // destructor void Clear(CNode *root=NULL); // deletion of all nodes after a specified one CNode *FindNode(int id,CNode *root=NULL); // search of a node by its ID starting from a specified node CNode *FindNode(string txt,CNode *root=NULL); // search of a node by txt starting from a specified node int GetID(string txt,CNode *root=NULL); // getting ID for a specified Text, the search starts from a specified node int GetMaxID(CNode *root=NULL); // getting maximal ID in the tree int AddNode(int id,string text,CNode *root=NULL); // adding a node to the list, search is performed by ID starting from a specified node int AddNode(string txt,string text,CNode *root=NULL); // adding a node to the list, search is performed by text starting from a specified node int AddNode(CNode *root,string text); // adding a node under root }; //------------------------------------------------------------------ class CTreeCtrl class CTreeCtrl : public CTreeBase { //--- base functions public: CTreeCtrl() { m_root.m_file="__base__"; m_root.m_line=0; m_root.m_func="__base__"; m_root.m_class="__base__"; } // constructor ~CTreeCtrl() { delete m_root; m_maxid=0; } // destructor void Reset(CNode *root=NULL); // reset the state of all nodes void SetDataBy(int mode,int id,string text,CNode *root=NULL); // changing text for a specified ID, search is started from a specified node string GetDataBy(int mode,int id,CNode *root=NULL); // getting text for a specified ID, search is started from a specified node //--- processing state public: bool IsExpand(int id,CNode *root=NULL); // getting the m_expand property for a specified ID, search is started from a specified node bool ExpandIt(int id,bool state,CNode *root=NULL); // change the m_expand state, search is started from a specified node void ExpandBy(int mode,CNode *node,bool state,CNode *root=NULL); // expand node of a specified node bool IsCheck(int id,CNode *root=NULL); // getting the m_check property for a specified ID, search is started from a specified node bool CheckIt(int id,bool state,CNode *root=NULL); // change the m_check state to a required one starting from a specified node void CheckBy(int mode,CNode *node,bool state,CNode *root=NULL); // mark the whole tree bool IsSelect(int id,CNode *root=NULL); // getting the m_select property for a specified ID, search is started from a specified node bool SelectIt(int id,bool state,CNode *root=NULL); // change the m_select state to a required one starting from a specified node void SelectBy(int mode,CNode *node,bool state,CNode *root=NULL); // highlight the whole tree bool IsBreak(int id,CNode *root=NULL); // getting the m_break property for a specified ID, search is started from a specified node bool BreakIt(int id,bool state,CNode *root=NULL); // change the m_break state, search is started from a specified node void BreakBy(int mode,CNode *node,bool state,CNode *root=NULL); // set only for a selected one //--- operations with nodes public: void SortBy(int mode,bool ascend,CNode *root=NULL); // sorting by a property void GroupBy(int mode,CTreeCtrl *atree,CNode *node=NULL); // grouping by a property };
结构结束于以下两个类:CTraceCtrl - 其唯一的实例直接用于跟踪,它包含 CTreeCtrl 类的三个实例,用于创建函数的所需结构;另一个是 CIn 类,是一个临时容器。这只是一个用于向 CTraceCtrl 添加新节点的辅助类。
CTraceCtrl 和 CIn 类
class CTraceView; // provisional declaration //------------------------------------------------------------------ class CTraceCtrl class CTraceCtrl { public: CTreeCtrl *m_stack; // object of graph CTreeCtrl *m_info; // object of graph CTreeCtrl *m_file; // grouping by files CTreeCtrl *m_class; // grouping by classes CTraceView *m_traceview; // pointer to displaying of class CNode *m_cur; // pointer to the current node CTraceCtrl() { Create(); Reset(); } // tracer created ~CTraceCtrl() { delete m_stack; delete m_info; delete m_file; delete m_class; } // tracer deleted void Create(); // tracer created void In(string afile,int aline,string aname,int aid); // entering a specified node void Out(int aid); // exit from a specified node bool StepBack(); // exit from a node one step higher (going to the parent) void Reset() { m_cur=m_stack.m_root; m_stack.Reset(); m_file.Reset(); m_class.Reset(); } // resetting all nodes void Clear() { m_cur=m_stack.m_root; m_stack.Clear(); m_file.Clear(); m_class.Clear(); } // resetting all nodes public: void AddWatch(string name,string val); // checking the debug mode for a node void Break(); // pause for a node }; //------------------------------------------------------------------ CIn class CIn { public: void In(string afile,int aline,string afunc) { if(NIL(m_trace)) return; // exit if there is no graph if(NIL(m_trace.m_tree)) return; if(NIL(m_trace.m_tree.m_root)) return; if(NIL(m_trace.m_cur)) m_trace.m_cur=m_trace.m_tree.m_root; m_trace.In(afile,aline,afunc,-1); // entering the next one } void ~CIn() { if(!NIL(m_trace)) m_trace.Out(-1); } // exiting higher };
CIn 类的操作模式
此类负责创建栈树。
图形的形成是使用两个 CTraceCtrl 函数,在两个阶段中逐步进行的:
void In(string afile, int aline, string aname, int aid); // entering a specified node void Out(int aid); // exit before a specified node
换言之,要形成树,进行了对 进入-离开-进入-离开-进入-进入-离开-离开 等的连续调用。
进入-离开 对按以下方式工作:
1. 进入块(函数、循环、条件等),即正好在大括号 "{" 的后面。
在进入块时,创建 CIn 的一个新实例,它获取已经从先前的节点开始的当前 CTraceCtrl。在 CIn 中调用 CTraceCtrl::In 函数,它在栈中创建一个新节点。该节点创建于当前节点 CTraceCtrl::m_cur 之下。关于进入的所有实际信息都被写入该节点:文件名、行号、类名、函数、当前时间等。
2. 遇到括号 "}" 时从块退出。
在从块退出时,MQL 自动调用析构函数 CIn::~CIn。CTraceCtrl::Out 是在析构函数中调用的。当前节点 CTraceCtrl::m_cur 的指针在树中升高一级。此时不为新的节点调用析构函数,节点保留在树中。
栈的形成方案
调用栈以树的形式的形成,并且填写有关调用的所有信息是使用 CIn 容器执行的。
让调用更加容易的宏
为了避免在您的代码中重写很长的创建 CIn 对象并进入节点的代码行,用调用宏来代替它非常方便:
#define _IN CIn _in; _in.In(__FILE__, __LINE__, __FUNCTION__)
如您所见,创建了 CIn 对象,然后我们进入节点。
因为 MQL 在局部变量的名称与全局变量的名称相同时会发出警告,最好采用以下形式(更加精确和清晰地)创建 3-4 个具有其它变量名的类似定义:
#define _IN1 CIn _in1; _in1.In(__FILE__, __LINE__, __FUNCTION__) #define _IN2 CIn _in2; _in2.In(__FILE__, __LINE__, __FUNCTION__) #define _IN3 CIn _in3; _in3.In(__FILE__, __LINE__, __FUNCTION__)
在您深入到子块时,使用下一个宏 _INx
bool CSampleExpert::InitCheckParameters(int digits_adjust) { _IN; //--- initial data checks if(InpTakeProfit*digits_adjust<m_symbol.StopsLevel()) { _IN1; printf("Take Profit must be greater than %d",m_symbol.StopsLevel());
随着在 build 411 中宏的出现,您可以充分使用通过 #define 进行的参数传递。
这是为什么您可以在 CTraceCtrl 类中找到以下宏定义的原因:
#define NIL(p) (CheckPointer(p)==POINTER_INVALID)
它允许缩短指针有效性的检查。
例如,以下代码行:
if (CheckPointer(m_tree))==POINTER_INVALID || CheckPointer(m_cur))==POINTER_INVALID) return;
被更短的形式代替:
if (NIL(m_tree) || NIL(m_cur)) return;
为跟踪准备您的文件
为了控制和获取栈,您需要采取三个步骤。
1. 添加需要的文件
#include <Trace.mqh>
此时整个标准库以 CObject 类为基础。因此,如果它在您的文件中也用作一个基类,则将 Trace.mqh 添加到 Object.mqh 就已经足够了。
2. 将 _IN 宏放入需要的块(您可以使用搜索/替换)
使用 _IN 宏的例子:
bool CSampleExpert::InitCheckParameters(int digits_adjust) { _IN; //--- initial data checks if(InpTakeProfit*digits_adjust<m_symbol.StopsLevel()) { _IN1; printf("Take Profit must be greater than %d",m_symbol.StopsLevel());
3. 在构成程序的主要模块的 OnInit、OnTime 和 OnDeinit 函数中,分别添加全局对象 CTraceCtrl 的创建、修改和删除。您可以在下面找到用于插入的现成代码:
在主代码中嵌入跟踪器
//------------------------------------------------------------------ OnInit int OnInit() { //**************** m_traceview= new CTraceView; // created displaying of the graph m_trace= new CTraceCtrl; // created the graph m_traceview.m_trace=m_trace; // attached the graph m_trace.m_traceview=m_traceview; // attached displaying of the graph m_traceview.Create(ChartID()); // created chart //**************** // remaining part of your code… return(0); } //------------------------------------------------------------------ OnDeinit void OnDeinit(const int reason) { //**************** delete m_traceview; delete m_trace; //**************** // remaining part of your code… } //------------------------------------------------------------------ OnTimer void OnTimer() { //**************** if (m_traceview.IsOpenView(m_traceview.m_chart)) m_traceview.OnTimer(); else { m_traceview.Deinit(); m_traceview.Create(ChartID()); } // if the window is accidentally closed //**************** // remaining part of your code… } //------------------------------------------------------------------ OnChartEvent void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam) { //**************** m_traceview.OnChartEvent(id, lparam, dparam, sparam); //**************** // remaining part of your code… }
跟踪显示类
这样,栈就组织好了。现在,让我们研究获得的信息的显示。
为此,我们要创建两个类。CTreeView – 用于树的显示,CTraceView – 用于控制树和有关栈的其他信息的显示。两个类都派生于基类 CView。
CTreeView 和 CTraceView 类
//------------------------------------------------------------------ class CTreeView class CTreeView: public CView { //--- basic functions public: CTreeView(); // constructor ~CTreeView(); // destructor void Attach(CTreeCtrl *atree); // attached the tree object for displaying it void Create(long chart,string name,int wnd,color clr,color bgclr,color selclr, int x,int y,int dx,int dy,int corn=0,int fontsize=8,string font="Arial"); //--- functions of processing of state public: CTreeCtrl *m_tree; // pointer to the tree object to be displayed int m_sid; // last selected object (for highlighting) int OnClick(string name); // processing the event of clicking on an object //--- functions of displaying public: int m_ndx, m_ndy; // size of margins from button for drawing int m_bdx, m_bdy; // size of button of nodes CScrollView m_scroll; bool m_bProperty; // show properties near the node void Draw(); // refresh the view void DrawTree(CNode *first,int xpos,int &ypos,int &up,int &dn); // redraw void DeleteView(CNode *root=NULL,bool delparent=true); // delete all displayed elements starting from a specified node }; //------------------------------------------------------------------ class CTreeView class CTraceView: public CView { //--- base functions public: CTraceView() { }; // constructor ~CTraceView() { Deinit(); } // destructor void Deinit(); // full deinitialization of representation void Create(long chart); // create and activate the representation //--- function of processing of state public: int m_hagent; // handler of the indicator-agent for sending messages CTraceCtrl *m_trace; // pointer to created tracer CTreeView *m_viewstack; // tree for displaying the stack CTreeView *m_viewinfo; // tree for displaying of node properties CTreeView *m_viewfile; // tree for displaying of the stack with grouping by files CTreeView *m_viewclass; // tree for displaying of stack with grouping by classes void OnTimer(); // handler of timer void OnChartEvent(const int,const long&,const double&,const string&); // handler of event //--- functions of displaying public: void Draw(); // refresh objects void DeleteView(); // delete the view void UpdateInfoTree(CNode *node,bool bclear); // displaying the window of detailed information about a node string TimeSeparate(long time); // special function for transformation of time into string };
作为一种最佳情形,我们已经选择在一个单独的子窗口中显示栈。
换言之,当在函数 CTraceView::Create 中创建 CTraceView 类时,也创建了图表窗口,并且在该窗口中绘制所有对象,尽管 CTraceView 是在 EA 交易于另一个窗口中创建和工作的。这样做能够防止大量的信息阻碍被跟踪程序的源代码的运行,并且防止阻碍在图表中显示其自己的信息。
但是为了让两个窗口能够互动,我们需要向窗口添加一个指标,该指标将用户的所有事件发送到带被跟踪程序的基本窗口。
指标在同一函数 CTraceView::Create 中创建。它只有一个外部参数 - 图表的 ID(所有事件应发送目的地)。