OOP, plantillas y macros en mql5, sutilezas y usos - página 10

 
Alexey Navoykov:

Al fin y al cabo, la cadena de herencia podría ser cualquier cosa: incluso Interfaz<CBase>, incluso Interfaz<C<B<A<CBase>>>>, hay toneladas de variantes. Tendríamos que lanzar CBase secuencialmente a todas las variantes posibles, lo cual es poco realista.

Recuerdo que iba a implementar el almacenamiento de la información sobre las interfaces en el propio objeto de la clase, y además de las almohadillas de interfaz existentes para hacer clases de interfaz independientes que funcionarían como una envoltura sobre nuestra almohadilla. Pero luego llegué a la conclusión de que todo esto es innecesario y redundante. En la práctica, nunca he visto la necesidad de fundir una clase base a ninguna interfaz, simplemente no tiene ningún sentido. La única opción es averiguar si la clase soporta esta interfaz para fines de depuración, pero no necesitamos el casting para eso.

En mi opinión, deberíamos almacenar la interfaz en la interfaz. Interface<T,CBase>::GetComparer().Compare(CBase &a, T& b); He esbozado un poco de código para un ejemplo, pero no quiero saturar el foro con demasiadas letras.

 
Ilya Malev:

En mi opinión, la interfaz debería almacenarse en la propia interfaz. Interface<T,CBase>::GetComparer().Compare(CBase &a, T& b); He esbozado un poco de código para un ejemplo, pero no quiero saturar el foro con demasiadas letras.

Adelante, publícalo. De todos modos, no es una tontería, sino un tema de debate. Pero en ese hilo del que se trasladó la discusión aquí, sería inapropiado.

 

ok)

//+------------------------------------------------------------------+
//|                                                    Comparer demo |
//|                                      Copyright 2012, CompanyName |
//|                                       http://www.companyname.net |
//+------------------------------------------------------------------+
#property strict

#define  throw(M)   {int a=0,b=1; printf("%s: %s",__FUNCSIG__,M); Print(b%a); } 
#define  msg_pure   "Method not implemented!"
#define  msg_compare "Types are uncomparable!"


template<typename T,typename CBase>
class IComparer;
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class Number
  {
public:
   virtual int CompareTo(Number *par)
    { 
      throw(msg_pure); return 0; 
    }
   int operator==(Number*par)
    {
      return CompareTo(par);
    }
   template<typename T> 
   T *operator[](T*)
    { 
      return dynamic_cast<T*>(&this); 
    }
   virtual string ToStr()
    { 
      return StringFormat("Type %s, object %i (%i)",typename(this),&this,CheckPointer(&this));
    }
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class Int: public Number
  {
private:
   int               i;
public:
   int Get()
    { 
      return i; 
    }
   Int*Set(int p)
    { 
      i=p; 
      return &this; 
    }
   virtual int CompareTo(Number*par)
     {
      Int*v1=dynamic_cast<Int*>(par);
      if(v1)
       {
        return IComparer<Int,Int>::GetComparer()[ IComparer<Int,Int>::GetComparer() ].Compare( &this,v1 );
       }
      
      Double*v2=dynamic_cast<Double*>(par);
      if(v2)
       {
        return IComparer<Double,Int>::GetComparer()[ IComparer<Double,Int>::GetComparer() ].Compare( &this,v2 );
       }
       
      throw(msg_compare); return 0;
     }
   Int*operator=( int p )
    { 
      return Set( p ); 
    }
   virtual string ToStr()
    { 
      return IntegerToString(i); 
    }
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class Double: public Number
  {
private:
   double            f;
public:
   double Get()
    { 
      return f; 
    }
   Double*Set(double p)
    { 
      f=p; 
      return &this; 
    }
   virtual int CompareTo(Number*par)
     {
      Double*v1=dynamic_cast<Double*>(par);
      if(v1)
       {
        return IComparer<Double,Double>::GetComparer()[ IComparer<Double,Double>::GetComparer() ].Compare( &this,v1 );
       }
       
      Int*v2=dynamic_cast<Int*>(par);
      if(v2)
       {
        return IComparer<Int,Double>::GetComparer()[ IComparer<Int,Double>::GetComparer() ].Compare( &this,v2 );
       }
       
      throw(msg_compare); return 0;
     }
   Double*operator=(double p)
    { 
      return Set(p); 
    }
   virtual string ToStr()
    { 
      return DoubleToString(f,_Digits); 
    }
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
template<typename T,typename CBase>
class IComparer: public CBase
  {
private:
   static IComparer *comparer;
public:
   IComparer()
    { 
      if(!comparer) 
       {
        comparer=&this; 
       }
    }
   virtual int Compare(CBase &op1,T &op2)
    { 
      throw(msg_pure); 
      return 0; 
    }

   static IComparer *GetComparer()
    { 
      return comparer ? comparer : new IComparer; 
    }
   static string ToStr(int cmp)
    { 
      return(!cmp?"equal":(cmp==-1?"lesser than":(cmp==1?"greater than":"undefined"))); 
    }
  };

template<typename T,typename CBase>
IComparer *IComparer::comparer=NULL;
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CmpIntToInt: public IComparer<Int,Int>
  {
   virtual int Compare(Int &op1,Int &op2)
    { 
      return(op1.Get()>op2.Get())-(op1.Get()<op2.Get()); 
    }
  }
citi;
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CmpIntToDouble: public IComparer<Int,Double>
  {
   virtual int Compare(Double &op1,Int &op2)
    { 
      return int(op1.Get()-op2.Get()>DBL_EPSILON)-int(op2.Get()-op1.Get()>DBL_EPSILON); 
    }
  }
citd;
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CmpDoubleToInt: public IComparer<Double,Int>
  {
   virtual int Compare(Int &op1,Double &op2)
    { 
      return int(op1.Get()-op2.Get()>DBL_EPSILON)-int(op2.Get()-op1.Get()>DBL_EPSILON); 
    }
  }
cdti;
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CmpDoubleToDouble: public IComparer<Double,Double>
  {
   virtual int Compare(Double &op1,Double &op2)
    { 
      return int(op1.Get()-op2.Get()>DBL_EPSILON)-int(op2.Get()-op1.Get()>DBL_EPSILON); 
    }
  }
cdtd;
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {
   srand(GetTickCount());
   Number *n[10];

   for(int i=0;i<10;i++)
     {
      if(rand()%2==0) n[i]=new Int=rand();
      else            n[i]=new Double=rand()*rand()/(1.0*rand());
     }

   for(int i=0;i<10;i++)
     {
      int a=rand()%10,b=rand()%10;
      printf("%s is %s %s",n[a].ToStr(),IComparer<Int,Int>::ToStr(*n[a]==n[b]),n[b].ToStr());
     }

   for(int i=0;i<10;i++)
     {
      delete n[i];
     }
  }
//+------------------------------------------------------------------+
 
Ilya Malev:

ok)

¿Por qué existe el métodoIComparer::Compare(CBase &op1, T &op2) si ambos argumentos deberían ser T

 
Alexey Navoykov:

Y por qué existe el métodoIComparer::Compare(CBase &op1, T &op2) si ambos argumentos deben ser T

¿Sólo es posible comparar los mismos tipos? Mi suposición es que no lo es. Haga T==CBase y habrá ambos argumentos T )

 

Ah, lo tengo, entonces lo has entendido mal. La clase IComparer en tu caso debería ser como IComparer<T1,T2,CBase>. En consecuencia, el método también lo será:

virtual Compare(T1 &op1, T2 &op2) = 0;

Y cuando heredes la clase sobrecargarás exactamente este método, todo caerá en su sitio.
 
Alexey Navoykov:

Ah, ya veo, entonces lo has entendido mal. La clase IComparer en tu caso debería ser como IComparer<T1,T2,CBase>:

Y cuando se hereda una clase, se sobrecarga este método, todo caerá en su lugar.

¿Y qué significa esta CBase? ¿Y por qué hay que comparar exactamente 2 valores de un mismo tipo?

 
No tengo el objetivo de hacerlo exactamente como en C#, es más, no he entendido en detalle cómo se hace allí. Pero a simple vista el IEnumerador de dos funciones reset y next es muy inconveniente, definitivamente no lo copiaría en la misma forma )
 

¿un reparto dinámico para comparar? ¿estás loco?

una función virtual pura se escribe así -virtual int CompareTo(Number *par) = 0 ;

es el compilador el que será maldecido y no el sustituto de la excepción autoescrito.

 
TheXpert:

una función virtual pura se escribe así -virtual int CompareTo(Number *par) = 0;

el compilador será maldecido en lugar del sustituto de excepción autoescrito.

No necesito que el compilador lo jure porque constantemente manipulo clases de tipo base (Number en este ejemplo). Si el compilador lo jura, el código no se ejecutará en absoluto