MQL4 OOP - Creating Custom Object in One Function Then Calling Object Method in Different Function

 

I'm just messing around with this custom indicator where I am trying to inherit CChartObjectRectangle and add some methods to it. I am not sure how I can create the custom object and keep calling the methods associated with the object in the different event handling functions. If I store pointers to the objects in a global array, I get "XXXX.XX.XX XX:XX:XX.XX ForumQuestion SYMBOL,TIMEFRAME: 1 object of type ExtendableRectangle left" warnings in the console which make sense since the pointers haven't been deleted.

//+------------------------------------------------------------------+
//|                                                ForumQuestion.mq4 |
//|                                                Zainuddin Siddiqi |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "Zainuddin Siddiqi"
#property link      ""
#property version   "1.00"
#property strict
#property indicator_chart_window

#include <ChartObjects\ChartObjectsShapes.mqh>

//--- input parameters
input color rectangleColor = clrGray;               //Rectangle Colour

// store pointers to objects
CChartObjectRectangle *rectangles[];


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class ExtendableRectangle : public CChartObjectRectangle
  {
public:
                     ExtendableRectangle(long chart_id, const string name, const int window, const datetime time1, const double price1, const datetime time2, const double price2, const color clr);
   bool              Extend(int extendBars=1);
  };


//+------------------------------------------------------------------+
//| Constructor                                              |
//+------------------------------------------------------------------+
ExtendableRectangle::ExtendableRectangle(long chart_id, const string name, const int window, const datetime time1, const double price1, const datetime time2, const double price2, const color clr)
  {
   CChartObjectRectangle::Create(chart_id, name, window, time1, price1, time2, price2);

   CChartObject::Color(rectangleColor);
   CChartObject::Timeframes(OBJ_PERIOD_MN1|OBJ_PERIOD_W1|OBJ_PERIOD_D1|OBJ_PERIOD_H4);
   CChartObject::Hidden(true);

   ExtendableRectangle::Extend(5);
  }


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool ExtendableRectangle::Extend(int extendBars=1)
  {
   datetime time = Time[0] + (PeriodSeconds(Period()) * extendBars);

   return CChartObject::SetInteger(OBJPROP_TIME2, time);
  }


//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   ChartSetInteger(0, CHART_EVENT_OBJECT_CREATE, true);
   ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, true);
   return(INIT_SUCCEEDED);
  }


//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   for(int i=0; i < ArraySize(rectangles); i++)
     {
      ExtendableRectangle *rect = rectangles[i];
      rect.Extend(5);
     }

   return(rates_total);
  }


//+------------------------------------------------------------------+
//| Custom indicator event function                                  |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long& lparam,
                  const double& dparam,
                  const string& sparam
                 )
  {
   if(id == CHARTEVENT_OBJECT_CREATE && ObjectType(sparam) == OBJ_RECTANGLE)
     {
      double price1 = ObjectGetDouble(0, sparam, OBJPROP_PRICE1);
      double price2 = ObjectGetDouble(0, sparam, OBJPROP_PRICE2);

      datetime time1 = ObjectGetInteger(0, sparam, OBJPROP_TIME1);
      datetime time2 = ObjectGetInteger(0, sparam, OBJPROP_TIME2);

      // delete original rectangle
      ObjectDelete(0, sparam);

      // create extendable rectangle and add to array
      string rectangleName = StringFormat("%s %s", PeriodToString(), sparam);
      ExtendableRectangle *rect = new ExtendableRectangle(0, rectangleName, 0, time1, price1, time2, price2, rectangleColor);

      ArrayResize(rectangles, ArraySize(rectangles) + 1);
      rectangles[ArraySize(rectangles) - 1] = rect;
     }
  }


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
string PeriodToString(int period=NULL)
  {
   if(period == NULL)
      period = Period();

   return StringSubstr(EnumToString((ENUM_TIMEFRAMES) period), 7);
  }
//+------------------------------------------------------------------+
Am I trying to implement a flawed pattern that is not expected in MQL4? Is there a better way to achieve this?
 
Zainuddin Siddiqi:

I'm just messing around with this custom indicator where I am trying to inherit CChartObjectRectangle and add some methods to it. I am not sure how I can create the custom object and keep calling the methods associated with the object in the different event handling functions. If I store pointers to the objects in a global array, I get "XXXX.XX.XX XX:XX:XX.XX ForumQuestion SYMBOL,TIMEFRAME: 1 object of type ExtendableRectangle left" warnings in the console which make sense since the pointers haven't been deleted.

Am I trying to implement a flawed pattern that is not expected in MQL4? Is there a better way to achieve this?

You can store the Extendable pointers too , and you can use ondeinit to delete them . 

//+------------------------------------------------------------------+
//|                                                ForumQuestion.mq4 |
//|                                                Zainuddin Siddiqi |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "Zainuddin Siddiqi"
#property link      ""
#property version   "1.00"
#property strict
#property indicator_chart_window

#include <ChartObjects\ChartObjectsShapes.mqh>

//--- input parameters
input color rectangleColor = clrGray;               //Rectangle Colour




//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class ExtendableRectangle : public CChartObjectRectangle
  {
public:
                     ExtendableRectangle(long chart_id, const string name, const int window, const datetime time1, const double price1, const datetime time2, const double price2, const color clr);
   bool              Extend(int extendBars=1);
  };
// store pointers to objects
ExtendableRectangle *rectangles[];

//+------------------------------------------------------------------+
//| Constructor                                              |
//+------------------------------------------------------------------+
ExtendableRectangle::ExtendableRectangle(long chart_id, const string name, const int window, const datetime time1, const double price1, const datetime time2, const double price2, const color clr)
  {
   CChartObjectRectangle::Create(chart_id, name, window, time1, price1, time2, price2);

   CChartObject::Color(rectangleColor);
   CChartObject::Timeframes(OBJ_PERIOD_MN1|OBJ_PERIOD_W1|OBJ_PERIOD_D1|OBJ_PERIOD_H4);
   CChartObject::Hidden(true);

   ExtendableRectangle::Extend(5);
  }


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool ExtendableRectangle::Extend(int extendBars=1)
  {
   datetime time = Time[0] + (PeriodSeconds(Period()) * extendBars);

   return CChartObject::SetInteger(OBJPROP_TIME2, time);
  }


//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   ChartSetInteger(0, CHART_EVENT_OBJECT_CREATE, true);
   ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, true);
   return(INIT_SUCCEEDED);
  }

void OnDeinit(const int reason){
for(int i=0;i<ArraySize(rectangles);i++){
   delete(rectangles[i]);
   }
   ArrayFree(rectangles);
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   for(int i=0; i < ArraySize(rectangles); i++)
     {
      rectangles[i].Extend(5);
     }

   return(rates_total);
  }


//+------------------------------------------------------------------+
//| Custom indicator event function                                  |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long& lparam,
                  const double& dparam,
                  const string& sparam
                 )
  {
   if(id == CHARTEVENT_OBJECT_CREATE && ObjectType(sparam) == OBJ_RECTANGLE)
     {
      double price1 = ObjectGetDouble(0, sparam, OBJPROP_PRICE1);
      double price2 = ObjectGetDouble(0, sparam, OBJPROP_PRICE2);

      datetime time1 = ObjectGetInteger(0, sparam, OBJPROP_TIME1);
      datetime time2 = ObjectGetInteger(0, sparam, OBJPROP_TIME2);

      // delete original rectangle
      ObjectDelete(0, sparam);

      // create extendable rectangle and add to array
      string rectangleName = StringFormat("%s %s", PeriodToString(), sparam);
      ExtendableRectangle *rect = new ExtendableRectangle(0, rectangleName, 0, time1, price1, time2, price2, rectangleColor);

      ArrayResize(rectangles, ArraySize(rectangles) + 1);
      rectangles[ArraySize(rectangles) - 1] = rect;
     }
  }


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
string PeriodToString(int period=NULL)
  {
   if(period == NULL)
      period = Period();

   return StringSubstr(EnumToString((ENUM_TIMEFRAMES) period), 7);
  }
//+------------------------------------------------------------------+
 
Lorentzos Roussos #:

You can store the Extendable pointers too , and you can use ondeinit to delete them . 

Thank you for your answer. My understanding is that the OnDeinit function will be called if the chart period changes (https://docs.mql4.com/constants/namedconstants/uninit), which means that if I delete the pointers in that function the objects are also deleted from the chart. I would like to keep the objects even if the chart period changes. Am I correct in saying this?
Uninitialization Reason Codes - Named Constants - Constants, Enumerations and Structures - MQL4 Reference
Uninitialization Reason Codes - Named Constants - Constants, Enumerations and Structures - MQL4 Reference
  • docs.mql4.com
Uninitialization Reason Codes - Named Constants - Constants, Enumerations and Structures - MQL4 Reference
 
Zainuddin Siddiqi #:
Thank you for your answer. My understanding is that the OnDeinit function will be called if the chart period changes (https://docs.mql4.com/constants/namedconstants/uninit), which means that if I delete the pointers in that function the objects are also deleted from the chart. I would like to keep the objects even if the chart period changes. Am I correct in saying this?
void OnDeinit(const int reason)

Just use the reason parameter to filter out timeframe changes.

 
Laszlo Tormasi #:

Just use the reason parameter to filter out timeframe changes.

Good suggestion, I think it will be enough for now even though the warning is still expectedly there when the chart period is changed.

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(_UninitReason != REASON_CHARTCHANGE)
     {
      for(int i=0; i<ArraySize(rectangles); i++)
        {
         delete(rectangles[i]);
        }
      ArrayFree(rectangles);
     }
  }
 
Zainuddin Siddiqi #:

Good suggestion, I think it will be enough for now even though the warning is still expectedly there when the chart period is changed.

Delete the pointers either way. Just keep the chart graphical objects and recreate the class objects based on them in oninit. 

 
Laszlo Tormasi #:

Delete the pointers either way. Just keep the chart graphical objects and recreate the class objects based on them in oninit. 

When I delete the pointers, the objects are also deleted. How can I delete a pointer but not the graphical object?
 
Zainuddin Siddiqi #:
When I delete the pointers, the objects are also deleted. How can I delete a pointer but not the graphical object?
Use the Detach() method before deleting the pointer.