"Curiously recurring template pattern" & Casting in MQL5 - help needed

 

consider the following code:

/*
 *    Curiously recurring template pattern
 *    In my use case the following inheritance tree was intended to work with Candlestick Patterns
 */
#include <Arrays\ArrayObj.mqh>

// base class for all patterns
template <typename Derived>
class CBase: public CObject  {
   public:
      static            Derived* create() { return new Derived(); }
      string            getType() { return typename(Derived); }; // simplify debugging
};

// handles things some patterns have in common
template <typename Derived>
class CBaseExt1 : public CBase<Derived> {
   // No need to redefine create method
};


// specialized Pattern class, each class handles just one pattern
class CBaseExt2a : public CBaseExt1<CBaseExt2a> {
   // No need to redefine create method
   // calling CBaseExt2a::create() returns a new CBaseExt2a object;
};

// one more example of a specialized Pattern class
class CBaseExt2b : public CBaseExt1<CBaseExt2b> {
   // No need to redefine create method
   // calling CBaseExt2b::create() returns a new CBaseExt2b object;
};


//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart() {

   /*
   This is for understanding the question and my challenge
   */
   CBase* cb = CBase::create();                 // ->error: template mismatch
   CBaseExt1* ext1 = CBaseExt1::create();       // ->error: template mismatch
   CBaseExt2a* ext2a = CBaseExt2a::create();    // working
   CBaseExt2b* ext2b = CBaseExt2b::create();    // working
   
   /*
   This is what I was trying to do
   */
   CArrayObj *listOfMyObjects = new CArrayObj();
   
   // adding is no Problem
   listOfMyObjects.Add(ext2a);
   listOfMyObjects.Add(ext2b);
   
   // this is what I was expecting: To be able to use the base class API on all the patterns that derived from it.
   CBase *item = (CBase)listOfMyObjects.At(1);

   delete ext1;
   delete ext2a;
   delete ext2b;
   delete listOfMyObjects;
}

This code does not compile because CBase is a template and not a class in the inheritance tree. 

Do you have an Idea on how I can get each pattern out of that CArrayObj and be able to work with that objects?

I case you find that interesting here is the code that compiles, as a starting point:

/*
 *    Curiously recurring template pattern
 *    In my use case the following inheritance tree was intended to work with Candlestick Patterns
 */
#include <Arrays\ArrayObj.mqh>

// base class for all patterns
template <typename Derived>
class CBase: public CObject  {
   public:
      static            Derived* create() { return new Derived(); }
      string            getType() { return typename(Derived); }; // simplify debugging
};

// handles things some patterns have in common
template <typename Derived>
class CBaseExt1 : public CBase<Derived> {
   // No need to redefine create method
};


// specialized Pattern class, each class handles just one pattern
class CBaseExt2a : public CBaseExt1<CBaseExt2a> {
   // No need to redefine create method
   // calling CBaseExt2a::create() returns a new CBaseExt2a object;
};

// one more example of a specialized Pattern class
class CBaseExt2b : public CBaseExt1<CBaseExt2b> {
   // No need to redefine create method
   // calling CBaseExt2b::create() returns a new CBaseExt2b object;
};


//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart() {

   /*
   This is for understanding the question and my challenge
   */
   //CBase* cb = CBase::create();                 // ->error: template mismatch
   //CBaseExt1* ext1 = CBaseExt1::create();       // ->error: template mismatch
   CBaseExt2a* ext2a = CBaseExt2a::create();    // working
   CBaseExt2b* ext2b = CBaseExt2b::create();    // working
   
   /*
   This is what I was trying to do
   */
   CArrayObj *listOfMyObjects = new CArrayObj();
   
   // adding is no Problem
   listOfMyObjects.Add(ext2a);
   listOfMyObjects.Add(ext2b);
   
   // this is what I was expecting: To be able to use the base class API on all the patterns that derived from it.
   //CBase *item = (CBase)listOfMyObjects.At(1);

   //delete ext1;
   delete ext2a;
   delete ext2b;
   delete listOfMyObjects;
}
 
   // calling CBaseExt2a::create() returns a new CBaseExt2a object;
False, it creates a
CBaseExt1<CBaseExt2a>
 
Ok, you are right. But does this gives me a solution to storing and retrieving the objects from the listOfMyObjects?
 
Like that?
#include <Arrays\ArrayObj.mqh>

//|------------------------------------------------------------------+
//|   Non-template interface                                         |
//|------------------------------------------------------------------+
class IPattern : public CObject
  {
public:
   //--- common interface
   virtual string GetType() const = 0;
  };

//|------------------------------------------------------------------+
//|   CRTP base class that inherits from IPattern                    |
//|------------------------------------------------------------------+
template <typename Derived>
class CBase : public IPattern
  {
public:
   //--- create method returns a new instance of Derived
   static Derived* create() { return(new Derived()); }
   
   //--- override GetType method from IPattern using a static method from Derived
   virtual string GetType() const { return("Pattern: " + typename(Derived)); }
  };

//|------------------------------------------------------------------+
//|   Specialized Pattern class: Pattern A                           |
//|------------------------------------------------------------------+
class CBaseExt2a : public CBase<CBaseExt2a>
  {
public:
   //--- provide a static method to return its type name
   static string TypeName() { return("CBaseExt2a"); }
  };

//|------------------------------------------------------------------+
//|   Specialized Pattern class: Pattern B                           |
//|------------------------------------------------------------------+
class CBaseExt2b : public CBase<CBaseExt2b>
  {
public:
   //--- provide a static method to return its type name
   static string TypeName() { return("CBaseExt2b"); }
  };

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   //--- create instances 
   CBaseExt2a *ext2a = CBaseExt2a::create();
   CBaseExt2b *ext2b = CBaseExt2b::create();
   
   //--- create an array to hold our objects 
   CArrayObj *listOfMyObjects = new CArrayObj();
   
   //--- add objects to the array
   listOfMyObjects.Add(ext2a);
   listOfMyObjects.Add(ext2b);
   
   //--- iterate over the array and use the common IPattern interface
   for(int i = 0; i < listOfMyObjects.Total(); i++)
     {
      IPattern *pattern = (IPattern *) listOfMyObjects.At(i);
      Print(pattern.GetType());
     }
   
   //--- cleanup
   delete ext2a;
   delete ext2b;
   delete listOfMyObjects;
  }
 

I love that, thank you so much.

Unbelievable simple. I tried so many complex approaches to this, but missed the obvious one. thanks again for your time to answer me.


For everybody interested in the Pattern, here is a tiny improvement to the code:

//|------------------------------------------------------------------+
//|   Specialized Pattern class: Pattern A                           |
//|------------------------------------------------------------------+
class CBaseExt2a : public CBase<CBaseExt2a> {
};

//|------------------------------------------------------------------+
//|   Specialized Pattern class: Pattern B                           |
//|------------------------------------------------------------------+
class CBaseExt2b : public CBase<CBaseExt2b> {
};

There is no need to override the GetType() Method.