Issue with template classes. Can templates be made to accept both reference and value types?

 

Given an example class below, I would like to be able to use it with values, pointers and structs.

This issue is that structs can only be passed by reference which gives me a lot of headaches trying to make my own personal library. 

See the simple Queue class below which is templated.


template<typename T>
class Queue {
   private:
      T _arr[];
      
   public:
      Queue();
      ~Queue();
      
      int      Size() const;
      
      void     Push(T val);
      void     PushRef(T &val);
      
      T        Pop();      
      bool     Pop(T &val);
      
      T        Peek();
      
      T        At(const int index);    
};


template<typename T>
void Queue::Push(T val) {
   Array::Push(val, _arr);
}


template<typename T>
void Queue::PushRef(T &val) {
   Array::PushRef(val, _arr);
}

I try to get around the struct issues by making the "PushRef" class. 

However I can't compile if I make say a new Queue<MqlRates> class because the Push method will be templated with a struct and give me this issue...

I understand the fix is to just make everything in the Queue class accept a reference.

It's extremely annoying trying to make templated classes that can accept any data struct.

But my question to the all the IQ here is... 

Is there any ways to work around this? Some hidden hack I haven't discovered yet? 

 

There are very few basic types, so you can make an overload for each of them. I did this in this library. Everything works great.

#define MACROS(A) void Push( A Val ) { this.Push<A>(Val); }

template <typename T>
class Queue
{
public:  
  template <typename T2>
  void Push( T2 &Val ) { /* Code for T2 */ }

  MACROS(char)
  MACROS(short)
  MACROS(int)
  MACROS(long)
  MACROS(uchar)
  MACROS(ushort)
  MACROS(uint)
  MACROS(ulong)
  MACROS(bool)
  MACROS(string)
  MACROS(double)
  MACROS(float)
  MACROS(color)
  MACROS(datetime)  
};

#undef MACROS

void OnStart()
{
  Queue<int> q_int;
  Queue<MqlTick> q_tick;
  
  MqlTick Tick;
  
  q_int.Push(1);
  q_tick.Push(Tick);    
}
TypeToBytes
TypeToBytes
  • www.mql5.com
Побайтовая работа со структурами, массивами и стандартными типами данных
 

Thanks very much for the share.

Only concern with this is that the code will get bloated for all the different collection types.

So if you use this signature in your templated class:

void Push<int>(const int val) { Array::Push(val, _arr); }

Then this will become the override for all int type queues?

I just need to get a second to try this out.

Does int cover all custom enum types as well?

 
dazamate #:

Does int cover all custom enum types as well?

Do an experiment.

 

I think it's best to divide those kind of libraries to three:

1. Base class template, with all methods and data shared for both objects and primitives.

2. A derived class template for objects/structs - derived from 1 with methods for objects

3. A derived class template for primitives - derived from one with the methods for primitives.

Just as prove of concept - Haven't tested this and is shared only to make a point.
* Sure need to take care of index = 0 in Pop() and so on.. But that's not the point of this post..

** Slight change in devision - division to reference / value (instead of points 2. 3.)

template <typename T>
class QueueBase
  {
private:
   T                 _arr[];
   int               m_begin,m_end;
   int               m_count;
protected:
   void              PushItem(T &val);
   T                 m_null;
public:
   void              QueueBase() {m_count=0; m_begin=0; m_end=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;
   m_count--;
   return _arr[m_begin++];
  }
template <typename T>
T QueueBase::Peek()
  {
   if(m_count<=0)
      return m_null;
   return _arr[m_begin];
  }
template <typename T>
void QueueBase::PushItem(T &val)
  {
   m_count++;
   ArrayResize(_arr,m_count);
   _arr[m_end++]=val;
  }
//+------------------------------------------------------------------+
//| 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);};
  };

//+------------------------------------------------------------------+
//| 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);}
  };
 

For using

   QueueRef<ANYSTRUCT> q_struct;
   QueueVal<ANYCLASS *> q_class;
   QueueVal<double>q_dbl;
   QueueVal<int>q_int;
   QueueVal<ANYENUM>q_enum;
	

*-Changed the Push in QueueObj to use the base PushItem

**-Now the structs are handled in QueueRef, and the rest in QueueVal

Reason: