Writing generic code in MQL for Objects [solved] - page 5

 
Alain Verleyen #:

I continued the analysis of your long post, with this " But here comes the twist: ... "

This isn't something different from the first comparison, same issue. So my post #37 is already synthetizing it.

Next one :

This is perfectly logic, this is not allowed at all, because it makes no sense, it's exactly the same, and the const is superfluous.

Then :

Yes this is perfectly clear (to me at least ).

So we come to your first EDIT:

Because the choice of templates is not the same. You have there 3 template versions for __Call which are also using __func, while for the __func() you have 1 non-template and 2 templates. That leads to the difference.

To terminate, I just don't understand your EDIT 2. It's unclear in what context you are talking.


So all in all, from my point of view my post #37 is will summarizing the main issue. We need to get an explanation about it and don't waste more time until we have one. Everything else follows from there.

I applied your layed out logic. find results in attached file. - It seems solved, at least for my use-case, but still quite some questions stay unanswered. - I would like to encourage you to go out and ask MQ about it. - Right now I have nothing to add, I would need a few more days to come back to this.


But for now, I am able to formulate member functions in such a way, that they accept every datatype I require/thats available.. - This is a huge step forward for me and I am greatful for your input.

Attached file has all relevant details (in code) to write/code an object thats able to take in any compatible datatype and work with it, with no restrictions imposed. - For anyone who is interested and may benefit from such an approach.

My use case is for having "unified" code for data storage objects in the form of iE: tree<int>, linked_list<some_struct>, container<class_objects>, map<pointer*> , fifo_buffer<> and so  on....

This makes my library much more universal and is a great advantage for me. - Finally stepping away from awkward limited code.

EDIT:

Updated attached file. - Cleaned up "const" usage in signatures to enable object to be deduced to "<const>" as well.

Files:
Playground.mq5  41 kb
 
Dominik Christian Egert #:

I applied your layed out logic. find results in attached file. - It seems solved, at least for my use-case, but still quite some questions stay unanswered. - I would like to encourage you to go out and ask MQ about it. - Right now I have nothing to add, I would need a few more days to come back to this.

Sure I will do.

But for now, I am able to formulate member functions in such a way, that they accept every datatype I require/thats available.. - This is a huge step forward for me and I am greatful for your input.

You are most welcome.

 
Dominik Egert #:


This makes my library much more universal and is a great advantage for me. - Finally stepping away from awkward limited code.

EDIT:

Updated attached file. - Cleaned up "const" usage in signatures to enable object to be deduced to "<const>" as well.

Can you try to break that down back to small blocks for specific use cases in order to make it somewhat useful?  

 
Amir Yacoby #:

Can you try to break that down back to small blocks for specific use cases in order to make it somewhat useful?  

Honestly, I have no idea what you are asking from me.

The goal of this "endeavor" was to have an object/class template that can work with any data type.

MQL is a bit "clumsy" when comes to "universal code" or generic code. So it takes some effort to make it aktually work with any "type name".

The example given has achieved that, how would you want this to be broken down?
 
Dominik Egert #:
Honestly, I have no idea what you are asking from me.

The goal of this "endeavor" was to have an object/class template that can work with any data type.

MQL is a bit "clumsy" when comes to "universal code" or generic code. So it takes some effort to make it aktually work with any "type name".

The example given has achieved that, how would you want this to be broken down?

For instance, try to write a queue class showing what it takes to do it.

I think that is way more than clumsy, it is totally non practical. Even if it can somehow be done, it is non practical at all.

But maybe you can do it.

 
Amir Yacoby #:

For instance, try to write a queue class showing what it takes to do it.

I think that is way more than clumsy, it is totally non practical. 

But maybe you can do it.

Oh, ok. I see where you are heading.

It was already around 3 weeks of work to get all this working the way I showed.

But, maybe I'll post a filo buffer example. Currently I am working on my own library. Once that is done, I might update my other parts and share the filo buffer as an example.

Until then, if you are interested in using this for your own, it might be useful for you to understand what is required to make it work.

As far as I am aware, everything required is in the last file I posted. And in fact it should be possible to pick up from there and write your own code into the "example" template I have provided.


 
Dominik Egert #:
Oh, ok. I see where you are heading.

It was already around 3 weeks of work to get all this working the way I showed.

But, maybe I'll post a filo buffer example. Currently I am working on my own library. Once that is done, I might update my other parts and share the filo buffer as an example.

Until then, if you are interested in using this for your own, it might be useful for you to understand what is required to make it work.

As far as I am aware, everything required is in the last file I posted. And in fact it should be possible to pick up from there and write your own code into the "example" template I have provided.


I tried to understand your example, but I feel relunctant to write such code, even if it will work somehow.

I proposed once another alternative, although not 100 % totally universal (as the const parameters will probably fail, and maybe some other exotic issues), but for me way more understnadable and maintainable.

Edit: Added the class example at the end

template <typename T>
class QueueBase
  {
private:
   T                 _arr[];
   int               m_begin,m_end;
   int               m_count;
protected:
   void              PushItem(T &val);
   void              PushItem(T &val[]);
   T                 m_null;
public:
   void              QueueBase() {m_count=0;}
   void             ~QueueBase() {};
   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::PushItem(T &val)
  {
   T val1=val;
   ArrayResize(_arr,m_count+1);
   _arr[m_count++]=val1;
  }
template <typename T>
void QueueBase::PushItem(T &val[])
  {
   ArrayResize(_arr,ArraySize(val));
   for(int i=0; i<ArraySize(val); i++)
     {
      m_count++;
      _arr[i]=val[i];
     }
  }
//+------------------------------------------------------------------+
//| For structs                                                      |
//+------------------------------------------------------------------+
template <typename T>
class QueueRef : public QueueBase<T>
  {
public:
   void              QueueRef() {T null= {0}; m_null=null;}
   void              Push(T &val) {PushItem(val);};
   void              Push(T &val[]) {PushItem(val);};
  };

//+------------------------------------------------------------------+
//| For objects and primitives                                       |
//+------------------------------------------------------------------+
template <typename T>
class QueueVal : public QueueBase<T>
  {
public:
   void              QueueVal()  {m_null=(T)NULL;}
   void              Push(T val) {PushItem(val);}
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class Test
  {
private:
   int               m_id;
public:
                     Test(const int id)
      :              m_id(id) {}
   int               Get() const {return(m_id);}
  };

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- definitions
   QueueRef<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);

//const MqlRates rc_arr[3];
//q_rates.Push(rc_arr);

//--- 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));
   Print(q_class.Pop().Get()==20&&q_class.Pop().Get()==10);
  } 
 
Amir Yacoby #:

I tried to understand your example, but I feel relunctant to write such code, even if it will work somehow.

I proposed once another alternative, although not 100 % totally universal (as the const parameters will probably fail, and maybe some other exotic issues), but for me way more understnadable and maintainable.

Edit: Added the class example at the end

Yes, I can see.

This is where I had started.... You walked my development path backwards.

Initially I also had multiple different containers for dealing with different types of data. That's what I wanted to get rid of. Because I didn't want to implement the same logic multiple times. I simple buffer is not a big deal, but think of complex containers like trees and derivatives, or maps, or hash tables...

All of which would require multiple copies of the same implementation. The maintenance work would grow massive. And the usability is also limited, as you have to apply the correct container to your data type.

So the idea was to unify this interface, and have only one object type for iE a FIFO buffer and only have one function name to push data to the buffer.

You actually reversed that effort....
 
Dominik Egert #:
Yes, I can see.

This is where I had started.... You walked my development path backwards.

Initially I also had multiple different containers for dealing with different types of data. That's what I wanted to get rid of. Because I didn't want to implement the same logic multiple times. I simple buffer is not a big deal, but think of complex containers like trees and derivatives, or maps, or hash tables...

All of which would require multiple copies of the same implementation. The maintenance work would grow massive. And the usability is also limited, as you have to apply the correct container to your data type.

So the idea was to unify this interfaca, and have only one object type for iE a FIFO buffer and only have one function name to push data to the buffer.

You actually reversed that effort....

I get what you mean, but as I look at the example code you posted, instead of two different classes extending the same base class and calling the same base methods, you seem to (correct me if I'm wrong) make multiple methods that call each other.

I think you have much justice in what you say about my approach, but for me writing that spaghetti of methods calling each other is also not applicable. This is why I think for myself, to relinquish the templated code of MQL5 for generic libraries. 

Also, if you study the generic standard library, there are many other issues. The HashMap for classes is not really an hash map, as it does not use an O(1) for query an object in the hash map, because the hashing of classes is done by the class name (typename(class)). 
So, what are we really achieving in this generic library? 


There are other problems as well, which this might not be the place to mention but in short, are caused by classes not able to extend interfaces apart from extending one class.

 
Amir Yacoby #:

I get what you mean, but as I look at the example code you posted, instead of two different classes extending the same base class and calling the same base methods, you seem to (correct me if I'm wrong) make multiple methods that call each other.

I think you have much justice in what you say about my approach, but for me writing that spaghetti of methods calling each other is also not applicable. This is why I think for myself, to relinquish the templated code of MQL5 for generic libraries. 

Also, if you study the generic standard library, there are many other issues. The HashMap for classes is not really an hash map, as it does not use an O(1) for query an object in the hash map, because the hashing of classes is done by the class name (typename(class)). 
So, what are we really achieving in this generic library? 


There are other problems as well, which this might not be the place to mention but in short, are caused by classes not able to extend interfaces apart from extending one class.

Yes, let's not even talk about mql "interfaces".

But, what is important about my example, it's not spaghetti code, because the calling of subfunctions is only for demo purposes (and can be used to remove const declarations and how to reduce the type even more.

What is relevant about the initially called functions are their signatures. In fact, you need to have all in all, referring to your example, 5 signatures for the function "push" and only two or one for the function pop.

This is what it comes down to, and that was believed not to be possible with mql, back when I started this thread.

It has shown, it is possible to do, and it is not as complex as it initially seemed to be.

It's about the interface function (iE push()) and it's required signatures for the compiler to guarantee it's type safety.

Concerning mql STL, it is in my eyes a quite "lean" implementation, not very flexible and mostly just aimed at "get the job done somehow"...

Not my standard I apply to my own work.
Reason: