come creare oggetti in modo dinamico? (Alcune cose OOP) - pagina 2

 
Doerk Hilger:

Non mi piace dire che lo state facendo in modo totalmente sbagliato, ma è così, perché questa è programmazione a struttura e non OOP. La grande differenza è il potere dell'ereditarietà e dell'overloading. E a proposito, non si può davvero ereditare da oggetti grafici reali, ma si può rappresentare qualsiasi cosa come un oggetto di codice e fare riferimento a una linea o qualsiasi altra cosa da questo oggetto. Questo è il modo in cui viene fatto di solito in qualsiasi classe, non importa se è una classe MFC o MQL, è tutto uguale.

Se le vostre linee sono oggetti, allora trattatele come tali. Non trattate gli array all'esterno, fatelo all'interno di una collezione di classi e lavorate con i puntatori. Date un'occhiata a CWndContainer e fatene un'idea. Questa classe è un contenitore che gestisce principalmente gli array di puntatori per gli oggetti CWnd. Fai un passo avanti, la tua struttura dovrebbe essere:

CObject come base per ogni oggetto

CPriceTimeObjects come base per ogni oggetto basato sul prezzo/tempo, come le linee, deriva da CObject. Controlla la creazione, tiene il tempo e il prezzo e chiama una OnCreate(), che può essere usata dall'erede successivo. Ha anche una funzione Tick che chiama OnTick() virtuale che viene poi sovraccaricata dagli eredi.

CTrendLine come base per le linee di tendenza, eredita da CPriceTimeObjects e gestisce OnCreate dove crea la linea finale usando la funzione ObjectCreate. Dovrebbe anche avere un gestore OnTick() per reagire/rispondere agli eventi Tick, perché deve essere sensibile al prezzo, per quanto ho capito.

Oltre a questo, hai una classe contenitore che gestisce un array di puntatori che contiene tutti gli oggetti CTimePriceObject desiderati, essa eredita anch'essa da CTimePriceObject e passa OnTick() ai suoi "figli". Il contenitore ha anche una funzione che gestisce l'OnChartEvent() per aggiungere linee o per rimuoverle. Dovrebbe anche avere una funzione di scansione per analizzare tutti gli oggetti esistenti nel caso in cui l'esperto sia stato aggiunto dopo la creazione delle linee. Inoltre gestisce l'OnTick() sovraccaricato di CTimePrice, fa un loop sull'array, chiede ad ogni oggetto CTrendLine presente se si sente in qualche modo responsabile di reagire chiamando la funzione Tick di ogni oggetto figlio che è gestita da un OnTick virtuale. Perché ancora questo? Perché la CTrendLine sovraccarica questa funzione anche da CTimePrice e in questo modo questa classe può essere ereditata anche da ulteriori eredi con ulteriori funzioni.

Molto bello, Dorek.

Ho alcune domande però:

Perché il contenitore che contiene CTimePriceObject deve ereditare se stesso da CTimePriceObject? Per esempio, CIndicators in lib standard eredita da CArrayObj e molto probabilmente fa riferimento a CIndicator senza ereditarlo.
So che vuole usare la lib standard e non gli array regolari, ma qualcosa sul concetto che un contenitore di oggetti di un certo tipo dovrebbe ereditare l'oggetto stesso non mi è chiaro (perché il contenitore non è esso stesso un oggetto che contiene, cioè non c'è una relazione "è un") anche se sento che è giusto.
Puoi per favore chiarire il tuo punto di vista su questo?

In secondo luogo, sembri costruire il framework da zero, senza fare affidamento sulla libreria standard quando dici che CTrendLine crea la linea finale usando la funzione ObjectCreate nativa del linguaggio.
Quando consiglieresti di estendere le librerie standard stesse, per esempio, supponiamo che io voglia che tutti gli oggetti CIndicator della libreria standard siano in grado di dire quante volte il prezzo tocca un buffer in essi
nelle ultime X barre. Come faresti, perché se estendi una classe base come CIndicator, o CIndicatorBuffer, allora hai tutti gli oggetti dipendenti della libreria standard che devono ereditare dal tuo
nuovo CIndicator o nuovo CBuffer, come per esempio CiMA. Potresti copiare tutte quelle classi di indicatori personalizzati nella tua cartella e cambiare la loro eredità al tuo nuovo CIndicatorBuffer? cosa succede se poi metaquotes aggiunge qualcosa alle sue classi standard CIndicatorBuffer o superiori?

Grazie mille per le intuizioni.

BR

 

Willbur:

...

Quando vai in questo modo il mio oggetto SmartLine dovrebbe apparire nel menu di MT5 accanto alla linea di tendenza, le frecce, l'oggetto testo e tutta questa roba.

...

Se MT5 lo permette, solo allora dobbiamo discutere la questione rimanente, come l'oggetto potrebbe essere attivato dal programma terminale quando il prezzo cambia.

...
Non è possibile farlo con mql5.
 
Amir Yacoby:

Molto bene, Dorek.

Ho alcune domande però:

Perché il contenitore che contiene CTimePriceObject deve ereditare se stesso da CTimePriceObject? Per esempio, CIndicators in lib standard eredita da CArrayObj e molto probabilmente fa riferimento a CIndicator senza ereditarlo.
So che vuole usare la lib standard e non gli array regolari, ma qualcosa sul concetto che un contenitore di oggetti di un certo tipo dovrebbe ereditare l'oggetto stesso non mi è chiaro (perché il contenitore non è esso stesso un oggetto che contiene, cioè non c'è una relazione "è un") anche se sento che è giusto.
Puoi per favore chiarire il tuo punto di vista su questo?

In secondo luogo, sembri costruire il framework da zero, senza fare affidamento sulla libreria standard quando dici che CTrendLine crea la linea finale usando la funzione ObjectCreate nativa del linguaggio.
Quando consiglieresti di estendere le librerie standard stesse, per esempio, supponiamo che io voglia che tutti gli oggetti CIndicator della libreria standard siano in grado di dire quante volte il prezzo tocca un buffer in essi
nelle ultime X barre. Come faresti, perché se estendi una classe base come CIndicator, o CIndicatorBuffer, allora hai tutti gli oggetti dipendenti della libreria standard che devono ereditare dal tuo
nuovo CIndicator o nuovo CBuffer, come per esempio CiMA. Potresti copiare tutte quelle classi di indicatori personalizzati nella tua cartella e cambiare la loro eredità al tuo nuovo CIndicatorBuffer? cosa succede se poi metaquotes aggiunge qualcosa alle sue classi standard CIndicatorBuffer o superiori?

Grazie mille per gli approfondimenti.

BR

1. Ereditarietà del contenitore.

Una limitazione di MQL è che non si possono avere eredità multiple. Per questo motivo, si deve morire almeno una volta. Naturalmente hai ragione, avrebbe anche senso ereditare da CArrayObj, ma non lo farei, perché il contenitore gestisce tutti gli eventi che qualsiasi oggetto CTimePrice gestisce, per esempio il Tick()->OnTick() e li distribuisce ai suoi figli, mentre un oggetto array non ha nulla a che fare con questo. Un oggetto contenitore che contiene altri oggetti che ereditano anch'essi dalla stessa classe base, ha semplicemente più punti in comune. Un tale contenitore potrebbe anche avere un'ancora, basata su un prezzo e su un tempo, e quando si sposta/spostamento di un tale contenitore, sarebbe compito del contenitore spostare anche tutti i suoi "figli". Questo è solo un esempio, ma la lista di queste idee è probabilmente più lunga di quella delle idee sulle funzionalità degli array.

E sì, è anche così, che ho davvero creato tali classi che trattano molti tipi di tali oggetti e l'esperienza che ho nel frattempo mi dice che è stata la decisione giusta per gestire l'ereditarietà in questo modo.

2. Framework da zero.

Simile qui. Quando ho iniziato ad andare un po' più a fondo nelle librerie standard ho trovato molte cose che non mi piacevano. Non solo le cattive prestazioni di quelle, ma anche la mancanza di flessibilità e anche a causa di un'architettura incompleta. Mi manca per esempio una classe/oggetto CMouse globale così come una classe tra CWnd e CObject, perché gli oggetti CWnd sono figli del grafico così come lo sono le linee, e non c'è alcuna connessione a questi e nessuna implementazione finale di tali oggetti come ho descritto sopra. E non c'è un oggetto master, che contiene tutti questi oggetti grafici e che rende possibile mostrarli/nasconderli tutti con un solo comando, distruggerli o altro. CCanvas, stessa cosa, una bella classe ma dov'è l'implementazione con CWnd che mi permette di creare oggetti interattivi basati su bitmap che ereditano da CWnd? E così via.

Inoltre, l'intera struttura di tutte le librerie standard non permette catene laterali, ma questo è necessario, perché MQL non permette i puntatori a vuoto. Per esempio: Sto usando una classe chiamata CDragLine che mi permette di creare oggetti trendline che possono essere trascinati. Se tale oggetto trendline è collegato a un ordine e inoltre a un pannello/display qualsiasi, ho bisogno della possibilità di usare una catena laterale tale che il pannello collegato riceva anche informazioni sui movimenti/cambiamenti, che sono causati da cambiamenti dell'ordine. E viceversa ho bisogno della possibilità di spostare l'ordine e di informare il pannello quando la linea stessa è stata trascinata. Questo tipo di messaggistica triangolare non può essere fatto con la libreria standard. Questo è fatto da catene laterali, il che significa, assolutamente tutti gli oggetti ereditano da una versione avanzata di CObject. Questa classe ha la possibilità di collegare qualsiasi altro oggetto ad un oggetto, e di inviare messaggi a tale oggetto collegato. In questo modo è possibile una messaggistica molto più complessa ed efficace senza avere del codice strano.

Non posso dare alcuna raccomandazione su cosa tutti dovrebbero fare, ma in realtà ho deciso di abbandonare il 99% delle librerie standard, le uniche classi che ho lasciato dalle originali sono CCanvas (ma con alcune modifiche e correzioni di bug, vedi Code Base) e CSymbolInfo.

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

Se qualcuno è interessato alla funzionalità della catena laterale, ecco il codice della mia classe CObject. Se si sostituisce il CObject originale in Object.mqh dopo ogni aggiornamento, la maggior parte delle parti delle librerie standard ottiene i miglioramenti di questa funzionalità di catena laterale. E a proposito, viene implementata anche la connessione dei nodi - cosa che non viene fatta nell'originale.

Per aggiungere tale caratteristica della catena laterale, fallo come segue all'interno della classe ricevente:

//--- 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))
               {
               //...
               } 
         }            
   };

Le classi di invio CWhatEver e CFurther non sanno nulla del ricevitore, se c'è un ricevitore o no. Il codice è solo il seguente:

//---
//...

   CustomEvent(123456);

//...
//---

Ecco la sostituzione del 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
 

Grazie per aver elaborato e per aver condiviso l'idea della catena laterale.

Francamente, non capisco bene, e il codice non si compila (ho creato CWhatEver e CFurther come classi vuote ereditando direttamente il CObject che hai postato con solo il costruttore solo per avere l'idea).

2 errori di compilazione ricevuti su CWhatEver::CWhatEver non può chiamare la funzione membro privata e stesso errore per CFurther.

Comunque, ho cercato su Google il termine "catena laterale" e non ho trovato, se per caso conosci del materiale scritto che posso leggere sarebbe fantastico.

BR

 

Sì, le classi sono vuote, ecco perché non compilano. Sono solo dei segnaposto.

Le catene laterali potrebbero non essere la descrizione ufficiale, è piuttosto il mio workaround MQL pulito per i puntatori nulli e/o l'ereditarietà multipla. In parole povere: Vi dà la possibilità di comunicare tra qualsiasi classe ed è molto utile se gli oggetti sono incorporati in classi, perché si può "dire" loro che la classe contenente è il ricevitore dei loro eventi personalizzati. In questo modo potete evitare qualsiasi codifica spaghetti.

Per esempio, se avete qualcosa come

return OnClick();

che chiamerebbe prima l'overload dell'ultima classe derivante, potreste estenderlo a qualcosa come

return OnClick()&CustomEvent(CUSTOM_CLICK);

In questo modo, non solo la classe derivante che normalmente ha un

bool virtuale OnClick()

verrebbe notificata, anche la classe che include l'oggetto è in grado di ricevere la notifica del clic.

 

Vuoi dire che è un modo per comunicare tra due oggetti qualsiasi, non con la solita messaggistica che ha bisogno di conoscere l'altro oggetto a cui sta inviando un messaggio, ma attraverso eventi personalizzati?

Questa idea l'ho capita, ma cosa intendi per oggetti incorporati? Nel tuo codice di CAnyClass hai due oggetti privati. Intendi per embedded che i loro corpi devono essere decalcati anche all'interno di
CAnyClass o possono essere definiti all'esterno? Voglio dire, embedded come usi il termine è un oggetto che è privato dentro un altro oggetto?

Se per esempio scrivo CWhatEver fuori da CAnyClass, e voglio mandare un messaggio a CAnyClass su un evento, vuoi dire che in CWhatEver restituirò

return OnClick()&CustomEvent(CUSTOM_CLICK); // <=== vuoi dire && invece di & ????

che manderà un messaggio alla classe derivata di CWhatEver come al solito e anche alla classe contenente che è CAnyClass (a causa dell'evento personalizzato)?

E perché CObject è definito all'interno di un #ifndef?

BTW, per curiosità, qual è lo scopo di stdlib.mqh nel tuo CObject?

 

Dal basso verso l'alto ...

- lo stdlib, non è comunque incluso nell'object.mqh originale? Non ne ho bisogno qui, ma contiene CompareDouble() che è l'unico modo affidabile per confrontare due doppi

- Uso #ifndef per evitare definizioni doppie di serie e per la compilazione condizionale

- sì, per la messaggistica triangolare

- la & binaria e la versione logica && sono le stesse con risultati bool

- Per incorporato intendo, se un'istanza/oggetto di una classe estranea è parte di un'altra classe ed entrambe non derivano l'una dall'altra. Usate l'esempio di OnClick() e supponiamo che l'oggetto m_ gestisca OnClick() in qualche modo per scopi interni, per esempio se è un pulsante o altro. Come sarebbe il codice se avete bisogno di sapere all'interno di CAnyClass che c'è stato un click senza questo evento personalizzato?

Ad essere onesti, non è una funzionalità di cui si ha bisogno ogni giorno per ogni scopo, ma se c'è una situazione in cui è necessario comunicare non solo la direzione, è la soluzione.

Torniamo al problema del thread originale. L'idea era di usare gli array per gestire più linee che reagiscono sul prezzo. Questi oggetti linea gestiscono gli eventi quando le loro linee sono attraversate da una candela e forzano qualche azione. Ho raccomandato di usare un contenitore. Se questo contenitore ora vuole contare queste azioni, solo per esempio, può essere facilmente fatto in questo modo quando il contenitore dice ad ogni oggetto che è il ricevitore dell'evento personalizzato usando per esempio

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

mentre la classe CTrendLine - ovviamente - deve implementarlo in qualche modo come questo

return OnLineCrossed()&CustomEvent(CTRENDLINE_CROSSED);

 
Wow, che bello che ci sia una tale discussione nel forum. Anche se devo ammettere che sono ancora all'inizio di OOP.

Purtroppo, vado in vacanza
per due settimane.Aereo parte in ore (ok, potrebbe essere peggio).

Una domanda che ho già a questo punto: C da qualche parte una documentazione dettagliata sul lavoro del telaio MQL?


Willbur
 
Willbur:
..

Una domanda che ho già a questo punto: C da qualche parte una documentazione dettagliata sul lavoro MQL?

No :-(

Dalla mia esperienza, è bene imparare a studiare il "quadro MQL". Ma come detto da Doerk, c'è un sacco di problemi con la libreria standard, e secondo me, non è utilizzabile in progetti seri e grandi.

 
Doerk Hilger:

Dal basso verso l'alto ...

- lo stdlib, non è comunque incluso nell'object.mqh originale? Non ne ho bisogno qui, ma contiene CompareDouble() che è l'unico modo affidabile per confrontare due doppi

- Uso #ifndef per evitare definizioni doppie di serie e per la compilazione condizionale

- sì, per la messaggistica triangolare

- la & binaria e la versione logica && sono le stesse con risultati bool

- Per incorporato intendo, se un'istanza/oggetto di una classe estranea è parte di un'altra classe ed entrambe non derivano l'una dall'altra. Usate l'esempio di OnClick() e supponiamo che l'oggetto m_ gestisca OnClick() in qualche modo per scopi interni, per esempio se è un pulsante o altro. Come sarebbe il codice se avete bisogno di sapere all'interno di CAnyClass che c'è stato un click senza questo evento personalizzato?

Ad essere onesti, non è una funzionalità di cui si ha bisogno ogni giorno per ogni scopo, ma se c'è una situazione in cui è necessario comunicare non solo la direzione, è la soluzione.

Torniamo al problema del thread originale. L'idea era di usare gli array per gestire più linee che reagiscono sul prezzo. Questi oggetti linea gestiscono gli eventi quando le loro linee sono attraversate da una candela e forzano qualche azione. Ho raccomandato di usare un contenitore. Se questo contenitore ora vuole contare queste azioni, solo per esempio, può essere facilmente fatto in questo modo quando il contenitore dice ad ogni oggetto che è il ricevitore dell'evento personalizzato usando per esempio

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

mentre la classe CTrendLine - ovviamente - deve implementarlo in qualche modo come questo

return OnLineCrossed()&CustomEvent(CTRENDLINE_CROSSED);

stdlib pensavo fosse solo MQL4 ma forse mi sbaglio.
Scusa, non ho capito perché l'intero CObject deve essere dentro #ifndef. Che cosa sarà definito doppiamente se sarebbe scritto in chiaro? E btw, perché metti CStruct come classe vuota sopra CObject?

Riguardo alla classe incorporata, (scusa, sono un programmatore da 25 anni ma non OO) il tuo esempio si riferisce a una situazione come questa per esempio? Supponiamo che io sia una classe CCar, e ho incorporato una classe CWheel.
E la CWheel ha un gestore di eventi per la pressione dell'aria minima o qualcosa del genere, e io come auto ho bisogno di saperlo?
Se riesci a trovare similitudini con questo esempio o altri specifici, perché non sono ancora del tutto lì in termini di quale classe spara eventi (nell'esempio che hai dato con il click è ovviamente il grafico) e chi lo gestisce. E anche tecnicamente, capendo per esempio queste linee (presumo che il loro scopo sia mostrare come il contenitore farà il lavoro di gestire le linee di tendenza che attraversano il prezzo di una barra):

m_trendline[n].CustomEventReceiver(PTR(this));
puoi chiarire se ho capito bene? chiama tutte le linee di tendenza [1..n] in un ciclo? se sì, che tipo di evento generale potrebbe essere quello che il contenitore sta gestendo in questo momento?
Può essere come una nuova barra o un cambiamento di prezzo che ha il potenziale per innescare l'evento CTRENDLINE_CROSSED? E perché invia GetPointer(this) ad ogni linea?
la linea ha un contenitore di callback, e perché?
Quindi, se questo è il caso

return OnLineCrossed()&CustomEvent(CTRENDLINE_CROSSED);
allora ogni linea chiamerà la propria OnLineCrossed() - che dovrebbe essere un metodo regolare che controlla solo se il prezzo è stato attraversato da quella specifica linea,
e poi chiamerà CustomEvent(CTRENDLINE_CROSSED) - che il suo ruolo è solo quello di chiamare ChartCustomEvent(..) per lanciare l'evento CTRENLINE_CROSSED - che nella tua struttura sarà gestito di nuovo nel contenitore? è l'ultima parte che rende la comunicazione triangolare o forse sto solo mescolando due concetti diversi. Quindi il contenitore avrà un gestore CustomEvent che gestisce CTRENDLINE_CROSSED?

scrivendo che alcune cose sono diventate più chiare penso (e il CCar può essere ignorato se si vuole, e concentrarsi solo sulla linea di tendenza) ma ancora non capisco perché spostare GetPointer(this) su ogni linea? E su quale tipo di evento per esempio il contenitore chiamerà il ricevitore di eventi personalizzato in ogni linea per rilevare i cambiamenti di prezzo, e dov'è la comunicazione triangolare o si applica solo l'esempio precedente per il solo triangolare (con CAnyClass)?
Puoi forse applicare l'esempio triangolare su questa linea di tendenza?

Ti ringrazio davvero per il tuo tempo, la tua pazienza e il tuo aiuto finora, non è affatto ovvio. Devo dire che mi hai aperto gli occhi sul potenziale della programmazione guidata dagli eventi.