How to implement multilevel inheritance to IComparable interface?

 

Hi, I want to create an object that could be sorted easily. but the thing is that I want its derived objects not to follow the same rule as of the based class. so I'm trying to override Compare method of the IComparable interface but I want to make each type of child objects to have their own method of compare.

class Parent: public IComparable<Parent*> {
public:
   virtual bool Equals(Parent* value) override {
// Some criteria
   }

   virtual int HashCode() override {
// Some code
   }

   virtual int Compare(Parent*value) override {
// Some rule
   }
}

class Child: public Parent{
public:
   virtual bool Equals(Child* value) {{
// Some criteria
   }

   virtual int HashCode() {
// Some code
   }

   virtual int Compare(Child* value) {
// Another rule
   }
}

lets say I have ArrayList of Child object that I want to sort. for some unknown reason the compiler doesn't chose Compare() method of the child class.

CArrayList<Child*> list;
list.Add(new Child());
list.Add(new Child());
list.Add(new Child());

list.Sort(); //Not working

The only way that I could manage to force it to use the child method to sort objects was to override the Compare method in the child class using parent object as argument and dynamic_cast inside the method to clarify the object's type, so that the compiler accept overriding and then use parent object when defining arraylist. which doesn't work for me, I want the arraylist to be the child type.

// Change Compare method to confirm with the parent signature
class Child: public Parent{
public:
   virtual bool Equals(Parent* value) override {
// Some criteria
   }

   virtual int HashCode() override {
// Some code
   }

   virtual int Compare(Parent* value) override {
// Another rule (use dynamic_cast<Child*>)
   }
}

CArrayList<Parent*> list;
list.Add(new Child());
list.Add(new Child());
list.Add(new Child());

list.Sort(); //uses the child compare method

so my conclusion is that the compiler doesn't recognize Child objects to be Comparable, so it uses the default comparer. unlike parent objects which sort without problem. what am I missing? maybe some obvious mistakes defining inheritance? doesn't derived class of a derived class of IComparable have to be an IComparable?

so my question is how to create a comparable object so that its multilevel derivatives be comparable as well, but with different sorting strategies? if it's not possible in mql5, do you have any ideas on how to implement this?

thank you.

 
Null_Pointer:

Hi, I want to create an object that could be sorted easily. but the thing is that I want its derived objects not to follow the same rule as of the based class. so I'm trying to override Compare method of the IComparable interface but I want to make each type of child objects to have their own method of compare.

lets say I have ArrayList of Child object that I want to sort. for some unknown reason the compiler doesn't chose Compare() method of the child class.

The only way that I could manage to force it to use the child method to sort objects was to override the Compare method in the child class using parent object as argument and dynamic_cast inside the method to clarify the object's type, so that the compiler accept overriding and then use parent object when defining arraylist. which doesn't work for me, I want the arraylist to be the child type.

so my conclusion is that the compiler doesn't recognize Child objects to be Comparable, so it uses the default comparer. unlike parent objects which sort without problem. what am I missing? maybe some obvious mistakes defining inheritance? doesn't derived class of a derived class of IComparable have to be an IComparable?

so my question is how to create a comparable object so that its multilevel derivatives be comparable as well, but with different sorting strategies? if it's not possible in mql5, do you have any ideas on how to implement this?

thank you.

The function signatures need to be the same for polymorphism to work. CArrayObj is far better to work with for dynamic object vectors. It can also delete dynamic allocated objects when it's scope runs out by default so there's no need to manage memory once you add objects to CArrayObj or CList. If you want to sort different objects based on different values of the same type then you can use a similar pattern to this. 

#include <arrays/arrayobj.mqh>
class Base : public CObject {
 public:
   int x, y;
   Base(int _x, int _y) {
      this.x = _x;
      this.y = _y;
   }
 protected:
   virtual int _get_value() const {
      return 0;
   }
   virtual int Compare(const CObject *node, const int mode = 0) const override {
      const Base *other = node;
      if (this._get_value() > other._get_value()) return 1;
      if (this._get_value() < other._get_value()) return -1;
      return 0;
   }
};

class Xclass : public Base {
 public:
   Xclass(int _x, int _y): Base(_x, _y) {}
 protected:
   virtual int _get_value() const override {
      return this.x;
   }
};

class Yclass : public Base {
 public:
   Yclass(int _x, int _y): Base(_x, _y) {}
 protected:
   virtual int _get_value() const override {
      return this.y;
   }
};

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart() {
   CArrayObj list;
   list.Add(new Xclass(10, 10));
   list.Add(new Yclass(20, -10));
   list.Add(new Xclass(5, 20));

   list.Sort();

}
 
nicholi shen:

The function signatures need to be the same for polymorphism to work. CArrayObj is far better to work with for dynamic object vectors. It can also delete dynamic allocated objects when it's scope runs out by default so there's no need to manage memory once you add objects to CArrayObj or CList. If you want to sort different objects based on different values of the same type then you can use a similar pattern to this. 

thanks for the code, interesting approach, that would solve the sorting problem. although I would like to use IComparable so I could be able to create all sort of generic collections (arraylists, hashmaps, etc). I still don't get why overriding Compare method of derivatives wouldn't effect Sort method. it's weird that it doesn't even sort child objects by base class compare method. it does nothing.

 
 interface IComparable : public IEqualityComparable<T>
You can't sort by equality. From CObject you can.
 
An interface contains variables and methods like a class but the methods in an interface are abstract by default unlike a class. Multiple inheritance by interface occurs if a class implements multiple interfaces or also if an interface itself extends multiple interfaces. 
 
Null_Pointer:

thanks for the code, interesting approach, that would solve the sorting problem. although I would like to use IComparable so I could be able to create all sort of generic collections (arraylists, hashmaps, etc). I still don't get why overriding Compare method of derivatives wouldn't effect Sort method. it's weird that it doesn't even sort child objects by base class compare method. it does nothing.

You have to cast them back up inside of the overridden method. A better approach is to still always inherit CObject use the primary CObject collections, but when you need a generic collection use IComparer to define a callback object for custom sorting. 

#include <generic/arraylist.mqh>
#include <arrays/list.mqh>


class  Base : public CObject {
 public:
   int x, y;
   Base(int _x, int _y) {
      this.x = _x;
      this.y = _y;
   }
   virtual int _get_value() const {
      return 0;
   }

};

class Xclass : public Base {
 public:
   Xclass(int _x, int _y): Base(_x, _y) {}
 protected:
   virtual int _get_value() const override {
      return this.x;
   }
};

class Yclass : public Base {
 public:
   Yclass(int _x, int _y): Base(_x, _y) {}
 protected:
   virtual int _get_value() const override {
      return this.y;
   }
};

class VirtualGetValueSorter : public IComparer<CObject*> {
 public:
   virtual int Compare(CObject* o1, CObject* o2) override {
      Base *b1 = o1;
      Base *b2 = o2;
      if (b1._get_value() > b2._get_value()) return 1;
      if (b1._get_value() < b2._get_value()) return -1;
      return 0;
   }
};

class XOnlySorter : public IComparer<CObject*> {
 public:
   virtual int Compare(CObject* o1, CObject* o2) override {
      Base *b1 = o1;
      Base *b2 = o2;
      if (b1.x > b2.x) return 1;
      if (b1.x < b2.x) return -1;
      return 0;
   }
};

class MyList : public CArrayList<CObject*> {
   CList m_gc;
 public:
   bool Add(CObject* obj) {
      m_gc.Add(obj);
      return CArrayList<CObject*>::Add(obj);
   }
};

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart() {
   MyList list;
   list.Add(new Xclass(10, 10));
   list.Add(new Yclass(20, -10));
   list.Add(new Xclass(5, 20));

   VirtualGetValueSorter vsort;
   XOnlySorter xsort;

   list.Sort(&vsort);
   list.Sort(&xsort);
}
//+------------------------------------------------------------------+
 
shamsmehra90:
An interface contains variables and methods like a class but the methods in an interface are abstract by default unlike a class. Multiple inheritance by interface occurs if a class implements multiple interfaces or also if an interface itself extends multiple interfaces. 
I don't think Mql5 supports multiple inheritance. my question was about "multilevel" inheritance, totally different subject. I made mistake typing the title then edited. so my bad!
 
nicholi shen:

You have to cast them back up inside of the overridden method. A better approach is to still always inherit CObject use the primary CObject collections, but when you need a generic collection use IComparer to define a callback object for custom sorting. 

yeah I'm already using IComparer to make the code running but the whole point was to avoid creating another class (an IComparer) for each derivative of the base class. I prefer to implement Compare methods inside each derived classes. "You have to cast them back up inside of the overridden method", I think, I didn't explain the problem clearly by the pseudo-code above. do you mean like this?:

//+------------------------------------------------------------------+
//|                                                      Sorting.mq5 |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include <Generic/ArrayList.mqh>
#include <Generic/Interfaces/IComparer.mqh>
#include <Math/Stat/Math.mqh>

class Parent: public IComparable<Parent*> {
private:
   int _id;
public:
   Parent() {}

   Parent(int id) {
      _id = id;
   }

   int Id() {
      return _id;
   }

   virtual bool Equals(Parent* value) override {
      return ::Equals(_id, value.Id());
   }

   virtual int HashCode() override {
      return 0;
   }

   virtual int Compare(Parent*value) override {
      return ::Compare(_id, value.Id());
   }
};

class Child: public Parent {
private:
   double _x;
public:
   Child(double x) {
      _x = x;
   }

   double X() {
      return _x;
   }

   virtual bool Equals(Parent* value) override {
      return ::Equals(_x, dynamic_cast<Child*>(value).X());
   }

   virtual int HashCode() override {
      return 0;
   }

   virtual int Compare(Parent*value) override {
      return ::Compare(_x, dynamic_cast<Child*>(value).X());
   }
};

CArrayList<Child*> childs;
Child* c;
CArrayList<Parent*> parents, castChilds;
Parent* p, *cc;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
//---
   for(int i = 0; i < 5; i++) {
      parents.Add(new Parent(5 - i));
      childs.Add(new Child(MathRandomNonZero()));
      castChilds.Add(new Child(MathRandomNonZero()));
   }
   PrintFormat("%24s%20s%20s", "parents(id)", "childs(x)", "castChilds(x)");
   PrintAll();

   parents.Sort();
   childs.Sort();
   castChilds.Sort();

   Print("after sorting");
   PrintAll();

//Output:
//               parents(id)           childs(x)       castChilds(x)
//#0:                    5                0.28                0.46
//#1:                    4                0.33                0.29
//#2:                    3                0.70                0.13
//#3:                    2                0.69                0.47
//#4:                    1                0.33                0.02
//after sorting:
//#0:                    1                0.28                0.02
//#1:                    2                0.33                0.13
//#2:                    3                0.70                0.29
//#3:                    4                0.69                0.46
//#4:                    5                0.33                0.47
//---
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
void PrintAll() {
   for(int i = 0; i < 5; i++) {
      parents.TryGetValue(i, p);
      childs.TryGetValue(i, c);
      castChilds.TryGetValue(i, cc);
      PrintFormat("#%d: %20d%20.2f%20.2f", i, p.Id(), c.X(), dynamic_cast<Child*>(cc).X());
   }
}
//+------------------------------------------------------------------+
lists created as base class sort smoothly, whether they have parent or child objects inside them or not. but when I create a child arraylist, it doesn't use neither its own Compare method nor its base class. among 3 arraylists above, "childs" (created as CArrayList<Child*>) does not sort. shouldn't virtual specifier make it jump to the current object method of compare (Child::Compare), why am I getting this behavior? maybe I'm not aware of some basics here.

thank you again for your help

 
William Roeder:
You can't sort by equality. From CObject you can.
IComparable and CObject both have the Compare method, what is the difference?
 
Null_Pointer:
IComparable and CObject both have the Compare method, what is the difference?

CObject takes a mode so you can define multiple sort modes. Icomparable example:

#include <generic/arraylist.mqh>



class P : public IComparable<P*> {

 public:
   int x;
   int y;
   P(): x(1), y(2) {}
   virtual int get_sort_value() = 0;
   virtual int HashCode() {
      return int(pow(this.x, 2) + pow(this.y, 2));
   }
   virtual bool Equals(P* other) {
      return (this.get_sort_value() == other.get_sort_value());
   }
   virtual int Compare(P* other) {
      if (this.get_sort_value() > other.get_sort_value()) return 1;
      if (this.get_sort_value() < other.get_sort_value()) return -1;
      return 0;
   }
};

class X : public P {
 public:
   virtual int get_sort_value() {
      return this.x;
   }
};

class Y : public P {
 public:
   virtual int get_sort_value() {
      return this.y;
   }
};


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart() {
   CArrayList<P*> list;
   list.Add(new X());
   list.Add(new Y());
   list.Sort();
}
 
nicholi shen:

CObject takes a mode so you can define multiple sort modes. Icomparable example:

Same problem with your code. I created an arraylist of X objects (CArrayList<X*> list2). Sort() method is not working. please check this out.

#include <generic/arraylist.mqh>



class P : public IComparable<P*> {

 public:
   int x;
   int y;
   P(): x(1), y(2) {}
   virtual int get_sort_value() = 0;
   virtual int HashCode() {
      return int(pow(this.x, 2) + pow(this.y, 2));
   }
   virtual bool Equals(P* other) {
      return (this.get_sort_value() == other.get_sort_value());
   }
   virtual int Compare(P* other) {
      if (this.get_sort_value() > other.get_sort_value()) return 1;
      if (this.get_sort_value() < other.get_sort_value()) return -1;
      return 0;
   }
};

class X : public P {
 public:
   virtual int get_sort_value() {
      return this.x;
   }
};

class Y : public P {
 public:
   virtual int get_sort_value() {
      return this.y;
   }
};


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart() {
   CArrayList<P*> list;
   list.Add(new X());
   list.Add(new Y());
   list.Sort();
   
   CArrayList<X*> list2;
   X* x1 = new X();
   x1.x=20;
   list2.Add(x1);
   X* x2 = new X();
   x2.x=2;
   list2.Add(x2);
   X* x3 = new X();
   x3.x=12;
   list2.Add(x3);
   
   list2.Sort(); //doesn't sort(output:[20,2,12])
}

why Sort() works on CArrayList<P*> but not CArrayList<X*>?

Reason: