Using static class members to instantiate an object of itself, within itself, without creating a stack-overflow?

 

Consider the following scenario; you have a class that stores a collection of values. You want to be able to filter these values, but you also want to retain a copy of the original values in order to "reset" the filter and go back to default values. Would this be the best way to implement such a design or is there a better - more efficient way?

#property strict
#include <Arrays\ArrayInt.mqh>

class MyClass : public CArrayInt
{
protected:
   static MyClass*   m_orig;
   static int        m_instances;
public:
   MyClass() 
   { 
      m_instances++; 
      Print("MyClass instances = ",m_instances);
   }
  ~MyClass() 
   {   
      if(m_instances <= 2 && CheckPointer(m_orig)==POINTER_DYNAMIC && &this != m_orig)
         delete m_orig;
      Print("Destroy instance ",m_instances);
      m_instances--;   
   }
   bool  operator == (MyClass &in) { return this == &in;}
   bool  operator == (MyClass *in) { return this.CompareArray(in);}
   void  operator += (MyClass &in) { this += &in; }
   void  operator += (MyClass *in) { this.AddArray(in);}
   void  operator =  (MyClass &in) { this = &in; }
   void  operator =  (MyClass *in) 
   { 
      if(m_orig==NULL)
      {
         m_orig = new MyClass; 
         m_orig.AssignArray(in);
      }
      AssignArray(in);
   }
   void FilterByIntFlags(int flags)
   {
      if(m_orig==NULL)
      {
         m_orig = new MyClass; 
         m_orig.AssignArray(&this);
      }
      int cnt=0;
      MyClass temp1,temp2;
      for(int c=1;c<=10;c++)
      {
         if(bool(flags&(1<<c)))
         {
            temp1 = &this;
            temp1.FilterByInt(c);
            temp2 += temp1;
            cnt++;
         }
      }
      if(cnt==0)
         return;
      this = temp2;   
   }
   void FilterByInt(int element)
   {
      for(int i=Total()-1;i>=0;i--)
         if(this[i]!=element)
            Delete(i);
   }
   MyClass* FilterReset()
   {
      this = m_orig;
      return &this;
   }
};
MyClass* MyClass::m_orig = NULL;
int MyClass::m_instances = 0;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
{
//---
   
   srand(GetTickCount());
   MyClass orig, diff, other;
   for(int i=0;i<10;i++)
      orig.Add(rand()%10+1);
   diff=orig;
   diff.FilterByInt(5);
   other=diff;
   other.FilterReset();
   for(int i=0;i<other.Total();i++)
      Print(other[i]);
   Print("---------------");
   int filter = (1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5);
   other.FilterReset().FilterByIntFlags(filter);
   for(int i=0;i<other.Total();i++)
      Print(other[i]);
   
   Print("Filtered(5) vs Unfiltered = ",diff.Total()," -> ",orig.Total());
   Print("Filtered(1|2|3|4|5) vs Unfiltered = ",other.Total()," -> ",orig.Total());
   diff.FilterReset();
   other.FilterReset();
   Print("Reset To Orig vs Unfiltered = ",diff.Total()," -> ",orig.Total());
   Print("Reset Array == Original Array: ",orig == diff);
}
//+------------------------------------------------------------------+
 
nicholishen:

Consider the following scenario; you have a class that stores a collection of values. You want to be able to filter these values, but you also want to retain a copy of the original values in order to "reset" the filter and go back to default values. Would this be the best way to implement such a design or is there a better - more efficient way?

Your code is... khm... very bad. I'd start completely from scratch. You have such options as:

- Creating new collection with filtered elements along with original collection; then delete filtered one when it's not needed anymore;

- Creating a dedicated class Filter to hold pointers to selected items;

- Marking filtered elements by some flags;

etc.

 
Stanislav Korotky:

Your code is... khm... very bad. I'd start completely from scratch. You have such options as:

- Creating new collection with filtered elements along with original collection; then delete filtered one when it's not needed anymore;

- Creating a dedicated class Filter to hold pointers to selected items;

- Marking filtered elements by some flags;

etc.

I appreciate the feedback, but I was looking to make this more efficient. I'm not seeing how your examples would make it more efficient. 


- Creating new collection with filtered elements along with original collection; then delete filtered one when it's not needed anymore;

That's exactly what this does, but better since the clone is available to all instantiated objects of this class, and you can destroy any object in any order and the (master) static object will remain persistent until the final object is destroyed. 

- Creating a dedicated class Filter to hold pointers to selected items;

Again, this does that as well... this.object is the class instance that holds the filtered pointer list. The static (master) class instance holds the original data. This example uses CArrayInt, but in practical use the data is not ints but rather dynamic objects and the container is CArrayObj.

- Marking filtered elements by some flags;

Redundant due to the aforementioned features. 

Edit: Filter flags are an interesting idea, but then I'd lose the feature of automatic assignment. I'd have to explicitly assign collections from one obj to the next.

 
nicholishen:

I appreciate the feedback, but I was looking to make this more efficient. I'm not seeing how your examples would make it more efficient. 

...

More efficient in what sense ?

 
Stanislav Korotky:

Your code is... khm... very bad. I'd start completely from scratch. You have such options as:

- Creating new collection with filtered elements along with original collection; then delete filtered one when it's not needed anymore;

- Creating a dedicated class Filter to hold pointers to selected items;

- Marking filtered elements by some flags;

etc.

Could you explain why ?

 
Alain Verleyen:

Could you explain why ?

Because, IMHO the code is a mess and error prone. It'd take to go line by line with it. Unfortunately I can not afford this.

 
Stanislav Korotky:

Because, IMHO the code is a mess and error prone. It'd take to go line by line with it. Unfortunately I can not afford this.

Damned, it was what I hoped you would do.
 

Look, fellas, I look up to you and I've used both of your coding examples while learning MQL. I don't appreciate the fact that you call my work "dangerous" and "error prone" without even taking the time to look at it. Do you have some kind of personal vendetta against me? ...because you obviously didn't take the time to look at the code, so you used your "feelings" to assert that my work is rubbish.

Here... I've taken the time to distill the code down to it's core fundamentals to make it more simple. I even added comments to make it easier to understand. I'm open to real constructive criticism (not feelings) on how to make this more efficient (it already does exactly what I want it to do, and no, it's not error prone).

//+------------------------------------------------------------------+
//|                                     static_objects_inception.mq4 |
//|                                                      nicholishen |
//|                                   www.reddit.com/u/nicholishenFX |
//+------------------------------------------------------------------+
#property copyright "nicholishen"
#property link      "www.reddit.com/u/nicholishenFX"
#property version   "1.00"
#property strict
#include <Arrays\ArrayInt.mqh>

class MyClass : public CArrayInt
{
protected:
   // static master class instance (clone of the original instance). 
   // Holds the original collection set by OnInit.
   static MyClass   *m_origin;
   // number of class instances running
   static int        m_instances;
public:
   MyClass() 
   { 
      m_instances++; 
      Print("MyClass instances = ",m_instances);
   }
  ~MyClass() 
   {   
      // ONLY delete the static instance when we are destroying the last remaining object.
      if(m_instances <= 2 && CheckPointer(m_origin)==POINTER_DYNAMIC && &this != m_origin)
         delete m_origin;
      Print("Destroy instance ",m_instances);
      m_instances--;   
   }
   void  operator = (MyClass &in) { this = &in; }  // This is NOT assignment! Call to...
   void  operator = (MyClass *in)                  // ...this overloaded operator!
   { 
      AssignArray(in);
   }
   
   void OnInit(CArrayInt *in) // Initializes the object once and all instances inherit this collection.
   {
      this.AssignArray(in);
      if(m_origin==NULL)
         m_origin = new MyClass; 
      m_origin.AssignArray(&this);
   }
   void FilterByInt(int element)
   {
      if(Total() == 0)
         this = m_origin;     // Again, NOT assignment. Calling = operator function.
      for(int i=Total()-1;i>=0;i--)
         if(this[i]!=element)
            Delete(i);
   }
   MyClass* FilterReset()
   {
      this = m_origin;
      return &this;
   }
};
MyClass *MyClass::m_origin = NULL;
int MyClass::m_instances = 0;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
{ 
   srand(GetTickCount());
   CArrayInt int_arr;
   for(int i=0;i<100;i++)
      int_arr.Add(rand()%10+1);
   
   MyClass *origin, other, diff;
   
   origin = new MyClass();
   origin.OnInit(&int_arr);
   delete origin;
   //origin is detroyed, but it's clone still exists in the static object.
  
   //"diff" and "other" have not been explicitly assigned any values, but they clone the original collection from m_origin
   diff.FilterByInt(4);
   other.FilterByInt(7);
   for(int i=0;i<diff.Total();i++)
      Print("diff[",i,"] = ",diff[i]);
  
}
//+------------------------------------------------------------------+
 
nicholishen:

Look, fellas, I look up to you and I've used both of your coding examples while learning MQL. I don't appreciate the fact that you call my work "dangerous" and "error prone" without even taking the time to look at it. Do you have some kind of personal vendetta against me? ...because you obviously didn't take the time to look at the code, so you used your "feelings" to assert that my work is rubbish.

Here... I've taken the time to distill the code down to it's core fundamentals to make it more simple. I even added comments to make it easier to understand. I'm open to real constructive criticism (not feelings) on how to make this more efficient (it already does exactly what I want it to do, and no, it's not error prone).

You are right, I didn't take time to study your code. However it's not me who said it's "dangerous" or "error prone". As soon as I have time I will study it and give you a constructive criticism, if any. (don't wait it as I have no idea when it will be, but it will be).

Sorry if I offended you in any way.

 
Alain Verleyen:

More efficient in what sense ?


Sorry Alain, I didn't see this, and this is a good question that I didn't want to go unanswered. I'm looking to make it more efficient in two ways.

1. Less code to accomplish the same desired traits (a universal master collection that all instances inherit).

2. Make it run faster... I'm sure that Deleting the object pointer for filtering could somehow be even faster...?

 
Stanislav Korotky:

Because, IMHO the code is a mess and error prone. It'd take to go line by line with it. Unfortunately I can not afford this.


Stanislav, How long would it take you to go through the following code? 5 mins? I'll tell you what... if you (or anyone reading this) can prove a single error in this script then I'll promptly send you 4 mBTC (~$10USD)

 

//+------------------------------------------------------------------+
//|                                     static_Objects_inception.mq4 |
//|                                                      nicholishen |
//|                                   www.reddit.com/u/nicholishenFX |
//+------------------------------------------------------------------+
#property copyright "nicholishen"
#property link      "www.reddit.com/u/nicholishenFX"
#property version   "1.00"
#property strict
#include <Arrays\ArrayInt.mqh>

class MyClass : public CArrayInt
{
protected:
   // static master class instance (clone of the original instance). 
   // Holds the original collection set by OnInit.
   static MyClass   *m_origin;
   // number of class instances running
   static int        m_instances;
public:
   MyClass() 
   { 
      m_instances++; 
      Print("MyClass instances = ",m_instances);
   }
  ~MyClass() 
   {   
      // ONLY delete the static instance when we are destroying the last remaining object.
      if(m_instances <= 2 && CheckPointer(m_origin)==POINTER_DYNAMIC && &this != m_origin)
         delete m_origin;
      Print("Destroy instance ",m_instances);
      m_instances--;   
   }
   void  operator = (MyClass &in) { this = &in; }  // This is NOT assignment! Call to...
   void  operator = (MyClass *in)                  // ...this overloaded operator!
   { 
      AssignArray(in);
   }
   
   void OnInit(CArrayInt *in) // Initializes the object once and all instances inherit this collection.
   {
      this.AssignArray(in);
      if(m_origin==NULL)
         m_origin = new MyClass; 
      m_origin.AssignArray(&this);
   }
   void FilterByInt(int element)
   {
      if(Total() == 0)
         this = m_origin;     // Again, NOT assignment. Calling = operator function.
      for(int i=Total()-1;i>=0;i--)
         if(this[i]!=element)
            Delete(i);
   }
   MyClass* FilterReset()
   {
      this = m_origin;
      return &this;
   }
};
MyClass *MyClass::m_origin    = NULL;
int      MyClass::m_instances = 0;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
{ 
   srand(GetTickCount());
   CArrayInt int_arr;
   for(int i=0;i<100;i++)
      int_arr.Add(rand()%10+1);
   
   MyClass *origin, other, diff;
   
   origin = new MyClass();
   origin.OnInit(&int_arr);
   delete origin;
   //origin is detroyed, but it's clone still exists in the static object.
  
   //"diff" and "other" have not been explicitly assigned any values. But they clone the collection from m_origin
   diff.FilterByInt(4);
   other = diff;
   other.FilterByInt(7); //will result in 0 elements since diff only contains 4's'
   Print("other.Total() = ",other.Total());
   for(int i=0;i<diff.Total();i++)
      Print("diff[",i,"] = ",diff[i]);
  
}
//+------------------------------------------------------------------+
Reason: