struct inheritance in Mql5

 

I'm trying to get at something like structure hierarchy with a super struct and different structures derived from it. 
Each derived structure has different kind of data, and there is a Pattern class that has the saving/reading methods for those different structures.
And so the structure is passed between different classes - one class saves it and another reads it and needs the data. 
For instance, one class calls the save strurcture with structure A, and another class calls it to get structure A back.

The problem I'm facing is casting a structure to it's super losses it's data and when I try to convert back, it's lost as can seen by the output


struct Super
  {
public:
   int               type;
  };
struct Child1 : public Super
  {
public:
   double            S1;
   double            S2;
  };
struct Child2 : public Super
  {
public:
   int               S3;
   double            S4;
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class Pattern
  {
private:
   Super             m_pattern;
public:
   void              Set(const Super &s) {m_pattern=s;}
   void              Get(Super &s) {s=m_pattern;}
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   Child1 cld1;
   cld1.S1=5.11;
   cld1.S1=6.3;
   //--- save struct and read it again
   Pattern obj;
   obj.Set(cld1);
   Child1 cld1Copy;
   obj.Get(cld1Copy);
   Print("S1 orig=",cld1.S1," S1 copy=",cld1Copy.S1," S2 orig=",cld1.S2," S2 copy=",cld1Copy.S2);
  }

I can use uchar arrays instead

struct Super
  {
public:
   int               type;
  };
struct Child1 : public Super
  {
public:
   double            S1;
   double            S2;
  };
struct Child2 : public Super
  {
public:
   int               S3;
   double            S4;
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class Pattern
  {
private:
   uchar             m_pattern[];
public:
   void              Set(uchar &arr[]) {ArrayCopy(m_pattern,arr);}
   void              Get(uchar &arr[]) {ArrayCopy(arr,m_pattern);}
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   Child1 cld1;
   cld1.S1=5.11;
   cld1.S2=6.3;
   //--- the save/restore class
   Pattern obj;
   //--- save struct and read it again
   //--- save
   uchar arr1[];
   StructToCharArray(cld1,arr1);
   obj.Set(arr1);                       // save the array of chars
   //--- restore
   uchar arr2[];
   obj.Get(arr2);
   Child1 cld1Copy;
   CharArrayToStruct(cld1Copy,arr2);    // convert it to the struct
   Print("S1 orig=",cld1.S1," S1 copy=",cld1Copy.S1," S2 orig=",cld1.S2," S2 copy=",cld1Copy.S2);
  }

Which gets the structure right, but the price to pay for that is that the typecasting back by the last CharArrayToStruct is not typesafe checked by compiler - I could write any struct in the function and it will compile: 

Child2 cld2Copy;                               // retriever tries to retrieve a different struct then the saved one
CharArrayToStruct(cld2Copy,arr2);    // should be cld1Copy which was previously saved, but compiler does not check

Almost sure there's no way, but anyway. 
void & struct_object (could be the equivalent of void *) which would have accepted any struct does not seem to work on Mql5 like in it's own functions, for instance the void first parameter of that function is not Mql5 syntax:

ArrayStruct 

The perfect solution would have been:

Set(void & struct_object);  // save any struct
void & Get();               // get the struct back

but of course not in Mql5

 

You will probably say the below solution doesn't solve your real issue, but it solves the simplified you presented ;-)

//---
template<typename T>
class Pattern
  {
private:
   T             m_pattern;
public:
   void              Set(const T &s) {m_pattern=s;}
   void              Get(T &s) {s=m_pattern;}
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   Child1 cld1;
   cld1.type=1;
   cld1.S1=5.11;
   cld1.S2=6.3;
   printf("Child original type=%i S1=%f S2=%f",cld1.type,cld1.S1,cld1.S2);

   //--- save struct and read it again
   Pattern<Child1> obj;
   obj.Set(cld1);
   Child1 cld1Copy;
   obj.Get(cld1Copy);
   printf("Child copy type=%i S1=%f S2=%f",cld1Copy.type,cld1Copy.S1,cld1Copy.S2);
  }
 
Structs are always copied on assignment. This is in accordance with similar object-oriented languages like C++ or C#. So how should the values of the derived struct be preserved when it gets assigned to the super struct? Use classes instead.
 
Alain Verleyen:

You will probably say the below solution doesn't solve your real issue, but it solves the simplified you presented ;-)

Interesting. Missed that possibility.
The idea in general is an event hub. 
The different structs represent different events (each with its own data). 
Each class can register itself to the hub, asking for event type notifications from another class. 
For instance, a strategy asks for event of stop-loss trigger from a trading supervisor class. 
The trading supervisor knows it had an sl triggered, it informes the hub with the event struct (this is the save).
So, the event has to include some data, which order is stopped, the ticket, price etc. 
The hub sends it to the client - which needs the restore of struct. 

I will try to see if it can be of help, thanks.
lippmaje:
Structs are always copied on assignment. This is in accordance with similar object-oriented languages like C++ or C#. So how should the values of the derived struct be preserved when it gets assigned to the super struct? Use classes instead.
It's counter-intuitive to use classes as event data, although it can be done. I dismissed that possibility in the past, but maybe reconsider. thanks.
 


Maybe someone knows templates better, I have a struct as template to CEventHub, but the struct itself has a constructor parameter, so this code is not compiled ("MqlEventDummy1 - wrong parameter count").
How do you specify that the template parameter has a parameter itself (and why is it needed at all) ? 

thanks

enum ENUM_EVENT_TYPE
  {
   EVENT_TYPE_DUMMY1
  };

template<typename T>
class CEventHub
  {
private:
   ENUM_EVENT_TYPE   m_event_type;
   T                 m_event;
public:
   void              Set(const T &event) {m_event=event;}
   void              Get(T &event) {event=m_event;}
   ENUM_EVENT_TYPE   EventType() const {return(m_event_type);}
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
struct MqlEvent
  {
private:
   ENUM_EVENT_TYPE   event_type;
public:
                     MqlEvent(const ENUM_EVENT_TYPE type) {event_type=type;}
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
struct MqlEventDummy1 : public MqlEvent
  {
                     MqlEventDummy1(const ENUM_EVENT_TYPE type);
  };
//+------------------------------------------------------------------+
//| constructor                                                      |
//+------------------------------------------------------------------+
MqlEventDummy1::MqlEventDummy1(const ENUM_EVENT_TYPE type) : MqlEvent(type)
  {
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   CEventHub<MqlEventDummy1> hub;   // need specifying parameter for struct ??
  }
 

I guess a constructor with parameters makes it a different type, so probably no way around but to specify NULL as default value to class constructor, so to imitate a non-parametrized class constructor.

Would have expected some syntax to handle that, but.. Can live with that.

 

Ok, tiredness..
First NULL
Second explicit value

struct MqlEvent
  {
private:
   ENUM_EVENT_TYPE   event_type;
public:
                     MqlEvent(const ENUM_EVENT_TYPE type=NULL) {event_type=type;}
  };
struct MqlEventDummy1 : public MqlEvent
  {
                     MqlEventDummy1();
  };
MqlEventDummy1::MqlDummy1() : MqlEvent(ENUM_EVENT_DUMMY1)
  {
  }
 

It works if you merge the data fields of all derived structs into the super struct like so:

enum EEventType
  {
   EMouseMoveEvent,
   EMessageEvent
  };

struct SEvent
  {
protected:
   int               i1;
   int               i2;
   string            s;
public:
   int               type;
  };

struct SMouseMoveEvent : public SEvent
  {
   SMouseMoveEvent(int x=0,int y=0) { type=EMouseMoveEvent; i1=x; i2=y; }
   int X() { return i1; }
   int Y() { return i2; }
  };

struct SMessageEvent : public SEvent
  {
   SMessageEvent(string msg="") { type=EMessageEvent; s=msg; }
   string Msg() { return s; }
  };

int OnInit()
  {
   SEvent ev;
   SMouseMoveEvent evmm(50,40);
   ev=evmm;
   SMouseMoveEvent evmm2=ev;
   Print(evmm2.X()," ",evmm2.Y());
   SMessageEvent evmsg("text");
   ev=evmsg;
   SMessageEvent evmsg2=ev;
   Print(evmsg2.Msg());
 
Looks like a pattern for that kind of problems maybe, reached to a combination between classes and structs.
Here, because structs not support OO fully, the classes are used, but just as struct carriers.
The user creates the classes by supplying the struct, and recieves the data by retrieving the structs.
Each event has also a class, that has nothing public, but the struct it recieves in the constructor, and the struct it returns.
enum ENUM_EVENT_TYPE
  {
   EVENT_MOUSE_MOVE,
   EVENT_MESSAGE
  };

struct MqlEvent
  {
   ENUM_EVENT_TYPE   event_type;
  };

class CEvent
  {
private:
   ENUM_EVENT_TYPE   event_type;
protected:
                     CEvent(const ENUM_EVENT_TYPE type) {event_type=type;}
public:
   ENUM_EVENT_TYPE   EventType() const {return event_type;}
  };

struct MqlEventMouseMove : public MqlEvent
  {
   int               x;
   int               y;
  };

class CEventMouseMove : public CEvent
  {
private:
   int               m_x;
   int               m_y;
public:
                     CEventMouseMove(MqlEventMouseMove &event) : CEvent(EVENT_MOUSE_MOVE)
     {m_x=event.x; m_y=event.y;}
   bool              ToStruct(MqlEventMouseMove &event)
     {event.x=m_x; event.y=m_y; return(true);}
  };

struct MqlEventMessage : public MqlEvent
  {
   string            message;
  };

class CEventMessage : public CEvent
  {
private:
   string               m_message;
public:
                     CEventMessage(MqlEventMessage &event) : CEvent(EVENT_MESSAGE)
     {m_message=event.message;}
   bool              ToStruct(MqlEventMessage &event)
     {event.message=m_message; return(true);}
  };
  
  
class CEventHub
  {
   private:
      CEvent        *m_event;
   public:
      void           SetEvent(CEvent *event) {m_event=event;}
      CEvent        *GetEvent() {return(m_event);}
  };
  
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   MqlEventMouseMove mouseEvent;
   mouseEvent.x=15;
   mouseEvent.y=23;
   CEventMouseMove *objMouseEvent=new CEventMouseMove(mouseEvent);
   
   MqlEventMessage messageEvent;
   messageEvent.message="Mouse moved";
   CEventMessage *objMessageEvent=new CEventMessage(messageEvent);
   
   CEventHub hub;
   hub.SetEvent(objMouseEvent);
   
   //--- get mouse event
   CEvent *event=hub.GetEvent();
   if(event.EventType()==EVENT_MOUSE_MOVE)
      {
         CEventMouseMove *m=event;
         MqlEventMouseMove e;
         m.ToStruct(e);
         Print("Mouse x=",e.x," Mouse y=",e.y);
      }
   
   //--- send message
   hub.SetEvent(objMessageEvent);
   //--- get message   
   event=hub.GetEvent();
   if(event.EventType()==EVENT_MESSAGE)
      {
         CEventMessage *m=event;
         MqlEventMessage e;
         m.ToStruct(e);
         Print("Message=",e.message);
      }
      
   delete objMouseEvent;
   delete objMessageEvent;
  }
 
Amir Yacoby:
Looks like a pattern for that kind of problems maybe, reached to a combination between classes and structs.
Here, because structs not support OO fully, the classes are used, but just as struct carriers.
The user creates the classes by supplying the struct, and recieves the data by retrieving the structs.
Each event has also a class, that has nothing public, but the struct it recieves in the constructor, and the struct it returns.

It's about polymorphism and closure.

For the interested people who read this, polymorphism is when objects of different type are grouped as one. Think of a ferry that can load cars. It doesn't matter what cars, Ferraris, Beetles, anything. What matters is the size and weight so they can be loaded on board. Think of a big box that holds the car. No one yet is interested in the content of that box, its just size and weight that matters.

Someone outside that box next harbor wants to know what is in it, and so studies the specs of it. "What's the type of the car?  What's its ID? An Alfa Romeo? Ok, I got someone waiting here, get it out of that box."

And this is difficult to achieve with structs, because they are copied on assignment, unlike classes. It's like you drive up with your car to the ferry and they make a copy of it, then throwing it away. Your car is now a boxed car copy, and the person who is waiting on the other side will get - yet another copy of it. Oh, you lost your sun glasses - you didn't specify it, sorry.

So instead of backing structs with classes, why not use classes first hand.


enum ENUM_EVENT_TYPE
  {
   EVENT_MOUSE_MOVE,
   EVENT_MESSAGE
  };

class CEvent
  {
private:
   ENUM_EVENT_TYPE   event_type;
protected:
                     CEvent(const ENUM_EVENT_TYPE type) {event_type=type;}
public:
   ENUM_EVENT_TYPE   EventType() const {return event_type;}
  };

class CEventMouseMove : public CEvent
  {
public:
   int               x;
   int               y;
                     CEventMouseMove(int px=0,int py=0) : CEvent(EVENT_MOUSE_MOVE)     {x=px; y=py;}
  };

class CEventMessage : public CEvent
  {
public:
   string            message;
                     CEventMessage(string pmessage="") : CEvent(EVENT_MESSAGE)     {message=pmessage;}
  };
  
class CEventHub
  {
   private:
      CEvent        *m_event;
   public:
      void           SetEvent(CEvent *event) { if(CheckPointer(m_event)==POINTER_DYNAMIC) delete m_event; m_event=event; }
      CEvent        *GetEvent() { return m_event; }
      CEventHub()  { m_event=NULL; }
     ~CEventHub()  { if(CheckPointer(m_event)==POINTER_DYNAMIC) delete m_event; }
  };
  
void OnStart()
  {
   CEventMouseMove *mouseEvent=new CEventMouseMove(15,23);
   CEventMessage *messageEvent=new CEventMessage("Mouse moved");
   
   CEventHub hub;
   hub.SetEvent(mouseEvent);
   
   //--- get mouse event
   CEvent *event=hub.GetEvent();
   if(event.EventType()==EVENT_MOUSE_MOVE)
      {
         CEventMouseMove *m=event;
         Print("Mouse x=",m.x," Mouse y=",m.y);
      }
   
   //--- send message
   hub.SetEvent(messageEvent);
   //--- get message   
   event=hub.GetEvent();
   if(event.EventType()==EVENT_MESSAGE)
      {
         CEventMessage *m=event;
         Print("Message=",m.message);
      }
  }
Ok some things around new/delete would need to be sorted out but the hub class here is a stub anyway.
 
lippmaje:

It's about polymorphism and closure.

For the interested people who read this, polymorphism is when objects of different type are grouped as one. Think of a ferry that can load cars. It doesn't matter what cars, Ferraris, Beetles, anything. What matters is the size and weight so they can be loaded on board. Think of a big box that holds the car. No one yet is interested in the content of that box, its just size and weight that matters.

Someone outside that box next harbor wants to know what is in it, and so studies the specs of it. "What's the type of the car?  What's its ID? An Alfa Romeo? Ok, I got someone waiting here, get it out of that box."

And this is difficult to achieve with structs, because they are copied on assignment, unlike classes. It's like you drive up with your car to the ferry and they make a copy of it, then throwing it away. Your car is now a boxed car copy, and the person who is waiting on the other side will get - yet another copy of it. Oh, you lost your sun glasses - you didn't specify it, sorry.

So instead of backing structs with classes, why not use classes first hand.


Ok some things around new/delete would need to be sorted out but the hub class here is a stub anyway.
Of course, it crossed my mind. But then I thought its exactly what I do, use classes. The only difference that my data is structs and yours is raw data. For my needs its preferable, but of course its an option.