How to pass structureArray in a Class method Call ?

 

UPDATE NOTE: I have found the solution, with the help of Drazen (https://www.mql5.com/en/users/drazen64) from the forum.

Dear Forum Members

I am having challenge of passing structure Array as 'parameter reference' to the test EA.

Following is the CLASS code with relevant sections

class CiSwing_HighLow
  {
private:
  //--- define structure AND structureArrays
    struct s_IndexPrice
      {
        int             index;                // to store index value of bar
        double          price;                // to store price value of bar
      };
    s_IndexPrice        sa_MajorLow[];
  //--- class variable for parameters
    string              m_Symbol;
    ENUM_TIMEFRAMES     m_TimeFrame;
    int                 m_Handle;             // Indicator handle
    string              m_IndicatorName;      // iCustom needs Custom Indicator name located in Indicator folder
  //--- Parameters for Swing High Low Indicator
    uint                m_SlowLength;         // Zigzag Slow Period
    uint                m_FastLength;         // Zigzag Fast Period
    int                 m_LookBack;           // No of Bars to look back
  //--- Instance of other Class(s)
    //CLog*             m_log;                // Logging

public:
  //--- Parametric Contstructor and default Destructor methods
          CiSwing_HighLow(string pSymbol,ENUM_TIMEFRAMES pTimeFrame,uint pSlowLength,uint pFastLength,
                          int pBarsLookBack);
         ~CiSwing_HighLow(void);
    void  Init_Handle(void);
  //--- Specific methods to get values from Custome Indicator 'iFx SwingHighLow'
    bool  Get_MajorLow(s_IndexPrice &p_saMajorLow[]);
  };
//+----------------------------------------------------------------------------------------------------------+
//| METHOD:       Get_MajorLow()
//| APPLICATION:  to return Major Swing Low 'Index' values in the specified Array
//| BUFFER:       0 = Major Swing Low
//| INPUT Parameter   arrayOfStructure to store Index and Price values MajorLow[i].Index & MajorLow[i].Price
//+----------------------------------------------------------------------------------------------------------+
bool CiSwing_HighLow::Get_MajorLow(s_IndexPrice &p_saMajorLow[])
  {
  //--- Data Preperation, Copy Buffer 0 values from iFx SwingHighLow Indicator to temp array
    double  tempArray[];
    if(m_Handle == INVALID_HANDLE)
      Init_Handle();
    ArrayResize(tempArray,m_LookBack+1);
    ArraySetAsSeries(tempArray,true);
    ResetLastError();
    if(!CopyBuffer(m_Handle,0,0,m_LookBack,tempArray))   // Copy 'Buffer#0' from Index[0] to m_LookBack Bars
      {
        Print(__FUNCTION__,": Error CopyBuffer Swing MajorLOW prices. Code ",GetLastError());
        return(false);
      }
  //--- Select MajorLow 'index' & 'price' into structureArray 'pMajorLow'
    int count = 0;
    for(int i = 0; i <= m_LookBack && !IsStopped(); i++) // loop from Index[1], completed bar to specified bars
      {
        if((tempArray[i] != 0) && (count <= 10))
          {
            if(ArraySize(p_saMajorLow) < 10)
              ArrayResize(p_saMajorLow,11);
            ArraySetAsSeries(p_saMajorLow,true);
            p_saMajorLow[count].index = i;
            p_saMajorLow[count].price = tempArray[i];
            count++;
          }
        if(count > 10)
          return(true);
      } // END Of for..loop to fill structureArray of Index & Price
  //---
    ArrayFree(tempArray);
    return(true);
  } // END Of Get_Value() method
//+----------------------------------------------------------------------------------------------------------+

 The EA Code


  #include "\..\Indicator\CiSwing HighLow.mqh"
//+----------------------------------------------------------------------------------------------------------+
//| Define 'Structure' and structure based 'Array'
//+----------------------------------------------------------------------------------------------------------+
  struct sIndexPrice
    {
      int     index;    // store high/low bar index
      double  price;    // store high/low bar price
    };
//+----------------------------------------------------------------------------------------------------------+
//| Define 'global variables' used in EA
//+----------------------------------------------------------------------------------------------------------+
  string              ea_Symbol         = _Symbol;
  ENUM_TIMEFRAMES     ea_TFM05          = PERIOD_M5;
  //--- "Parameters for Swing High/Low Indicator"
  uint                SlowLength        = 13;           // Slow Length for Zigzag Period
  uint                FastLength        = 5;            // Fast Length for Zigzan Period
  int                 BarsLookBack      = 500;          // No of Bars to lookback for High Low Prices
//+----------------------------------------------------------------------------------------------------------+
//| INCLUDE MQL5 or Custom 'CLASS' files and create instances of them
//+----------------------------------------------------------------------------------------------------------+
  CiSwing_HighLow     SwingHL_M05(ea_Symbol,ea_TFM05,SlowLength,FastLength,BarsLookBack);
  sIndexPrice         M05_saMajorLow[];
//+----------------------------------------------------------------------------------------------------------+
//| Expert initialization function
//+----------------------------------------------------------------------------------------------------------+
int OnInit()
  {
//---
    SwingHL_M05.Init_Handle();
//---
    return(INIT_SUCCEEDED);
  }
//+----------------------------------------------------------------------------------------------------------+
//| Expert de initialization function
//+----------------------------------------------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
  }
//+----------------------------------------------------------------------------------------------------------+
//| Expert tick function
//+----------------------------------------------------------------------------------------------------------+
void OnTick()
  {
    int barStart = 0, barCount = 5, index = 1;

  //---
    if(IsNewBar())    // Check every 5 minute ...
      {
        SwingHL_M05.Get_MajorLow(M05_saMajorLow); // causing error parameter conversion not allowed !!!
        for(int i = 0; i <= 10 && !IsStopped(); i++)
          {
            Print("Major Low [",i,"] ",IntegerToString(M05_saMajorLow[i].index,0)," Price ",DoubleToString(M05_saMajorLow[i].price,5));
          }
      }
  } // END Of expert advisor
//+----------------------------------------------------------------------------------------------------------+
//| METHOD:       IsNewBar()
//| APPLICATION:  Check if it is a New Candle on specified instrument & Time Frame
//+----------------------------------------------------------------------------------------------------------+
bool IsNewBar(void)
{
  bool   newBar;
  static datetime dtBarCurrent  = WRONG_VALUE;
         datetime dtBarPrevious = dtBarCurrent;
  // "SERIES_LASTBAR_DATE" Open time of the last bar of the symbol - period
  dtBarCurrent = (datetime)SeriesInfoInteger(ea_Symbol,ea_TFM05,SERIES_LASTBAR_DATE);  
  if(dtBarCurrent != dtBarPrevious)  newBar = true;
  else                               newBar = false;
  //---
  return(newBar);
} // END Of IsNewBar()
//+----------------------------------------------------------------------------------------------------------+
 

Hi Anil,

why you are defining the structure sIndexPrice in the EA instead of using the s_IndexPrice struct defined
in the class CiSwing_HighLow?

//+----------------------------------------------------------------------------------------------------------+
//| Define 'Structure' and structure based 'Array'
//+----------------------------------------------------------------------------------------------------------+
/*  struct sIndexPrice
    {
      int     index;    // store high/low bar index
      double  price;    // store high/low bar price
    }; */
//+----------------------------------------------------------------------------------------------------------+
//| Define 'global variables' used in EA
//+----------------------------------------------------------------------------------------------------------+
  string              ea_Symbol         = _Symbol;
  ENUM_TIMEFRAMES     ea_TFM05          = PERIOD_M5;
  //--- "Parameters for Swing High/Low Indicator"
  uint                SlowLength        = 13;           // Slow Length for Zigzag Period
  uint                FastLength        = 5;            // Fast Length for Zigzan Period
  int                 BarsLookBack      = 500;          // No of Bars to lookback for High Low Prices
//+----------------------------------------------------------------------------------------------------------+
//| INCLUDE MQL5 or Custom 'CLASS' files and create instances of them
//+----------------------------------------------------------------------------------------------------------+
  CiSwing_HighLow     SwingHL_M05(ea_Symbol,ea_TFM05,SlowLength,FastLength,BarsLookBack);
  s_IndexPrice         M05_saMajorLow[];

I hope, this will help you.

Best regards

 

You have two different declarations of the structure - one inside the class, another inside the EA code.

Even if you give them the same name, the compiler will treat them as different types.

Solution is to create one declaration of that struct and use it throughout the project.

 

Thanks Drazen

I have tried in your suggested way, but still it did not work.

Also I am facing another issue while trying to call 3 EMAs.

// structure defined in the CLASS ...
    struct s_3EMAPrices
      {
        double  Fastest;
        double  Fast;
        double  Slow;
      };
    s_3EMAPrices        sa_3EMA[];

// Call for CopyBuffer from iCustom for 3 separate buffers ...

bool CiVW_3EMAs::Get_Value(const int pBarCount,s_3EMAPrices &p3EMA[])
  {
    int barStart = 0;
    if(m_Handle == INVALID_HANDLE)
      Init_Handle();
  //--- Array to be filled and returned with Volume Weighted EMA values
    ArrayResize(p3EMA,pBarCount+1);
    ArraySetAsSeries(p3EMA,true);

  //--- Copy Buffer values from Custom Indicator "iFx VW_3EMAs"
    ResetLastError();
    if(!CopyBuffer(m_Handle,0,barStart,pBarCount,pEMAFastest))	// HOW TO DEFINE STRUCTURE_ARRAY IN THIS CALL ?
      {
        Print(__FUNCTION__,": FAILED CopyBuffer iFx VW_3EMAs Buffer = 0 (Fastest).Code ",GetLastError());
        return(false);
      }
    if(!CopyBuffer(m_Handle,1,barStart,pBarCount,pEMAFast))	// HOW TO DEFINE !
      {
        Print(__FUNCTION__,": FAILED CopyBuffer iFx VW_3EMAs Buffer = 1 (Fast).Code ",GetLastError());
        return(false);
      }
    if(!CopyBuffer(m_Handle,2,barStart,pBarCount,pEMASlow))	// HOW TO DEFINE !
      {
        Print(__FUNCTION__,": FAILED CopyBuffer iFx VW_3EMAs Buffer = 2 (Slow).Code ",GetLastError());
        return(false);
      }
  //---
    return(true);
  } // END Of method
 

You should post complete example and error description.

But, judging from the comment in the first line:

// structure defined in the CLASS ...

you defined struct inside the class?

If that is the case, that definition is private to the class and is not visible outside of that class.


 

 
Drazen Penic:

You should post complete example and error description.

But, judging from the comment in the first line:

you defined struct inside the class?

If that is the case, that definition is private to the class and is not visible outside of that class.


 

wow, you gave perfect solution.

Yes I have defined it within CLASS earlier. Now defined structure before defining the class and it is working well.

Any suggestions, how to include strutureArray into a CopyBuffer call ?

bool CiVW_3EMAs::Get_3EMAs(const int pBarCount,s_3EMA &p3EMA[].Fastest,double &pEMAFast[],double &pEMASlow[])
  {
    if(!CopyBuffer(m_Handle,0,barStart,pBarCount,p3EMA.Fastest)) DOES NOT WORK !!!

Thanks a lot Drazen.

Regards.

 

You can't "extract" double array from your struct array.

If you want to fill field Fastest in each struct in your array, you will have to declare helper array in your  function, fill that array with CopyBuffer and then copy those values into the struct array.

Something like this:

bool CiVW_3EMAs::Get_3EMAs(const int pBarCount,s_3EMA &p3EMA[],double &pEMAFast[],double &pEMASlow[])
{
    double helperArray[];
    if(!CopyBuffer(m_Handle,0,barStart,pBarCount,helperArray)) 
    {
    }
    else
    {
      for(int i = 0; i < pBarCount; i++)
       p3EMA[i].Fastest = helperArray[i];
    }
...

Code is not tested, but you should get the idea.

 
Drazen Penic:

You can't "extract" double array from your struct array.

If you want to fill field Fastest in each struct in your array, you will have to declare helper array in your  function, fill that array with CopyBuffer and then copy those values into the struct array.

Something like this:

Code is not tested, but you should get the idea.

Hi Drazen

you have innovative ideas, I must appreciate :)

Yeh, I got the idea, and could try to implement it. But before that, would like to know, will it be optimal way of coding OR simple 3 arrays for each EMA should be fine ?

I will be looking at the most previous 10 bars maximum for EMAs, so data handling is not too much.

StructureArray will give ease of having only one Array to manage, otherwise I will have 3 arrays to manage.

Through some lights on it.

PS: there is another thread https://www.mql5.com/en/forum/370223 where I am facing error 4802. If possible, please have a look on it. PLEASE forgive me if I have asked too much :).

Thanks a lot for your support.

 
Anil Varma -:

Hi Drazen

you have innovative ideas, I must appreciate :)

Yeh, I got the idea, and could try to implement it. But before that, would like to know, will it be optimal way of coding OR simple 3 arrays for each EMA should be fine ?

I will be looking at the most previous 10 bars maximum for EMAs, so data handling is not too much.

StructureArray will give ease of having only one Array to manage, otherwise I will have 3 arrays to manage.

Through some lights on it.

PS: there is another thread https://www.mql5.com/en/forum/370223 where I am facing error 4802. If possible, please have a look on it. PLEASE forgive me if I have asked too much :).

Thanks a lot for your support.

It's not innovative at all. But it's not Drazen's responsibility, it's a BAD idea to work with a structure like you are doing. mql5 is optimized to work with arrays, most of the time arrays of double, working with an arrays of structure with double member you completely lost the advantage of arrays. You will get poor performance.

It's far better to use a struct of arrays.

So

So use 
      struct series
        {
         double      data1[];
         double      data2[];
         datetime    data3[];
         ...
        }MyData;
Instead of 
      struct serie
        {
         double      data1;
         double      data2;
         datetime    data3;
         ...
        }MyDatas[];
 
Alain Verleyen:

It's not innovative at all. But it's not Drazen's responsibility, it's a BAD idea to work with a structure like you are doing. mql5 is optimized to work with arrays, most of the time arrays of double, working with an arrays of structure with double member you completely lost the advantage of arrays. You will get poor performance.

It's far better to use a struct of arrays.

So

Thanks a lot Alan,

yeh it make sense better. Why we newbies cann't think of it LOL

I will give it a try and revert if any problem.

UPDATE:

Hi Allan

I have tried your suggestions and it seems working, as did not get any compilation errors. However, when running Test EA, having error 4802 (https://www.mql5.com/en/forum/370223) hope you can help.

Thanks a lot in advance

Help me debug error 4802
Help me debug error 4802
  • 2021.05.28
  • www.mql5.com
Dear Forum Members It is never easy to use iCustom function in MQL5. Please help me to debug why I am getting error 4802...
 
Alain Verleyen:

It's not innovative at all. But it's not Drazen's responsibility, it's a BAD idea to work with a structure like you are doing. mql5 is optimized to work with arrays, most of the time arrays of double, working with an arrays of structure with double member you completely lost the advantage of arrays. You will get poor performance.

It's far better to use a struct of arrays.

So

Hi Alain

I just thought of MqlRate structure, which is basically same way as Drazen has suggested.

struct MqlRates
  {
   datetime  time;          // Period start time
   double    open;          // Open price
   double    high;          // The highest price of the period
   double    low;           // The lowest price of the period
   double    close;         // Close price
   long      tick_volume;   // Tick volume
   int       spread;        // Spread
   long      real_volume // Trade volume
  };

In my opinion, this approach should be used when when we have different data for a single Row (Bar), so your index value remain same and you can get specified value on that index. That is what MqlRates does and in my case High/Low bar index with its price make sense to keep Drazen suggestion. With your suggestion one might error in specifying different index values and get wrong results.

Otherwise it seems (I am just guessing) both approach are optimal in MQL, depending on situation we can use either one. e.g. CopyBuffer your approach seems easy, while cases where we have to assign values the other approach could be used.

Seeks your advise.

Reason: