I tipi definiti dall'utente

La parola chiave typedef in C++ permette di creare tipi di dati definiti dall'utente. Per farlo, è sufficiente specificare un nuovo nome di tipo di dati per un tipo di dati già esistenti. Il nuovo tipo di dati non viene creato. Un nuovo nome per il tipo esistente viene definito, invece. I tipi definiti dall'utente rendono le applicazioni più flessibili: a volte, è sufficiente cambiare istruzioni typedef utilizzando macro sostitutive (#define). I tipi definiti dall'utente anche migliorano la leggibilità del codice dal momento che è possibile applicare nomi personalizzati ai tipi di dati standard utilizzando typedef. Il formato generale della voce per la creazione di un tipo definito dall'utente:

   typedef type new_name;

Qui, type significa qualsiasi tipo di dati accettabile, mentre new_name è un nuovo nome del tipo. Un nuovo nome viene impostato soltanto in aggiunta (non in sostituzione) a un nome di tipo esistente. MQL5 permette di creare puntatori a funzioni utilizzando typedef.

Puntatore alla funzione

Un puntatore a una funzione è generalmente definito nel seguente formato

   typedef tipo_risultato_funzione*Nome_tipo_funzione)(lista_di_tipi_di_parametri_di_input);

dove dopo typedef, viene impostata la firma di funzione (numero e tipo dei parametri di input, così come un tipo di risultato restituito dalla funzione). Ecco un semplice esempio di creazione ed applicazione di un puntatore ad una funzione:

//--- dichiarare un puntatore a una funzione che accetta due parametri int
   typedef int (*TFunc)(int,int);
//--- TFunc è un tipo, ed è possibile dichiarare la variabile puntatore alla funzione
   TFunc func_ptr; // puntatore alla funzione
//--- dichiara le funzioni corrispondenti alla descrizione TFunc
   int sub(int x,int y) { return(x-y); }  // sottrae un numero da un altro
   int add(int x,int y) { return(x+y); }  // aggiunta di due numeri
   int neg(int x)       { return(~x);  }  // inverte i bit nella variabile
//--- la variabile func_ptr può memorizzare l'indirizzo della funzione da dichiarare successivamente
   func_ptr=sub;
   Print(func_ptr(10,5));
   func_ptr=add;
   Print(func_ptr(10,5));
   func_ptr=neg;           // errore: neg non ha il tipo int (int,int)
   Print(func_ptr(10));    // errore: mancano due parametri

In questo esempio, la variabile func_ptr può ricevere le funzioni sub e add poiché hanno due input ciascuno di tipo int definito nel puntatore alla funzione TFunc. Al contrario, la funzione neg non può essere assegnata al puntatore func_ptr giacchè dalla sua firma è diversa.

Organizzazione di modelli di eventi nell'interfaccia utente

Puntatori a funzioni permettono di creare facilmente l'elaborazione di eventi durante la creazione di un'interfaccia utente. Facciamo un esempio, dalla sezione CButton per mostrare come creare bottoni ed aggiungere le funzioni per il loro handling. In primo luogo, definire un puntatore alla funzione TAction da chiamare premendo il bottone e creare tre funzioni secondo la descrizione TAction.

//--- crea un tipo di funzione personalizzata
typedef int(*TAction)(string,int);
//+------------------------------------------------------------------+
//|  Apre il file                                                  |
//+------------------------------------------------------------------+
int Open(string name,int id)
  {
   PrintFormat("%s funzione chiamata (nome=%s id=%d)",__FUNCTION__,name,id);
   return(1);
  }
//+------------------------------------------------------------------+
//|  Salva il file                                                  |
//+------------------------------------------------------------------+
int Save(string name,int id)
  {
   PrintFormat("%s funzione chiamata (nome=%s id=%d)",__FUNCTION__,name,id);
   return(2);
  }
//+------------------------------------------------------------------+
//|  Chiude il file                                                   |
//+------------------------------------------------------------------+
int Close(string name,int id)
  {
   PrintFormat("%s funzione chiamata (nome=%s id=%d)",__FUNCTION__,name,id);
   return(3);
  }
 

Quindi, crea la classe MyButton da CButton, dove dovremmo aggiungere il puntatore alla funzione TAction.

//+------------------------------------------------------------------+
//| Creare la classe bottone con la funzione di elaborazione eventi |
//+------------------------------------------------------------------+
class MyButton: public CButton
  {
private:
   TAction           m_action;                    // chart events handler
public:
                     MyButton(void){}
                    ~MyButton(void){}
   //--- costruttore specificando il testo del bottone e il puntatore all' event handler della funzione 
                     MyButton(string text, TAction act)
     {
      Text(text);
      m_action=act;
     }
   //--- imposta la funzione personalizzata chiamata dall'event handler OnEvent()
   void              SetAction(TAction act){m_action=act;}
   //--- standard chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) override
     {      
      if(m_action!=NULL && lparam==Id())
        { 
         //--- chiama l'handler custom m_action() 
         m_action(sparam,(int)lparam);
         return(true);
        }
      else
      //--- restituisce la chiamate dell'handler dalla classe genitore CButton 
         return(CButton::OnEvent(id,lparam,dparam,sparam));
     }
  };

Crea la classe derivata CControlsDialog daCAppDialog, aggiunge l'array m_buttons ad essal per memorizzare i pulsanti tel tipo MyButton, nonché i metodi AddButton(MyButton &button) e CreateButtons() .

//+------------------------------------------------------------------+
//| CControlsDialog class                                            |
//| Obiettivo: pannello grafico per la gestione dell'applicazione    |
//+------------------------------------------------------------------+
class CControlsDialog : public CAppDialog
  {
private:
   CArrayObj         m_buttons;                     // array del bottone
public:
                     CControlsDialog(void){};
                    ~CControlsDialog(void){};
   //--- crea
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2) override;
   //--- aggiunge il bottone
   bool              AddButton(MyButton &button){return(m_buttons.Add(GetPointer(button)));m_buttons.Sort();};
protected:
   //--- crea i bottoni 
   bool              CreateButtons(void);
  };
//+------------------------------------------------------------------+
//| Crea l'oggetto CControlsDialog sul chart                         |
//+------------------------------------------------------------------+
bool CControlsDialog::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
   if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
   return(CreateButtons());
//---
  }
//+------------------------------------------------------------------+
//| i defines                                                        |
//+------------------------------------------------------------------+
//--- indents e gaps
#define INDENT_LEFT                         (11)      // indentazione da sinistra (con permissività per lo spessore del bordo)
#define INDENT_TOP                          (11)      // indentazione dall'alto(con permissività per lo spessore del bordo)
#define CONTROLS_GAP_X                      (5)       // gap da coordinata X
#define CONTROLS_GAP_Y                      (5)       // gap da coordinata Y
//--- per i bottoni
#define BUTTON_WIDTH                        (100)     // grandezza da coordinate X
#define BUTTON_HEIGHT                       (20)      // grandezza da coordinata Y
//--- for the indication area
#define EDIT_HEIGHT                         (20)      // grandezza da coordinata Y
//+------------------------------------------------------------------+
//| Creare ed aggiunge bottoni al pannello CControlsDialog           |
//+------------------------------------------------------------------+
bool CControlsDialog::CreateButtons(void)
  {
//--- calcula le coordinate dei buttoni
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y);
   int x2;
   int y2=y1+BUTTON_HEIGHT;
//--- aggiunge oggetti bottoni con puntatori a funzioni
   AddButton(new MyButton("Open",Open));
   AddButton(new MyButton("Save",Save));
   AddButton(new MyButton("Close",Close));
//--- creare i bottoni graficamente
   for(int i=0;i<m_buttons.Total();i++)
     {
      MyButton *b=(MyButton*)m_buttons.At(i);
      x1=INDENT_LEFT+i*(BUTTON_WIDTH+CONTROLS_GAP_X);
      x2=x1+BUTTON_WIDTH;
      if(!b.Create(m_chart_id,m_name+"bt"+b.Text(),m_subwin,x1,y1,x2,y2))
        {
         PrintFormat("Fallimento nel creare il bottone %s %d",b.Text(),i);
         return(false);
        }
      //--- aggiungere ogni bottone al contenitore CControlsDialog
      if(!Add(b))
         return(false);
     }
//--- successo
   return(true);
  }

Ora, siamo in grado di sviluppare il programma utilizzando il pannello di controllo CControlsDialog avente 3 pulsanti: Apri, Salva e Chiudi. Quando si fa clic su un bottone, la funzione appropriata nella forma di puntatore TAction viene chiamata.

//--- dichiara l'oggetto a livello globale per creare automaticamente quando si lancia il programma
CControlsDialog MyDialog;
//+------------------------------------------------------------------+
//| Funzione di inizializzazione Expert                              |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- ora, crea l'oggetto sul chart
   if(!MyDialog.Create(0,"Controls",0,40,40,380,344))
      return(INIT_FAILED);
//--- avvia l'applicazione
   MyDialog.Run();
//--- applicazione inizializzata con successo
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Funzione deinizializzazione Expert                               |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- distrugge la finestra
   MyDialog.Destroy(reason);
  }
//+------------------------------------------------------------------+
//| Funzione di evento chart dell' Expert                            |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // ID evento  
                  const long& lparam,   // parametro evento del tipo long
                  const double& dparam, // parametro event del tipo double
                  const string& sparam) // paraemetro evento del tipo string
  {
//--- chiama l'handler della classe padre (qui è CAppDialog) per gli eventi chart
   MyDialog.ChartEvent(id,lparam,dparam,sparam);
  }

L'apparenza dell'applicazione lanciata e il click dei bottoni viene fornita sulla schermata.

panel_buttons

 

Il codice sorgente completo del programma

//+------------------------------------------------------------------+
//|                                                Panel_Buttons.mq5 |
//|                        Copyright 2017, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
 
#property copyright "Copyright 2017, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property description "Il pannello con vari bottoni CButton"
#include <Controls\Dialog.mqh>
#include <Controls\Button.mqh>
//+------------------------------------------------------------------+
//| i defines                                                        |
//+------------------------------------------------------------------+
//--- indents e gaps
#define INDENT_LEFT                         (11)      // indentazione da sinistra (con permissività per lo spessore del bordo)
#define INDENT_TOP                          (11)      // indentazione dall'alto(con permissività per lo spessore del bordo)
#define CONTROLS_GAP_X                      (5)       // gap da coordinata X
#define CONTROLS_GAP_Y                      (5)       // gap da coordinata Y
//--- per i bottoni
#define BUTTON_WIDTH                        (100)     // grandezza da coordinate X
#define BUTTON_HEIGHT                       (20)      // grandezza da coordinata Y
//--- for the indication area
#define EDIT_HEIGHT                         (20)      // grandezza da coordinata Y
 
//--- crea il tipo della funzione custom
typedef int(*TAction)(string,int);
//+------------------------------------------------------------------+
//|  Apre il file                                                    |
//+------------------------------------------------------------------+
int Open(string name,int id)
  {
   PrintFormat("%s funzione chiamata (nome=%s id=%d)",__FUNCTION__,name,id);
   return(1);
  }
//+------------------------------------------------------------------+
//|  Salva il file                                                   |
//+------------------------------------------------------------------+
int Save(string name,int id)
  {
   PrintFormat("%s funzione chiamata (nome=%s id=%d)",__FUNCTION__,name,id);
   return(2);
  }
//+------------------------------------------------------------------+
//|  Chiude il file                                                  |
//+------------------------------------------------------------------+
int Close(string name,int id)
  {
   PrintFormat("%s funzione chiamata (nome=%s id=%d)",__FUNCTION__,name,id);
   return(3);
  }
//+------------------------------------------------------------------+
//| Creare la classe bottone con la funzione di elaborazione eventi  |
//+------------------------------------------------------------------+
class MyButton: public CButton
  {
private:
   TAction           m_action;                    // chart events handler
public:
                     MyButton(void){}
                    ~MyButton(void){}
   //--- costruttore specificando il testo del bottone e il puntatore all' event handler della funzione 
                     MyButton(string text,TAction act)
     {
      Text(text);
      m_action=act;
     }
   //--- imposta la funzione personalizzata chiamata dall'event handler OnEvent()
   void              SetAction(TAction act){m_action=act;}
   //--- standard chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) override
     {
      if(m_action!=NULL && lparam==Id())
        {
         //--- chiama l'handler custom
         m_action(sparam,(int)lparam);
         return(true);
        }
      else
      //--- restituisce la chiamate dell'handler dalla classe genitore CButton 
         return(CButton::OnEvent(id,lparam,dparam,sparam));
     }
  };
//+------------------------------------------------------------------+
//| CControlsDialog class                                            |
//| Obiettivo: pannello grafico per la gestione dell'applicazione    |
//+------------------------------------------------------------------+
class CControlsDialog : public CAppDialog
  {
private:
   CArrayObj         m_buttons;                     // array del bottone
public:
                     CControlsDialog(void){};
                    ~CControlsDialog(void){};
   //--- crea
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2) override;
   //--- aggiunge il bottone
   bool              AddButton(MyButton &button){return(m_buttons.Add(GetPointer(button)));m_buttons.Sort();};
protected:
   //--- crea i bottoni 
   bool              CreateButtons(void);
  };
//+------------------------------------------------------------------+
//| Crea l'oggetto CControlsDialog sul chart                         |
//+------------------------------------------------------------------+
bool CControlsDialog::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
   if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
   return(CreateButtons());
//---
  }
//+------------------------------------------------------------------+
//| Creare ed aggiunge bottoni al pannello CControlsDialog           |
//+------------------------------------------------------------------+
bool CControlsDialog::CreateButtons(void)
  {
//--- calcula le coordinate dei buttoni
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y);
   int x2;
   int y2=y1+BUTTON_HEIGHT;
//--- aggiunge oggetti bottoni con puntatori a funzioni
   AddButton(new MyButton("Open",Open));
   AddButton(new MyButton("Save",Save));
   AddButton(new MyButton("Close",Close));
//--- creare i bottoni graficamente
   for(int i=0;i<m_buttons.Total();i++)
     {
      MyButton *b=(MyButton*)m_buttons.At(i);
      x1=INDENT_LEFT+i*(BUTTON_WIDTH+CONTROLS_GAP_X);
      x2=x1+BUTTON_WIDTH;
      if(!b.Create(m_chart_id,m_name+"bt"+b.Text(),m_subwin,x1,y1,x2,y2))
        {
         PrintFormat("Fallimento nel creare il bottone %s %d",b.Text(),i);
         return(false);
        }
      //--- aggiungere ogni bottone al contenitore CControlsDialog
      if(!Add(b))
         return(false);
     }
//--- successo
   return(true);
  }
//--- dichiara l'oggetto a livello globale per creare automaticamente quando si lancia il programma
CControlsDialog MyDialog;
//+------------------------------------------------------------------+
//| Funzione di inizializzazione Expert                              |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- ora, crea l'oggetto sul chart
   if(!MyDialog.Create(0,"Controls",0,40,40,380,344))
      return(INIT_FAILED);
//--- avvia l'applicazione
   MyDialog.Run();
//--- applicazione inizializzata con successo
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Funzione deinizializzazione Expert                               |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- distrugge la finestra
   MyDialog.Destroy(reason);
  }
//+------------------------------------------------------------------+
//| Funzione di evento chart dell' Expert                            |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // ID evento  
                  const long& lparam,   // parametro evento del tipo long
                  const double& dparam, // parametro event del tipo double
                  const string& sparam) // paraemetro evento del tipo string
  {
//--- chiama l'handler della classe padre (qui è CAppDialog) per gli eventi chart
   MyDialog.ChartEvent(id,lparam,dparam,sparam);
  }

 

Guarda anche

variabili, Funzioni