About generic code

 

There was a post here about generic code, making an impressive effort and attempt to solve that with template methods.

The main drawback of the template methods presented here, is that a template method can not be a virtual method and thus can not be used in building a class library.

Hence, I suggest what is in my opinion a more usable approach to building libraries. It is slightly better than a previous version I had that used 3 classes, this one uses only two classes, where IMHO one can be bypassed if MQ will make a slight effort to improve the methods selection.

For now, there is a need for one class that takes all the reference data, and one extension to use only for those methods that the user pass a T type in the method signature. The number of signatures for each method is 1 (passing the value/pointer to the reference base method).

IMHO MQ can make it true generic, by saving us from the need to write a second class for values by making the reference types accept primitive value/pointer:

1. Allow to pass a primitive literal value to a reference method (like C++ does) if it's the only signature that is available.

2. Allow to pass an object pointer to a reference method 

As you can see from the code example, QueueVal only passes the primitive value literal or pointer to the QueueBase reference method, so there might be no big issue for MQ to allow that, unless I miss something.

But without MQ's effort, this is my preffered solution for a true generic library

template <typename T>
class QueueBase
  {
private:
   T                 _arr[];
   int               m_begin,m_end;
   int               m_count;
protected:
   T                 m_null;
public:
   void              QueueBase() {m_count=0;}
   void             ~QueueBase() {};
   //---
   void              Push(T &val);
   void              Push(T &val[]);
   int               Size() {return m_count;}
   T                 Pop();
   T                 Peek();
  };
template <typename T>
T QueueBase::Pop()
  {
   if(m_count<=0)
      return m_null;
   return _arr[--m_count];
  }
template <typename T>
T QueueBase::Peek()
  {
   if(m_count<=0)
      return m_null;
   return _arr[m_count-1];
  }
template <typename T>
void QueueBase::Push(T &val)
  {
   T val1=val;
   ArrayResize(_arr,m_count+1);
   _arr[m_count++]=val1;
  }
template <typename T>
void QueueBase::Push(T &val[])
  {
   ArrayResize(_arr,ArraySize(val));
   for(int i=0; i<ArraySize(val); i++)
     {
      m_count++;
      _arr[i]=val[i];
     }
  }

//+------------------------------------------------------------------+
//| For objects and primitives                                       |
//+------------------------------------------------------------------+
template <typename T>
class QueueVal : public QueueBase<T>
  {
public:
   void              QueueVal()  {m_null=(T)NULL;}
   void              Push(T val) {QueueBase<T>::Push(val);}
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class Test
  {
private:
   int               m_id;
public:
                     Test(const int id)
      :              m_id(id) {}
   int               Get() const {return(m_id);}
  };
//+------------------------------------------------------------------+
//|                                                                                  |
//+------------------------------------------------------------------+
class MyTest : public Test
  {
public:
                     MyTest(const int id)
      :              Test(id) {}
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- definitions
   QueueBase<MqlRates> q_rates;
   QueueVal<Test *> q_class;
   QueueVal<double>q_dbl;
   QueueVal<int> q_int;
   QueueVal<ENUM_TIMEFRAMES>q_enum;

//--- int
   int a=5;
   q_int.Push(3);
   q_int.Push(a);
   int a1=q_int.Pop();
   int a2=q_int.Pop();
   Print(a1==a&&a2==3);

//--- double
   double d1=3.14;
   q_dbl.Push(d1);
   q_dbl.Push(6.28);
   Print(q_dbl.Pop()==6.28&&q_dbl.Pop()==d1);

//--- struct
   MqlRates r[3];
   CopyRates(_Symbol,_Period,1,3,r);
   q_rates.Push(r[0]);
   q_rates.Push(r[1]);
   q_rates.Push(r[2]);

   MqlRates ro[3];
   ro[2]=q_rates.Pop();
   ro[1]=q_rates.Pop();
   ro[0]=q_rates.Pop();

   Print(ArrayCompare(r,ro)==0);

//--- struct array as parameter
   q_rates.Push(r);
   MqlRates ro_arr[3];
   ro_arr[2]=q_rates.Pop();
   ro_arr[1]=q_rates.Pop();
   ro_arr[0]=q_rates.Pop();
   Print(ArrayCompare(r,ro_arr)==0);

//--- enum
   q_enum.Push(PERIOD_M1);
   q_enum.Push(PERIOD_M2);

   Print(q_enum.Pop()==PERIOD_M2&&q_enum.Pop()==PERIOD_M1);
//--- class pointer
   q_class.Push(new Test(10));
   q_class.Push(new Test(20));
   q_class.Push(new MyTest(30));
   Print(q_class.Pop().Get()==30&&q_class.Pop().Get()==20&&q_class.Pop().Get()==10);
  }
The Use of the MQL5 Standard Trade Class libraries in writing an Expert Advisor
The Use of the MQL5 Standard Trade Class libraries in writing an Expert Advisor
  • www.mql5.com
This article explains how to use the major functionalities of the MQL5 Standard Library Trade Classes in writing Expert Advisors which implements position closing and modifying, pending order placing and deletion and verifying of Margin before placing a trade. We have also demonstrated how Trade classes can be used to obtain order and deal details.
 
Amir Yacoby:

There was a post here about generic code, making an impressive effort and attempt to solve that with template methods.

The main drawback of the template methods presented here, is that a template method can not be a virtual method and thus can not be used in building a class library.

Hence, I suggest what is in my opinion a more usable approach to building libraries. It is slightly better than a previous version I had that used 3 classes, this one uses only two classes, where IMHO one can be bypassed if MQ will make a slight effort to improve the methods selection.

For now, there is a need for one class that takes all the reference data, and one extension to use only for those methods that the user pass a T type in the method signature. The number of signatures for each method is 1 (passing the value/pointer to the reference base method).

IMHO MQ can make it true generic, by saving us from the need to write a second class for values by making the reference types accept primitive value/pointer:

1. Allow to pass a primitive literal value to a reference method (like C++ does) if it's the only signature that is available.

2. Allow to pass an object pointer to a reference method 

As you can see from the code example, QueueVal only passes the primitive value literal or pointer to the QueueBase reference method, so there might be no big issue for MQ to allow that, unless I miss something.

But without MQ's effort, this is my preffered solution for a true generic library

That's a very clean solution.

The limitation imposed by the compiler can obviously only be circumvented by either using two object classes to separate the functions into two scopes, or by addressing them in a separate template scope within one object.

The fact, templates functions cannot be virtual and templates cannot be overloaded, is hindering the combination into one object scope.

Since I haven't compiled your code jet, is it necessary to have the template definitions within the class on each function? Or is it enough to have the template on class scope?

So, I would link this post to my original thread as well, as a second solution.

It is not exactly what I was aiming for, because it does use two object types, and that was one of my starting points, but I think it is clean and valid, and therefore should also be considered a solution.

Here is the original thread


 
Dominik Egert #:

Since I haven't compiled your code jet, is it necessary to have the template definitions within the class on each function? Or is it enough to have the template on class scope?



The template is only on the class scope
 
Amir Yacoby #:
The template is only on the class scope
So you can omit the template argument on each function then.
 
Dominik Egert #:
So you can omit the template argument on each function then.
Oh yes, if you place the method definitions inside the class {} brackets. But as I declared them outside, you have to specify them because the class name is CQueuBase<int> for it to be distinguished from another class CQueuBase<double> etc. Like in C++ btw.

So they rrefer  to the class name and not the method name
 
Amir Yacoby #:
Oh yes, if you place the method definitions inside the class {} brackets. But as I declared them outside, you have to specify them because the class name is CQueuBase<int> for it to be distinguished from another class CQueuBase<double> etc. Like in C++ btw.

So they rrefer  to the class name and not the method name
Right, I didnt see that.