Tipos personalizados

La palabra clave typedef en el lenguaje C++ permite crear tipos de datos personalizados, para ello, basta con definir el nuevo nombre del tipo de datos para un tipo de datos ya existente. Además, en este caso, el propio tipo de datos no se crea: solo se define el nuevo nombre para un tipo de datos ya existente. Gracias al uso de tipos personalizados  es posible hacer los programas más flexibles: para ello, a veces basta con cambiar las instrucciones typedef con la ayuda de macro-sustituciones (#define). El uso de tipos personalizados permite mejorar la legibilidad del código, ya que para los tipos de datos estándar, con la ayuda de typedef se pueden usar nombres descriptivos propios. El formato general de registro de instrucciones para crear un tipo personalizado es el siguiente:

   typedef tipo nuevo_nombre;

Aquí el elemento tipo designa cualquier tipo de datos permisible, y el elemento nuevo_nombre, el nuevo nombre para este tipo. Es importante destacar que el nuevo nombre se define solo como adición a un nombre de tipo ya existente, pero no lo sustituye. En el lenguaje MQL5, con la ayuda de typedef, se pueden crear punteros a una función.

Puntero a una función

En general, el puntero a una función se define con el formato de registro

   typedef tipo_de_resultado_de_la_función (*Nombre_del_tipo_de_función)(lista_de_tipos_de_los_parámetros_de_entrada);

donde, después de la palabra typedef se establece la signatura de la función, es decir, el número y el tipo de parámetros de entrada, así como el tipo de resultado retornado por la función. Como explicación, mostraremos un sencillo ejemplo de creación y uso de un puntero a una función:

//--- declaramos el puntero a una función, que usa dos parámetros del tipo int
   typedef int (*TFunc)(int,int);
//--- TFunc es el tipo, podemos declarar la variable-puntero a una función
   TFunc func_ptr; // puntero a una función
//--- declaramos las funciones que corresponden a la descripción de TFunc
   int sub(int x,int y) { return(x-y); }  // resta de una cifra a la otra
   int add(int x,int y) { return(x+y); }  // suma de una cifra a la otra
   int neg(int x)       { return(~x);  }  // invertir bits en la variable
//--- en la variable func_ptr se puede guardar la dirección de la función, para llamarla en lo sucesivo
   func_ptr=sub;
   Print(func_ptr(10,5));
   func_ptr=add;
   Print(func_ptr(10,5));
   func_ptr=neg;           // error: neg no tiene el tipo  int (int,int)
   Print(func_ptr(10));    // error: debe haber dos parámetros

En este ejemplo, a la variable func_ptr se le pueden asignar las funciones sub y add, puesto que cada una de ellas tiene dos parámetros int, como se indica en la definición del puntero a la función TFunc. A su vez, la función neg no puede ser asignada al puntero func_ptr, puesto que su signatura se diferencia.

Organización de los modelos de evento en la interfaz de usuario

Con la ayuda de los punteros a funciones es sencillo construir el procesamiento de eventos a la hora de crear una interfaz de usuario. Usando como ejemplo el apartado CButton, mostraremos cómo crear botones y añadirles funciones para procesar la pulsación. Primero tenemos que definir el puntero a la función TAction, que se llamará al pulsar un botón, y después crear tres funciones de acuerdo con la descripción de TAction.

//--- creamos el tipo de función personalizado
typedef int(*TAction)(string,int);
//+------------------------------------------------------------------+
//|  Abrir archivo                                                  |
//+------------------------------------------------------------------+
int Open(string name,int id)
  {
   PrintFormat("Se ha llamado la función %s (name=%s id=%d)",__FUNCTION__,name,id);
   return(1);
  }
//+------------------------------------------------------------------+
//|  Guardar archivo                                                  |
//+------------------------------------------------------------------+
int Save(string name,int id)
  {
   PrintFormat("Se ha llamado la función %s (name=%s id=%d)",__FUNCTION__,name,id);
   return(2);
  }
//+------------------------------------------------------------------+
//|  Cerrar archivo                                                  |
//+------------------------------------------------------------------+
int Close(string name,int id)
  {
   PrintFormat("Se ha llamado la función %s (name=%s id=%d)",__FUNCTION__,name,id);
   return(3);
  }
 

Después reproduciomos la clase MyButton de CButton, en la que añadimos el miembro TAction, que es el puntero a la función.

//+------------------------------------------------------------------+
//| Creando nuestra propia clase de botón con la función de procesamiento de eventos          |
//+------------------------------------------------------------------+
class MyButton: public CButton
  {
private:
   TAction           m_action;                    // procesador de eventos del gráfico
public:
                     MyButton(void){}
                    ~MyButton(void){}
   //--- constructor con indicación de texto del botón y el puntero a la función para procesar eventos
                     MyButton(string text, TAction act)
     {
      Text(text);
      m_action=act;
     }
   //--- instalando la función propia, que se llamará desde el procesador de eventos OnEvent()
   void              SetAction(TAction act){m_action=act;}
   //--- procesador estándar de eventos del gráfico
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) override
     {      
      if(m_action!=NULL && lparam==Id())
        { 
         //--- llamamos el procesador propio m_action() 
         m_action(sparam,(int)lparam);
         return(true);
        }
      else
      //--- retornamos el  resultado de la llamada del procesador desde la clase padre CButton
         return(CButton::OnEvent(id,lparam,dparam,sparam));
     }
  };

A continuación, creamos la clase derivada CControlsDialog de CAppDialog, en la que añadimos la matriz m_buttons para guardar los botones del tipo MyButton,  así como los métodos AddButton(MyButton &button) y CreateButtons().

//+------------------------------------------------------------------+
//| Clase CControlsDialog                                            |
//| Cometido: panel gráfico para controlar la aplicación       |
//+------------------------------------------------------------------+
class CControlsDialog : public CAppDialog
  {
private:
   CArrayObj         m_buttons;                     // matriz de botones
public:
                     CControlsDialog(void){};
                    ~CControlsDialog(void){};
   //--- create
   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;
   //--- añadir botón
   bool              AddButton(MyButton &button){return(m_buttons.Add(GetPointer(button)));m_buttons.Sort();};
protected:
   //--- crear botón 
   bool              CreateButtons(void);
  };
//+------------------------------------------------------------------+
//| Crear objeto CControlsDialog en el gráfico                      |
//+------------------------------------------------------------------+
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());
//---
  }
//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
//--- indents and gaps
#define INDENT_LEFT                         (11)      // indent from left (with allowance for border width)
#define INDENT_TOP                          (11)      // indent from top (with allowance for border width)
#define CONTROLS_GAP_X                      (5)       // gap by X coordinate
#define CONTROLS_GAP_Y                      (5)       // gap by Y coordinate
//--- for buttons
#define BUTTON_WIDTH                        (100)     // size by X coordinate
#define BUTTON_HEIGHT                       (20)      // size by Y coordinate
//--- for the indication area
#define EDIT_HEIGHT                         (20)      // size by Y coordinate
//+------------------------------------------------------------------+
//| Crear y añadir botones en el panel CControlsDialog           |
//+------------------------------------------------------------------+
bool CControlsDialog::CreateButtons(void)
  {
//--- calculando las coordenadas de los botones
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y);
   int x2;
   int y2=y1+BUTTON_HEIGHT;
//--- añadimos los objetos de los botones junto con los punteros a las funciones
   AddButton(new MyButton("Open",Open));
   AddButton(new MyButton("Save",Save));
   AddButton(new MyButton("Close",Close));
//--- creamos los botones gráficamente
   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("Failed to create button %s %d",b.Text(),i);
         return(false);
        }
      //--- añadimos cada botón al contenedor CControlsDialog
      if(!Add(b))
         return(false);
     }
//--- succeed
   return(true);
  }

Ahora podemos escribir un programa con el uso del panel de control CControlsDialog, en el que se crean 3 botones "Open", "Save" y "Close". Al pulsar un botón, se llama la función que le corresponde, escrita en forma de puntero a la función TAction.

//--- declaramos el objeto a nivel global, para crearlo de forma automática al iniciar el programa
CControlsDialog MyDialog;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- ahora creamos el objeto en el gráfico
   if(!MyDialog.Create(0,"Controls",0,40,40,380,344))
      return(INIT_FAILED);
//--- iniciamos la aplicación
   MyDialog.Run();
//--- la aplicación ha sido iniciada con éxito
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy dialog
   MyDialog.Destroy(reason);
  }
//+------------------------------------------------------------------+
//| Expert chart event function                                      |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // event ID  
                  const long& lparam,   // event parameter of the long type
                  const double& dparam, // event parameter of the double type
                  const string& sparam) // event parameter of the string type
  {
//--- para los eventos del gráfico llamamos el procesador desde la clase padre (CAppDialog, en este caso)
   MyDialog.ChartEvent(id,lparam,dparam,sparam);
  }

En la imagen se muestra el aspecto externo de la aplicación iniciada, así como los resultados de la pulsación de los botones.

panel_buttons

 

Código fuente completo del programa

//+------------------------------------------------------------------+
//|                                                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 "Panel con varios botones CButton"
#include <Controls\Dialog.mqh>
#include <Controls\Button.mqh>
//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
//--- indents and gaps
#define INDENT_LEFT                         (11)      // indent from left (with allowance for border width)
#define INDENT_TOP                          (11)      // indent from top (with allowance for border width)
#define CONTROLS_GAP_X                      (5)       // gap by X coordinate
#define CONTROLS_GAP_Y                      (5)       // gap by Y coordinate
//--- for buttons
#define BUTTON_WIDTH                        (100)     // size by X coordinate
#define BUTTON_HEIGHT                       (20)      // size by Y coordinate
//--- for the indication area
#define EDIT_HEIGHT                         (20)      // size by Y coordinate
 
//--- creando el tipo personalizado de función
typedef int(*TAction)(string,int);
//+------------------------------------------------------------------+
//|  Abrir archivo                                                  |
//+------------------------------------------------------------------+
int Open(string name,int id)
  {
   PrintFormat("Se ha llamado la función %s (name=%s id=%d)",__FUNCTION__,name,id);
   return(1);
  }
//+------------------------------------------------------------------+
//|  Guardar archivo                                                  |
//+------------------------------------------------------------------+
int Save(string name,int id)
  {
   PrintFormat("Se ha llamado la función %s (name=%s id=%d)",__FUNCTION__,name,id);
   return(2);
  }
//+------------------------------------------------------------------+
//|  Cerrar archivo                                                  |
//+------------------------------------------------------------------+
int Close(string name,int id)
  {
   PrintFormat("Se ha llamado la función %s (name=%s id=%d)",__FUNCTION__,name,id);
   return(3);
  }
//+------------------------------------------------------------------+
//| Creando nuestra propia clase de botón con la función de procesamiento de eventos          |
//+------------------------------------------------------------------+
class MyButton: public CButton
  {
private:
   TAction           m_action;                    // procesador de eventos del gráfico
public:
                     MyButton(void){}
                    ~MyButton(void){}
   //--- constructor con indicación de texto del botón y el puntero a la función para procesar eventos
                     MyButton(string text,TAction act)
     {
      Text(text);
      m_action=act;
     }
   //--- instalando la función propia, que se llamará desde el procesador de eventos OnEvent()
   void              SetAction(TAction act){m_action=act;}
   //--- procesador estándar de eventos del gráfico
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) override
     {
      if(m_action!=NULL && lparam==Id())
        {
         //--- llamamos el procesador propio
         m_action(sparam,(int)lparam);
         return(true);
        }
      else
      //--- retornamos el  resultado de la llamada del procesador desde la clase padre CButton
         return(CButton::OnEvent(id,lparam,dparam,sparam));
     }
  };
//+------------------------------------------------------------------+
//| Clase CControlsDialog                                            |
//| Cometido: panel gráfico para controlar la aplicación       |
//+------------------------------------------------------------------+
class CControlsDialog : public CAppDialog
  {
private:
   CArrayObj         m_buttons;                     // matriz de botones
public:
                     CControlsDialog(void){};
                    ~CControlsDialog(void){};
   //--- create
   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;
   //--- añadir botón
   bool              AddButton(MyButton &button){return(m_buttons.Add(GetPointer(button)));m_buttons.Sort();};
protected:
   //--- crear botón 
   bool              CreateButtons(void);
  };
//+------------------------------------------------------------------+
//| Crear objeto CControlsDialog en el gráfico                      |
//+------------------------------------------------------------------+
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());
//---
  }
//+------------------------------------------------------------------+
//| Crear y añadir botones en el panel CControlsDialog           |
//+------------------------------------------------------------------+
bool CControlsDialog::CreateButtons(void)
  {
//--- calculando las coordenadas de los botones
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y);
   int x2;
   int y2=y1+BUTTON_HEIGHT;
//--- añadimos los objetos de los botones junto con los punteros a las funciones
   AddButton(new MyButton("Open",Open));
   AddButton(new MyButton("Save",Save));
   AddButton(new MyButton("Close",Close));
//--- creamos los botones gráficamente
   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("Failed to create button %s %d",b.Text(),i);
         return(false);
        }
      //--- añadimos cada botón al contenedor CControlsDialog
      if(!Add(b))
         return(false);
     }
//--- succeed
   return(true);
  }
//--- declaramos el objeto a nivel global, para crearlo de forma automática al iniciar el programa
CControlsDialog MyDialog;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- ahora creamos el objeto en el gráfico
   if(!MyDialog.Create(0,"Controls",0,40,40,380,344))
      return(INIT_FAILED);
//--- iniciamos la aplicación
   MyDialog.Run();
//--- la aplicación ha sido iniciada con éxito
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy dialog
   MyDialog.Destroy(reason);
  }
//+------------------------------------------------------------------+
//| Expert chart event function                                      |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // event ID  
                  const long& lparam,   // event parameter of the long type
                  const double& dparam, // event parameter of the double type
                  const string& sparam) // event parameter of the string type
  {
//--- para los eventos del gráfico llamamos el procesador desde la clase padre (CAppDialog, en este caso)
   MyDialog.ChartEvent(id,lparam,dparam,sparam);
  }

 

Vea también

Variables, funciones