¿Cómo crear objetos de forma dinámica? (Algunas cosas de POO) - página 2

 
Doerk Hilger:

No me gusta decir que lo estás haciendo totalmente mal, pero lo haces, porque esto es programación estructural y no OOP. La gran diferencia es el poder de la herencia y la sobrecarga. Y por cierto, no puedes heredar de objetos gráficos reales, pero puedes representar cualquier cosa como un objeto de código y referirte a una línea o lo que sea desde este objeto. Así es como se hace normalmente en cualquier clase, no importa si es una clase MFC o MQL, es todo lo mismo.

Si sus líneas son objetos, entonces trátelos como tales. No trates con arrays fuera, hazlo dentro de una colección de clases y trabaja con punteros. Echa un vistazo a CWndContainer y tener una idea de ella. Esta clase es un contenedor que gestiona principalmente arrays de punteros para objetos CWnd. Ve un paso más allá, tu estructura debería ser

CObject como base para cada objeto

CPriceTimeObjects como base para cada objeto basado en el precio/tiempo, como las líneas, deriva de CObject. Controla la creación, mantiene el tiempo y el precio y llama a un OnCreate(), que puede ser utilizado por el siguiente heredero. También tiene una función Tick que llama a la función virtual OnTick() que luego es sobrecargada por los herederos.

CTrendLine como base para las líneas de tendencia, hereda de CPriceTimeObjects y maneja OnCreate donde crea la línea final usando la función ObjectCreate. También debería tener un manejador OnTick() para reaccionar/responder a los eventos Tick, ya que será sensible al precio por lo que he entendido.

Además de esto, tienes una clase contenedora que gestiona un array de punteros que contiene todos los objetos CTimePriceObject que quieras, hereda también de CTimePriceObject y pasa OnTick() a sus "hijos". El contenedor tiene una función también que maneja el OnChartEvent() para añadir líneas o eliminarlas. También debe tener una función de escaneo para escanear todos los objetos existentes para el caso, el experto fue añadido después de la creación de líneas. Además maneja el OnTick() sobrecargado de CTimePrice, hace un bucle en el array allí, pregunta a cada objeto CTrendLine en él si se siente de alguna manera responsable de reaccionar llamando a la función Tick de cada objeto hijo que es manejado por un OnTick virtual. ¿Por qué esto de nuevo? Porque el CTrendLine sobrecarga esta función también de CTimePrice y de esta manera esta clase también puede ser heredada por otros herederos con más funciones.

Muy bien, Dorek.

Sin embargo, tengo algunas preguntas:

¿Por qué el contenedor que contiene CTimePriceObject tiene que heredar de CTimePriceObject? Por ejemplo, CIndicators en la librería estándar hereda de CArrayObj y probablemente se refiera a CIndicator sin heredarlo.
Sé que quiere usar la lib estándar y no los arrays normales, pero algo sobre el concepto de que un contenedor de objeto de cierto tipo debe heredar el objeto mismo no me queda claro (porque el contenedor no es en sí mismo un objeto que contiene, es decir, no hay una relación "es un") aunque me parece correcto.
¿Puede aclarar su opinión al respecto?

En segundo lugar, parece que construyes el framework desde cero, sin depender de la biblioteca estándar cuando dices que CTrendLine crea la línea final usando la función ObjectCreate nativa del lenguaje.
Cuando recomendarías extender las propias librerías estándar, por ejemplo, supongamos que quiero que todos los objetos CIndicator de la librerías estándar sean capaces de decir cuántas veces el precio toca un buffer en ellos
en las últimas X barras. ¿Cómo lo haría, porque si extiendes una clase base como CIndicator, o CIndicatorBuffer, entonces tienes todos los objetos dependientes de la librería estándar que tienen que heredar de tu
nuevo CIndicator o nuevo CBuffer, como por ejemplo CiMA. ¿Copiarías todas esas clases de indicadores personalizados a tu carpeta y cambiarías su herencia a tu nuevo CIndicatorBuffer? ¿qué pasa entonces si metaquotes añade algo a sus clases estándar CIndicatorBuffer o superiores?

Muchas gracias por la información.

BR

 

Willbur:

...

Cuando se va por este camino mi objeto SmartLine debería aparecer en el menú de MT5 al lado de la línea de tendencia, las flechas, el objeto de texto y todas estas cosas.

...

Si MT5 permite eso, sólo entonces tenemos que discutir la cuestión restante, cómo el objeto podría ser activado por el programa de terminal cuando el precio cambia.

...
No se puede hacer eso con mql5.
 
Amir Yacoby:

Muy bien, Dorek.

Sin embargo, tengo algunas preguntas:

¿Por qué el contenedor que contiene CTimePriceObject tiene que heredar de CTimePriceObject? Por ejemplo, CIndicators en la librería estándar hereda de CArrayObj y probablemente se refiera a CIndicator sin heredarlo.
Sé que quiere usar la lib estándar y no los arrays normales, pero algo sobre el concepto de que un contenedor de objeto de cierto tipo debe heredar el objeto mismo no me queda claro (porque el contenedor no es en sí mismo un objeto que contiene, es decir, no hay una relación "es un") aunque me parece correcto.
¿Puede aclarar su opinión al respecto?

En segundo lugar, parece que construyes el framework desde cero, sin depender de la biblioteca estándar cuando dices que CTrendLine crea la línea final usando la función ObjectCreate nativa del lenguaje.
Cuando recomendarías extender las propias librerías estándar, por ejemplo, supongamos que quiero que todos los objetos CIndicator de la librerías estándar sean capaces de decir cuántas veces el precio toca un buffer en ellos
en las últimas X barras. ¿Cómo lo haría, porque si extiendes una clase base como CIndicator, o CIndicatorBuffer, entonces tienes todos los objetos dependientes de la librería estándar que tienen que heredar de tu
nuevo CIndicator o nuevo CBuffer, como por ejemplo CiMA. ¿Copiarías todas esas clases de indicadores personalizados a tu carpeta y cambiarías su herencia a tu nuevo CIndicatorBuffer? ¿qué pasa entonces si metaquotes añade algo a sus clases estándar CIndicatorBuffer o superiores?

Muchas gracias por los comentarios.

BR

1. Herencia del contenedor.

Una limitación de MQL es, que usted no puede tener múltiples herencias. Debido a eso, uno tiene que morir al menos una vez. Por supuesto tienes razón, también tendría sentido heredar de CArrayObj, pero yo no lo haría, porque el contenedor maneja todos los eventos que cualquier objeto CTimePrice maneja, por ejemplo el Tick()->OnTick() y los distribuye a sus hijos, mientras que un objeto array no tiene nada que ver con eso. Un objeto contenedor que contiene otros objetos que también heredan de la misma clase base, tienen simplemente más puntos en común. Un contenedor de este tipo también podría tener un ancla, basada en un precio y en un tiempo, y cuando se mueve/desplaza un contenedor de este tipo, sería el trabajo del contenedor mover todos sus "hijos" también. Esto es sólo un ejemplo, pero la lista de tales ideas es probablemente más larga que la lista de ideas sobre las funcionalidades de los arrays.

Y sí, también es así, que realmente creé tales clases que se ocupan de muchos tipos de tales objetos y la experiencia que tengo en el ínterin me dice, que fue la decisión correcta para gestionar la herencia de esta manera.

2. Marco de trabajo desde cero.

Similar aquí. Cuando empecé a profundizar un poco más en las librerías estándar encontré muchas cosas, que no me gustaban. No sólo el mal rendimiento de las mismas, sino también la falta de flexibilidad y también por una arquitectura incompleta. Me falta, por ejemplo, una clase/objeto global CMouse, así como una clase entre CWnd y CObject, porque los objetos CWnd son hijos del gráfico al igual que las líneas, y no hay conexión con ellos ni implementación final de ningún objeto de este tipo como he descrito anteriormente. Además, no hay un objeto maestro que contenga todos los objetos del gráfico y que permita mostrarlos/ocultarlos todos con un solo comando, destruirlos o lo que sea. CCanvas, lo mismo, una bonita clase pero ¿dónde está la implementación con CWnd que me permita crear objetos interactivos basados en mapas de bits que hereden de CWnd? Y así sucesivamente.

Además, toda la estructura de todas las bibliotecas estándar no permite cadenas laterales, pero esto es necesario, porque MQL no permite punteros void. Por ejemplo: Estoy usando una clase llamada CDragLine que me permite crear objetos de línea de tendencia que pueden ser arrastrados. Si este objeto de línea de tendencia está conectado a una orden y además a un panel/pantalla, necesito la posibilidad de utilizar una cadena lateral que permita al panel conectado obtener información sobre los movimientos/cambios, que son causados por los cambios de la orden. Y viceversa, necesito la opción de mover la orden e informar al panel cuando la línea misma fue arrastrada. Este tipo de mensajería triangular no se puede hacer con la biblioteca estándar. Esto se hace mediante cadenas laterales, lo que significa, que absolutamente todos los objetos heredan de una versión avanzada de CObject. Esta clase tiene la posibilidad de conectar cualquier otro objeto a un objeto, y enviar mensajes a dicho objeto conectado. De esta manera es posible una mensajería mucho más compleja y efectiva sin tener ningún código raro.

No puedo dar ninguna recomendación de lo que todo el mundo debería hacer, pero en realidad decidí dejar de lado el 99% de las librerías estándar, las únicas clases que me quedan de las originales son CCanvas (pero con algunos cambios y correcciones de errores, ver Código Base) y CSymbolInfo.

--------------

Si alguien está interesado en la funcionalidad de la cadena lateral, aquí está el código de mi clase CObject. Si reemplazas el CObject original en Object.mqh después de cada actualización, la mayoría de las partes de las bibliotecas estándar obtienen las mejoras de esta función de cadena lateral. Y por cierto, también se implementa la conexión de nodos - que no se hace en el original.

Para añadir esta característica de cadena lateral, hazlo de la siguiente manera dentro de la clase receptora:

//--- Example for receiving class
//---

class CAnyClass : public CAnyBaseClass // of course CAnyBaseClass inherits from CObject in the end too
   {
   private:
      CWhatEver   m_object;     // embedded object
      CFurther    m_further;    // embedded object

   public:
   //+------------------------------------------------------------------+
   //|  Creation                                                        |
   //+------------------------------------------------------------------+
   CAnyClass(void)
      {
      m_classname="CAnyClass"; 

      //--- Connect side chains 
      m_object.CustomEventReceiver(PTR(this));
      m_further.CustomEventReceiver(PTR(this));
      }
   
   protected:
   //+------------------------------------------------------------------+
   //|  Custom event handler for side chain messages                    |
   //+------------------------------------------------------------------+
      virtual void      OnCustomEvent(CObject * sender, int eventid)
         {
            if (sender==PTR(m_object))
               {
               switch (eventid)
                  {
                  case 123456:
                     Print("Here we go with 123456");
                     break;
               //...
                  }
               }
            else if (sender==PTR(m_further))
               {
               //...
               } 
         }            
   };

Las clases emisoras CWhatEver y CFurther no saben nada sobre el receptor, de si hay un receptor o no. El código es simplemente el siguiente:

//---
//...

   CustomEvent(123456);

//...
//---

Aquí está el reemplazo de CObject:

//+------------------------------------------------------------------+
//|                                                       Object.mqh |
//|                                               Copyright by Doerk |
//+------------------------------------------------------------------+

#ifndef __DH_OBJECT_CLASS
#define __DH_OBJECT_CLASS

#include <stdlib.mqh>
//+------------------------------------------------------------------+
//|                                                                  |
//| Defintions                                                       |
//|                                                                  |
//+------------------------------------------------------------------+
#ifndef  PTR
   #define  PTR(object) GetPointer(object)
   #define  PTR_DELETE(object) { if (CheckPointer(object)==POINTER_DYNAMIC) delete object; }
   #define  PTR_INVALID(object) (object==NULL || CheckPointer(object)==POINTER_INVALID)
#endif   

enum ENUM_FILE_IO
   {
   FILE_IO_NONE = 0,    //--- No file interaction
   FILE_IO_BINARY = 1,  //--- Binary, OnLoad() / OnSave() events
   FILE_IO_INI = 2,     //--- Ini file, OnLoadIni() / OnSaveIni() events
   };
   
//+------------------------------------------------------------------+
//|                                                                  |
//| Class CStruct - the base of everything                           |
//|                                                                  |
//+------------------------------------------------------------------+
class CStruct
   {
   };
//+------------------------------------------------------------------+
//|                                                                  |
//| Class CObject                                                    |
//|                                                                  |
//+------------------------------------------------------------------+
class CObject : public CStruct
  {
   protected:      
      long              m_obj_id;               //--- Unique ID of each object
      string            m_classname;            //--- Name of (deriving) class
      CObject          *m_prev;                 //--- Previous item in chain
      CObject          *m_next;                 //--- Next item in chain
      CStruct          *m_struct;               //--- Additional attached struct
      CObject          *m_object;               //--- Additional attached object
      string            m_tag;                  //--- Additional tag (File operation)
   private:
      CObject          *m_eventreceiver;        //--- Object which gets custom notifications
      ENUM_FILE_IO      m_fileio;               //--- Enables/disable file input/output
      
   public:
      //+------------------------------------------------------------------+
      //| Construction                                                     |
      //+------------------------------------------------------------------+
      CObject(void) : m_fileio(FILE_IO_BINARY),
                      m_struct(NULL),
                      m_tag(NULL),
                      m_object(NULL)
         {
      //--- Set ID
            __obj_cnt++;
            m_obj_id=__obj_cnt;
      //--- Reset notified object            
            m_eventreceiver=NULL;
      //--- Connect chain            
            Prev(__obj_prev);
            if (__obj_prev!=NULL)
               __obj_prev.Next(PTR(this));
            __obj_prev=PTR(this);
            Next(NULL);
            
         } 
         
      //+------------------------------------------------------------------+
      //| Destruction                                                      |
      //+------------------------------------------------------------------+
      ~CObject(void)
         {
      //--- Reconnect chain
            if (m_prev!=NULL)
               m_prev.Next(m_next);
            if (m_next!=NULL)
               m_next.Prev(m_prev);    
            if (__obj_prev==PTR(this))
               __obj_prev=Prev();                    
         } 
      //+------------------------------------------------------------------+
      //| Chain access                                                     |
      //+------------------------------------------------------------------+
   public:      
      CObject          *Prev(void)                                      const { return(m_prev); }
      void              Prev(CObject *node)                                   { m_prev=node;    }
      CObject          *Next(void)                                      const { return(m_next); }
      void              Next(CObject *node)                                   { m_next=node;    }
      //+------------------------------------------------------------------+
      //| Custom events - allows interaction between embedded objects and  |
      //|                containers                                        |
      //+------------------------------------------------------------------+
   public:
      CObject *         CustomEventReceiver(void)                       const { return(m_eventreceiver); }
      bool              CustomEventReceiver(CObject *receiver)
         {
            if (m_eventreceiver!=NULL)
               return false;
            m_eventreceiver=receiver;
            return true;   
         }
      void              CustomEvent(int eventid=0)
         {
            if (!PTR_INVALID(m_eventreceiver))
               m_eventreceiver._CustomEvent(PTR(this), eventid);
         }      
      void              _CustomEvent(CObject * sender, int eventid)
         {
            OnCustomEvent(sender, eventid);
         }      
   protected:
      virtual void      OnCustomEvent(CObject * sender, int eventid)         
         {
         }
                                          
      //+------------------------------------------------------------------+
      //| File interaction                                                 |
      //+------------------------------------------------------------------+
   public:
      bool              Save(const int file_handle)                           { if (m_fileio==FILE_IO_NONE) return true; return(OnSave(file_handle));   }
      bool              Load(const int file_handle)                           { if (m_fileio==FILE_IO_NONE) return true; return(OnLoad(file_handle));   }
      bool              Save(CObject *fileobject)                             { if (m_fileio==FILE_IO_NONE) return true; return(OnSave(fileobject)); }
      bool              Load(CObject *fileobject)                             { if (m_fileio==FILE_IO_NONE) return true; return(OnLoad(fileobject)); }
      bool              LoadDefault(void)                                     { return (OnLoadDefault()); }
      bool              FileIO(const ENUM_FILE_IO flag)                       { m_fileio=flag; return true; }
      ENUM_FILE_IO      FileIO(void)                                          { return m_fileio; }
   protected:
      virtual bool      OnSave(const int file_handle)                         { return true; }
      virtual bool      OnLoad(const int file_handle)                         { return true; }
      virtual bool      OnSave(CObject *fileobject)                           { return true; }
      virtual bool      OnLoad(CObject *fileobject)                           { return true; }
      virtual bool      OnLoadDefault(void)                                   { return true; }
      
      //+------------------------------------------------------------------+
      //| Identification                                                   |
      //+------------------------------------------------------------------+
   public:      
      long              Id(void)                                        const { return m_obj_id;    }
      virtual int       Type(void)                                      const { return(0);      }
      string            ClassName(void)                                 const { return(m_classname); }
      string            Tag(void)                                       const { return m_tag; }
      bool              Tag(string value)                                     { m_tag=value; return true; }

      //+------------------------------------------------------------------+
      //| Comparison                                                       |
      //+------------------------------------------------------------------+
   public:      
      virtual int       Compare(const CObject *node,const int mode=0)   const { return(0);      }
      
  };
//+------------------------------------------------------------------+
long __obj_cnt=-1;
CObject * __obj_prev=NULL;
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+

#endif                  // __DH_OBJECT_CLASS
 

Gracias por elaborar y por compartir la idea de la cadena lateral.

Francamente, no entiendo muy bien, y el código no compila (he creado CWhatEver y CFurther como clases vacías heredando directamente el CObject que has publicado con sólo un constructor para hacerme una idea).

Recibí 2 errores de compilación sobre CWhatEver::CWhatEver no puede llamar a una función miembro privada y el mismo error para CFurther.

De todas formas, he buscado en Google el término "side chain" y no he encontrado. Si por casualidad conoces algún material escrito que pueda leer sería genial.

BR

 

Sí, las clases están vacías, por eso no compilan. Estos son sólo marcadores de posición.

Las cadenas laterales pueden no ser la descripción oficial para ello, es más bien mi solución limpia MQL para los punteros nulos y / o la herencia múltiple. En pocas palabras: Le da la capacidad de comunicarse entre cualquier clase y es muy útil si los objetos están incrustados en las clases, porque se puede "decir" que la clase que contiene es el receptor de sus eventos personalizados. De esta manera puedes evitar cualquier código espagueti.

Por ejemplo, si tienes algo como

return OnClick();

que llamaría primero a la sobrecarga de la última clase derivada, podrías extenderlo a algo como

return OnClick()&CustomEvent(CUSTOM_CLICK);

De esta manera, no sólo la clase derivada que normalmente tiene un

bool virtual OnClick()

sino que también la clase que incluye el objeto puede recibir la notificación de clic.

 

¿Te refieres a que es una forma de comunicarse entre dos objetos cualesquiera, no mediante la mensajería habitual que necesita conocer el otro objeto al que envía un mensaje, sino a través de eventos personalizados?

Esa idea la entiendo, pero ¿qué quieres decir con objetos embebidos? en tu código de CAnyClass tenías dos objetos privados. ¿Quieres decir con incrustado que sus cuerpos deben ser también decalred dentro de
CAnyClass o pueden ser definidos fuera? Es decir, ¿incorporado como usas el término es un objeto que es privado dentro de otro objeto?

Si por ejemplo escribo CWhatEver fuera de CAnyClass, y quiero enviar un mensaje a CAnyClass sobre un evento, ¿quieres decir que en CWhatEver devolveré

return OnClick()&CustomEvent(CUSTOM_CLICK); // <=== quieres decir && en lugar de & ????

que enviará un mensaje a la clase derivada de CWhatEver como siempre y también a la clase contenedora que es CAnyClass (por el evento personalizado)?

¿Y por qué se define CObject dentro de un #ifndef?

Por cierto, sólo por curiosidad, ¿cuál es el propósito de stdlib.mqh en tu CObject?

 

De abajo a arriba ...

- la stdlib, ¿no se incluye de todos modos con el object.mqh original? No lo necesito aquí, pero contiene CompareDouble() que es la única forma fiable de comparar dos dobles

- Utilizo #ifndef para evitar definiciones dobles por estándar y para la compilación condicional

- sí, para la mensajería triangular

- el binario & y la versión lógica && son lo mismo con resultados bool

- por incrustado me refiero, si una instancia/objeto de una clase ajena es parte de otra clase y ambas no derivan una de otra. Usa el ejemplo de OnClick() y asume que el objeto m_object maneja el OnClick() de alguna manera para propósitos internos, por ejemplo si es un botón o lo que sea. ¿Cómo se vería el código si necesitas saber dentro de CAnyClass que hubo un clic sin este evento personalizado?

Para ser honesto, no es una funcionalidad que uno necesita todos los días para todos los propósitos, pero si hay una situación en la que usted necesita para comunicar no sólo direccional, es la solución.

Vuelve al tema del hilo original. La idea era utilizar arrays para gestionar múltiples líneas que reaccionan sobre el precio. Estos objetos de línea manejan eventos cuando ellos / sus líneas son cruzadas por una vela y forzar alguna acción. Yo recomendé usar un contenedor. Si este contenedor ahora quiere contar estas acciones, sólo por ejemplo, se puede hacer fácilmente de esta manera cuando el contenedor le dice a cada objeto que es el receptor del evento personalizado usando por ejemplo

m_trendline[n].CustomEventReceiver(PTR(this));

mientras que la clase CTrendLine - por supuesto - tiene que implementarlo de alguna manera así

return OnLineCrossed()&CustomEvent(CTRENDLINE_CROSSED);

 
Vaya, qué bien que haya una discusión de este tipo en el foro. Aunque debo admitir que todavía estoy en el comienzo de la OOP.

Por desgracia,
me voy de vacaciones durante dos semanas. Elavión sale en horas (ok, podría ser peor).

Una pregunta que tengo ya en este punto: ¿Existe en algún lugar una documentación detallada sobre el trabajo del marco MQL?


Willbur
 
Willbur:
..

Una pregunta que tengo ya en este punto: ¿Existe en algún lugar una documentación detallada sobre el marco de trabajo MQL?

No :-(

Desde mi experiencia, es bueno aprender a estudiar el "marco de trabajo mql". Pero como dijo Doerk, hay muchos problemas con la biblioteca estándar, y en mi opinión, no es utilizable en proyectos serios y grandes.

 
Doerk Hilger:

De abajo a arriba ...

- la stdlib, ¿no se incluye de todos modos con el object.mqh original? No lo necesito aquí, pero contiene CompareDouble() que es la única forma fiable de comparar dos dobles

- Utilizo #ifndef para evitar definiciones dobles por estándar y para la compilación condicional

- sí, para la mensajería triangular

- el binario & y la versión lógica && son lo mismo con resultados bool

- por incrustado me refiero, si una instancia/objeto de una clase ajena es parte de otra clase y ambas no derivan una de otra. Usa el ejemplo de OnClick() y asume que el objeto m_object maneja el OnClick() de alguna manera para propósitos internos, por ejemplo si es un botón o lo que sea. ¿Cómo se vería el código si necesitas saber dentro de CAnyClass que hubo un clic sin este evento personalizado?

Para ser honesto, no es una funcionalidad que uno necesita todos los días para todos los propósitos, pero si hay una situación en la que usted necesita para comunicar no sólo direccional, es la solución.

Vuelve al tema del hilo original. La idea era utilizar arrays para gestionar múltiples líneas que reaccionan sobre el precio. Estos objetos de línea manejan eventos cuando ellos / sus líneas son cruzadas por una vela y forzar alguna acción. Yo recomendé usar un contenedor. Si este contenedor ahora quiere contar estas acciones, sólo por ejemplo, se puede hacer fácilmente de esta manera cuando el contenedor le dice a cada objeto que es el receptor del evento personalizado usando por ejemplo

m_trendline[n].CustomEventReceiver(PTR(this));

mientras que la clase CTrendLine - por supuesto - tiene que implementarlo de alguna manera así

return OnLineCrossed()&CustomEvent(CTRENDLINE_CROSSED);

stdlib pensé que era sólo MQL4 pero tal vez estoy equivocado.
Lo siento, no entendí porque todo el CObject tiene que estar dentro de #ifndef. ¿Qué va a ser doblemente definido si se escribe sin más? Y por cierto, ¿por qué pones CStruct como clase vacía encima de CObject?

Sobre la clase incrustada, (lo siento soy un programador desde hace 25 años también, pero no OO) su ejemplo se refiere a una situación como esta, por ejemplo? Supongamos que tengo una clase CCar, y he incrustado una clase CWheel.
Y la CWheel tiene un manejador de eventos para el mínimo de presión de aire o algo así, y yo como el coche necesito saber eso?
Si se pueden encontrar similitudes con este ejemplo u otros específicos, porque todavía no estoy del todo en cuanto a qué clase dispara los eventos (en el ejemplo que pusiste con el clic es obviamente el gráfico) y quién lo maneja. E incluso técnicamente, entendiendo por ejemplo estas líneas (supongo que su propósito es mostrar cómo el contenedor hará el trabajo de gestionar las líneas de tendencia que cruzan el precio por una barra):

m_trendline[n].CustomEventReceiver(PTR(this));
¿puedes aclararme si lo he entendido bien? ¿llama a todas las líneas de tendencia [1..n] en un bucle? si es así, ¿qué tipo de evento general podría ser el que está manejando el contenedor en este momento? no es un evento específico de la línea
¿Puede ser como una nueva barra o un cambio de precio que tenga el potencial de activar el evento CTRENDLINE_CROSSED? ¿Y por qué envía GetPointer(this) a cada línea?
¿La línea tiene que hacer un callback del contenedor, y por qué?
Entonces, si ese es el caso

return OnLineCrossed()&CustomEvent(CTRENDLINE_CROSSED);
entonces cada línea llamará a su propio OnLineCrossed() - que debería ser un método normal que sólo comprueba si el precio ha sido cruzado por esa línea específica,
y luego llamará a CustomEvent(CTRENDLINE_CROSSED) - que su función es simplemente llamar a ChartCustomEvent(..) para disparar el evento CTRENLINE_CROSSED - que en su estructura será manejado de nuevo en el contenedor... es esa última parte la que hace la comunicación triangular o quizás estoy mezclando aquí dos conceptos diferentes. ¿Entonces el contenedor tendrá un manejador de CustomEvent que maneja CTRENDLINE_CROSSED?

Escribiendo esto algunas cosas quedan más claras (y el CCar puede ser ignorado si se quiere, y sólo centrarse en la línea de tendencia) pero todavía no entiendo por qué mover GetPointer(this) a cada línea? ¿Y en qué tipo de evento, por ejemplo, el contenedor llamará al receptor de eventos personalizado en cada línea para detectar los cambios de precio, y dónde está la comunicación triangular o sólo se aplica el ejemplo anterior sólo para triangular (con CAnyClass)?
¿Puede tal vez aplicar el ejemplo triangular en esta línea de tendencia?

Realmente te agradezco tu tiempo y paciencia y la ayuda hasta ahora, no es obvio en absoluto. Tengo que decir que me has abierto los ojos al potencial de la programación dirigida por eventos.