how to create objects dynamicly? (Some OOP stuff) - page 2

Amir Yacoby
1383
Amir Yacoby  
Doerk Hilger:

I dont like to say your doing it totally wrong but you do, because this is struct programming and not OOP. The big difference is the power of inheritance and overloading. And by the way, you cannot really inherit from real graphical objects, but you can represent anything as a code object and refer to a line or whatever from this object. Thats the way how its usually done in any class no matter if its a MFC or a MQL class, its all the same. 

If your lines are objects, then treat them as such. Dont deal with arrays outside, do it inside a class collection and work with pointers. Take a look at CWndContainer and get an idea of it. This class is a container which mainly manages pointer arrays for CWnd objects. Go one step ahead, your structure should be:

CObject as base for every object

CPriceTimeObjects as base for every price/time based object, such as lines, derives from CObject. It controls creation, holds time and price and calls an OnCreate(), which can be used by the next inheritant. It also has a Tick function which calls virtual OnTick() which is then overloaded by inheritants. 

CTrendLine as base for trendlines, inherits from CPriceTimeObjects and handles OnCreate where it creates the final line using ObjectCreate function. It should also have an OnTick() handler to react/respond on Tick events, cause it shall be price-sensitive as far as I understood.

Besides this, you have a container class which manages a pointer array that holds all the CTimePriceObject objects you want, it inherits itself also from CTimePriceObject and passes OnTick() to its "childs". The container has a also function that handles the OnChartEvent() to add lines or to remove them. It should also have a scan function to scan all existing objects for the case, the expert was added after lines were created. Furthermore it handles the overloaded OnTick() from CTimePrice, loops the array there, asks every CTrendLine object in it if it feels somehow responsible to react by calling the Tick function of every child object which is handled by a virtual OnTick. Why this again? Because the CTrendLine overloads this function also from CTimePrice and this way this class can also be inherited by further inheritants with further functions. 

Very nice, Dorek.

I have some questions though:

Why does container that holds CTimePriceObject has to inherit itself from CTimePriceObject? For instnce, CIndicators in standard lib inherits from CArrayObj and most probably reffers to CIndicator without inheriting it.
I know it wants to use the standard lib and not the regular arrays, but something about the concept that a container of object of certain kind should inherit the object itself is unclear to me (because the container is not itself an object that it contains, i.e. there is no "is a" relation) even though I feel it is right.
Can you please clarify your view on that?

Second, you seeem to build framework from scratch, without relying on standard library when you say CTrendLine creates final line using the language native ObjectCreate function.
When would you recommend extending the standard libraries themselves, for instance, suppose I want all CIndicator objects of standart lib to be able to say how many times price touches a buffer in them
in the last X bars. How would you go on this, because if you extend a base class like CIndicator, or CIndicatorBuffer, than you have all the dependent objects of the standard library that have to inherit from your
new CIndicator or new CBuffer, like for instance CiMA. Would you copy all those custom indicator classes to your folder and change their inherit to your new CIndicatorBuffer? what then if metaquotes adds something to its standard CIndicatorBuffer or above classes?

Thank you very much for insights.

BR

Alain Verleyen
42593

Willbur:

...

When you go this way my SmartLine object should appear in the MT5 menue beside the trend line, the arrows, the text object and all this stuff.

...

If MT5 allows that, only then we have to discuss the remaining question, how the object could be triggered by the terminal program when the price changes.

...
You can't do that with mql5.
Doerk Hilger
1814
Doerk Hilger  
Amir Yacoby:

Very nice, Dorek.

I have some questions though:

Why does container that holds CTimePriceObject has to inherit itself from CTimePriceObject? For instnce, CIndicators in standard lib inherits from CArrayObj and most probably reffers to CIndicator without inheriting it.
I know it wants to use the standard lib and not the regular arrays, but something about the concept that a container of object of certain kind should inherit the object itself is unclear to me (because the container is not itself an object that it contains, i.e. there is no "is a" relation) even though I feel it is right.
Can you please clarify your view on that?

Second, you seeem to build framework from scratch, without relying on standard library when you say CTrendLine creates final line using the language native ObjectCreate function.
When would you recommend extending the standard libraries themselves, for instance, suppose I want all CIndicator objects of standart lib to be able to say how many times price touches a buffer in them
in the last X bars. How would you go on this, because if you extend a base class like CIndicator, or CIndicatorBuffer, than you have all the dependent objects of the standard library that have to inherit from your
new CIndicator or new CBuffer, like for instance CiMA. Would you copy all those custom indicator classes to your folder and change their inherit to your new CIndicatorBuffer? what then if metaquotes adds something to its standard CIndicatorBuffer or above classes?

Thank you very much for insights.

BR

1. Inheritance of the container.

A limitation of MQL is, that you cannot have multiple inheritances. Because of that, one has to die at least one death. Of course you are right, it would make also sense to inherit from CArrayObj, but I would not do that, because the container handles all the events that any CTimePrice object handles, for example the Tick()->OnTick() and distributes them to its childs, while an array object has nothing to do with that. A container object which holds other objects that also inherit from the same base class, have simply more commonalities. Such a container could also have an anchor, based on a price and on a time, and when you move/shift such a container, it would be the job of the container to move all it´s "childs" too. This is just an example, but the list of such ideas is probably longer than the list of ideas about array functionalities. 

And yes, it´s also like that, that I really created such classes which deal with many types of such objects and the experience that I have in the meanwhile tells me, that it was the right decision to manage inheriting this way.  

2. Framework from scratch.

Similar here. When I started to go a bit deeper into the standard libraries I found many things, which I did not like. Not only the bad performance of those, but also lack of flexibility and also because of an incomplete architecture. I am missing for example a global CMouse class/object as well as a class between CWnd and CObject, because CWnd objects are childs of the chart as well as lines are, and there is no connection to such and no final implementation of any such objects at all like I described it above. And, there is no master object, which holds all such chart objects which makes it possible to show/hide all of them with one command, destroy them, whatever. CCanvas, same thing, a nice class but where is the implementation with CWnd which allows me to create interactive objects based on bitmaps that inherit from CWnd? And so on. 

Furthermore, the whole structure of all standard libraries does not allow side-chains, but this is necessary, because MQL does not allow void-pointers. For example: I am using a classed named CDragLine which allows me to create trendline objects which can be dragged. If such a trendline object is connected to an order and furthermore to a panel/display whatever, I need the possibility to use such a side-chain that the connected panel gets informations about movements/changes too, which are caused by changes of the order. And vice versa I need the option to move the order and to inform the panel when the line itself was dragged. This kind of triangular messaging cannot be done with the standard library. This is done by side chains, which means, absolutely all objects inherit from an advanced version of CObject. This class has the possibility to connect any other object to an object, and to send messages to such a connected object. This way a much more complex and effective messaging is possible without having any weird code. 

I cannot give any recommendation of what everybody should do, but actually I decided for me to drop 99% of the standard libraries, the only classes which I have left from the originals are CCanvas (but with some changes and bug fixes, see Code Base) and CSymbolInfo.    

-------------- 

If someone is interested in the side chain functionality, here is the code of my CObject class. If you replace the original CObject in Object.mqh after every update, most parts of the standard libraries get the enhancements of this side chain feature. And by the way, the node connection is implemented too - which is not done in the original.    

To add such a side chain feature, do it as follows inside the receiving class:

//--- Example for receiving class
//---

class CAnyClass : public CAnyBaseClass // of course CAnyBaseClass inherits from CObject in the end too
   {
   private:
      CWhatEver   m_object;     // embedded object
      CFurther    m_further;    // embedded object

   public:
   //+------------------------------------------------------------------+
   //|  Creation                                                        |
   //+------------------------------------------------------------------+
   CAnyClass(void)
      {
      m_classname="CAnyClass"; 

      //--- Connect side chains 
      m_object.CustomEventReceiver(PTR(this));
      m_further.CustomEventReceiver(PTR(this));
      }
   
   protected:
   //+------------------------------------------------------------------+
   //|  Custom event handler for side chain messages                    |
   //+------------------------------------------------------------------+
      virtual void      OnCustomEvent(CObject * sender, int eventid)
         {
            if (sender==PTR(m_object))
               {
               switch (eventid)
                  {
                  case 123456:
                     Print("Here we go with 123456");
                     break;
               //...
                  }
               }
            else if (sender==PTR(m_further))
               {
               //...
               } 
         }            
   };

 The sending classes CWhatEver and CFurther don't know anything about the receiver, of if there is a receiver or not. The code is just the following:

 

//---
//...

   CustomEvent(123456);

//...
//---

 Here is the CObject replacement:

//+------------------------------------------------------------------+
//|                                                       Object.mqh |
//|                                               Copyright by Doerk |
//+------------------------------------------------------------------+

#ifndef __DH_OBJECT_CLASS
#define __DH_OBJECT_CLASS

#include <stdlib.mqh>
//+------------------------------------------------------------------+
//|                                                                  |
//| Defintions                                                       |
//|                                                                  |
//+------------------------------------------------------------------+
#ifndef PTR
   #define PTR(object) GetPointer(object)
   #define PTR_DELETE(object) { if (CheckPointer(object)==POINTER_DYNAMIC) delete object; }
   #define PTR_INVALID(object) (object==NULL || CheckPointer(object)==POINTER_INVALID)
#endif   

enum ENUM_FILE_IO
   {
   FILE_IO_NONE = 0,    //--- No file interaction
   FILE_IO_BINARY = 1,  //--- Binary, OnLoad() / OnSave() events
   FILE_IO_INI = 2,     //--- Ini file, OnLoadIni() / OnSaveIni() events
   };
   
//+------------------------------------------------------------------+
//|                                                                  |
//| Class CStruct - the base of everything                           |
//|                                                                  |
//+------------------------------------------------------------------+
class CStruct
   {
   };
//+------------------------------------------------------------------+
//|                                                                  |
//| Class CObject                                                    |
//|                                                                  |
//+------------------------------------------------------------------+
class CObject : public CStruct
  {
   protected:      
      long              m_obj_id;               //--- Unique ID of each object
      string            m_classname;            //--- Name of (deriving) class
      CObject          *m_prev;                 //--- Previous item in chain
      CObject          *m_next;                 //--- Next item in chain
      CStruct          *m_struct;               //--- Additional attached struct
      CObject          *m_object;               //--- Additional attached object
      string            m_tag;                  //--- Additional tag (File operation)
   private:
      CObject          *m_eventreceiver;        //--- Object which gets custom notifications
      ENUM_FILE_IO      m_fileio;               //--- Enables/disable file input/output
      
   public:
      //+------------------------------------------------------------------+
      //| Construction                                                     |
      //+------------------------------------------------------------------+
      CObject(void) : m_fileio(FILE_IO_BINARY),
                      m_struct(NULL),
                      m_tag(NULL),
                      m_object(NULL)
         {
      //--- Set ID
            __obj_cnt++;
            m_obj_id=__obj_cnt;
      //--- Reset notified object            
            m_eventreceiver=NULL;
      //--- Connect chain            
            Prev(__obj_prev);
            if (__obj_prev!=NULL)
               __obj_prev.Next(PTR(this));
            __obj_prev=PTR(this);
            Next(NULL);
            
         } 
         
      //+------------------------------------------------------------------+
      //| Destruction                                                      |
      //+------------------------------------------------------------------+
      ~CObject(void)
         {
      //--- Reconnect chain
            if (m_prev!=NULL)
               m_prev.Next(m_next);
            if (m_next!=NULL)
               m_next.Prev(m_prev);    
            if (__obj_prev==PTR(this))
               __obj_prev=Prev();                    
         } 
      //+------------------------------------------------------------------+
      //| Chain access                                                     |
      //+------------------------------------------------------------------+
   public:      
      CObject          *Prev(void)                                      const { return(m_prev); }
      void              Prev(CObject *node)                                   { m_prev=node;    }
      CObject          *Next(void)                                      const { return(m_next); }
      void              Next(CObject *node)                                   { m_next=node;    }
      //+------------------------------------------------------------------+
      //| Custom events - allows interaction between embedded objects and  |
      //|                containers                                        |
      //+------------------------------------------------------------------+
   public:
      CObject *         CustomEventReceiver(void)                       const { return(m_eventreceiver); }
      bool              CustomEventReceiver(CObject *receiver)
         {
            if (m_eventreceiver!=NULL)
               return false;
            m_eventreceiver=receiver;
            return true;   
         }
      void              CustomEvent(int eventid=0)
         {
            if (!PTR_INVALID(m_eventreceiver))
               m_eventreceiver._CustomEvent(PTR(this), eventid);
         }      
      void              _CustomEvent(CObject * sender, int eventid)
         {
            OnCustomEvent(sender, eventid);
         }      
   protected:
      virtual void      OnCustomEvent(CObject * sender, int eventid)         
         {
         }
                                          
      //+------------------------------------------------------------------+
      //| File interaction                                                 |
      //+------------------------------------------------------------------+
   public:
      bool              Save(const int file_handle)                           { if (m_fileio==FILE_IO_NONE) return true; return(OnSave(file_handle));   }
      bool              Load(const int file_handle)                           { if (m_fileio==FILE_IO_NONE) return true; return(OnLoad(file_handle));   }
      bool              Save(CObject *fileobject)                             { if (m_fileio==FILE_IO_NONE) return true; return(OnSave(fileobject)); }
      bool              Load(CObject *fileobject)                             { if (m_fileio==FILE_IO_NONE) return true; return(OnLoad(fileobject)); }
      bool              LoadDefault(void)                                     { return (OnLoadDefault()); }
      bool              FileIO(const ENUM_FILE_IO flag)                       { m_fileio=flag; return true; }
      ENUM_FILE_IO      FileIO(void)                                          { return m_fileio; }
   protected:
      virtual bool      OnSave(const int file_handle)                         { return true; }
      virtual bool      OnLoad(const int file_handle)                         { return true; }
      virtual bool      OnSave(CObject *fileobject)                           { return true; }
      virtual bool      OnLoad(CObject *fileobject)                           { return true; }
      virtual bool      OnLoadDefault(void)                                   { return true; }
      
      //+------------------------------------------------------------------+
      //| Identification                                                   |
      //+------------------------------------------------------------------+
   public:      
      long              Id(void)                                        const { return m_obj_id;    }
      virtual int       Type(void)                                      const { return(0);      }
      string            ClassName(void)                                 const { return(m_classname); }
      string            Tag(void)                                       const { return m_tag; }
      bool              Tag(string value)                                     { m_tag=value; return true; }

      //+------------------------------------------------------------------+
      //| Comparison                                                       |
      //+------------------------------------------------------------------+
   public:      
      virtual int       Compare(const CObject *node,const int mode=0)   const { return(0);      }
      
  };
//+------------------------------------------------------------------+
long __obj_cnt=-1;
CObject * __obj_prev=NULL;
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+

#endif                  // __DH_OBJECT_CLASS
Amir Yacoby
1383
Amir Yacoby  

Thank you for elaborating and for sharing the side chain idea.

Frankly, I don't quite understand, and the code does not compile (I created CWhatEver and CFurther as empty classes directly inheriting the CObject you posted with just constructor just to get the idea).

2 compile errors received about CWhatEver::CWhatEver cannot call private member function and same error for CFurther.

Anyway, I googled the term "side chain" and couldn't find. If you happen to know of some written material I can read it would be great.

 

BR 

Doerk Hilger
1814
Doerk Hilger  

Yes the classes are empty, thats why they dont compile. These are just placeholders.

Side chains may not be the official description for it, its rather my clean MQL workaround for void pointers and/or multiple inheritance. In short words: It gives you the ability to communicate between any classes and is very useful if objects are embedded in classes, because you can "tell" them that the containing class is the receiver of their custom events. This way you can avoid any spaghetti coding. 

For example, if you have something like

return OnClick();

which would call the overload of the last deriving class first, you could extend it to something like

return OnClick()&CustomEvent(CUSTOM_CLICK); 

This way, not only the deriving class which normally has a

virtual bool OnClick()

function would be notified, also the class which includes the object is able to receive the click-notification.  

Amir Yacoby
1383
Amir Yacoby  

Do you mean that it is a way to communicate between any two objects not by usual messaging that needs to know the other object it's sending a message to, but thru custom events?

That idea I get, but what do you mean by embedded objects? in your code of CAnyClass you had two private objects. Do you mean by embedded that their bodies should be also decalred inside
CAnyClass or they can be defined outside? I mean, embedded as you use the term is an object which is private inside another object?

If for instance I write CWhatEver outside of CAnyClass, and I want to message CAnyClass about an event, do you mean that in CWhatEver I will return

return OnClick()&CustomEvent(CUSTOM_CLICK);      // <=== you mean && instead of & ????

which will message the derived class of CWhatEver as usual and also the containing class which is CAnyClass (because of custom event)?

And why is CObject defined inside an #ifndef?

BTW, just curious, what is the purpose of stdlib.mqh in your CObject? 

Doerk Hilger
1814
Doerk Hilger  

From bottom to top ...

- the stdlib, isnt it anyway included with the original object.mqh? I dont need it here, but it contains CompareDouble() which is the only reliable way to compare two doubles

- I use #ifndef to avoid double definitions by standard and for conditional compiling

- yes, for triangular messaging

- the binary & and the logical version && are the same with bool results

- by embedded I mean, if an instance/object of a foreign class is part of another class and both do not derive from eachother. Use the OnClick() example and assume, the m_object handles the OnClick() somehow for internal purposes, e.g. if its a button or whatever. How would the code look like if you need to know within CAnyClass that there was a click without this custom event

To be honest, its not a functionality that one needs every day for every purpose, but if there is a situation when you need to communicate not only directional, its the solution. 

Get back to the original thread issue. The idea was to use arrays to manage multiple lines that react on the price. These line objects handle events when they/their lines are crossed by a candle and force some action. I recommended to use a container. If this container now wants to count these actions, just for example, it can be easily done this way when the container tells every object that it is the receiver of the custom event by using for example

m_trendline[n].CustomEventReceiver(PTR(this));

while the CTrendLine class - of course - has to implement it somehow like this:

return OnLineCrossed()&CustomEvent(CTRENDLINE_CROSSED);

Willbur
226
Willbur  
Wow, how great that there is such a discussion in the forum. Although I must admit that I am still at the beginning of OOP.

Unfortunately, I go on vacation
for two weeks. Plane leaves in hours (ok, could be worse).

One question I have at this point already: Is there somewhere a detailed documentation about the MQL frame work?


Willbur
Alain Verleyen
42593
Willbur:
..

One question I have at this point already: Is there somewhere a detailed documentation about the MQL frame work?

No :-(

From my experience, it's good to learn to study the "mql framework". But as stated by Doerk, there is a lot of problems with the Standard Library, and in my opinion, it's not usable in serious and big projects.

Amir Yacoby
1383
Amir Yacoby  
Doerk Hilger:

From bottom to top ...

- the stdlib, isnt it anyway included with the original object.mqh? I dont need it here, but it contains CompareDouble() which is the only reliable way to compare two doubles

- I use #ifndef to avoid double definitions by standard and for conditional compiling

- yes, for triangular messaging

- the binary & and the logical version && are the same with bool results

- by embedded I mean, if an instance/object of a foreign class is part of another class and both do not derive from eachother. Use the OnClick() example and assume, the m_object handles the OnClick() somehow for internal purposes, e.g. if its a button or whatever. How would the code look like if you need to know within CAnyClass that there was a click without this custom event

To be honest, its not a functionality that one needs every day for every purpose, but if there is a situation when you need to communicate not only directional, its the solution. 

Get back to the original thread issue. The idea was to use arrays to manage multiple lines that react on the price. These line objects handle events when they/their lines are crossed by a candle and force some action. I recommended to use a container. If this container now wants to count these actions, just for example, it can be easily done this way when the container tells every object that it is the receiver of the custom event by using for example

m_trendline[n].CustomEventReceiver(PTR(this));

while the CTrendLine class - of course - has to implement it somehow like this:

return OnLineCrossed()&CustomEvent(CTRENDLINE_CROSSED);

stdlib I thought was just MQL4 but maybe I'm wrong.
Sorry, I didn't get why the whole CObject has to be inside #ifndef. What will be double defined if it would be written plain? And btw, why do you put CStruct as empty class above CObject?

About the embedded class, (sorry I'm a programmer for 25 yrs too but not OO) your example refers to a situation like this for instance? Suppose that I'm a CCar class, and I have embedded a CWheel class.
And the CWheel has an event handler for air pressure minimum or something, and I as the car need to know that?
If you can find similarities to this example or other specific, because I'm still not quite there in terms of which class fires events (in the example you gave with click it's obviously the chart) and who handles it. And even technically, understanding for instance these lines (I assume their purpose is showing how container will do the work of managing the trendlines crossing the price by a bar):

m_trendline[n].CustomEventReceiver(PTR(this));
can you clarify if I got it right? does it call the all trendlines [1..n] in a loop? if so, what type of a general event could it be that the container is handling right now? it's not line
specific event because it calls all lines. Can it be like a new bar or price change that has potential to trigger CTRENDLINE_CROSSED event? And why does it send GetPointer(this) to each line?
does the line has to callback container, and why?
So, if that's the case,  

return OnLineCrossed()&CustomEvent(CTRENDLINE_CROSSED);  
then each line will call it's own OnLineCrossed() - which should be a regular method that just checks if the price has been crossed by that specific line,
and then call CustomEvent(CTRENDLINE_CROSSED) - which its roll is just to call ChartCustomEvent(..) to fire the event CTRENLINE_CROSSED - which in your structure will be handled again in the container? is that last part what makes triangular communication or maybe I'm just mixining here two different concepts. So the container will have a CustomEvent handler that handles CTRENDLINE_CROSSED?

writing that some things became more clear I think (and the CCar can be disregarded if you like, and just focus on the trendline ) but still I don't get why moving GetPointer(this) to each line? And on what kind of event for instance the container will call the custom event receiver in each line in order to detect price changes, and where is triangular communication or is it apply only previous example for triangular only (with  CAnyClass)?
Can you maybe apply triangular example on this trendline?

I really thank you for your time and patience and help so far, it's not obvious at all. I have to say you opened my eyes to potential of event driven programming.