Русский

Справочник MQL4 Основы языка Типы данных Пользовательские типы

Пользовательские типы

Ключевое слово typedef в языке C++ позволяет создавать пользовательские типы данных – для этого достаточно определить новое имя типа данных для уже существующего типа данных. При этом сам новый тип данных не создается, а лишь определяется новое имя для уже существующего типа. Благодаря использованию пользовательских типов  можно делать программы более гибкими: для этого иногда достаточно изменить typedef-инструкции с помощью макросов подстановки (#define). Использование пользовательских типов позволяет также улучшить читабельность кода, поскольку для стандартных типов данных с помощью typedef можно использовать собственные описательные имена. Общий формат записи инструкции для создания пользовательского типа:

   typedef тип новое_имя;

Здесь элемент тип означает любой допустимый тип данных, а элемент новое_имя – новое имя для этого типа. Важно отметить, что новое имя определяется только в качестве дополнения к существующему имени типа, а не для его замены. В языке MQL5 с помощью typedef можно создавать указатели на функции.

Указатель на функцию

Указатель на функцию в общем виде определятся форматом записи

   typedef тип_результата_функции (*Имя_типа_функции)(список_типов_входных параметров);

где после слова typedef задается сигнатура функции – количество и тип входных параметров, а также тип возвращаемого функцией результата. В качестве объяснения приведем простой пример создания и использования указателя на функцию:

//--- объявим указатель на функцию, которая принимает два параметра типа int
   typedef int (*TFunc)(int,int);
//--- TFunc является типом и мы можем объявить переменную-указатель на функцию
   TFunc func_ptr; // указатель на функцию
//--- объявим функции, которые соответствуют описанию TFunc
   int sub(int x,int y) { return(x-y); }  // вычитание одного числа из другого
   int add(int x,int y) { return(x+y); }  // сложение двух чисел
   int neg(int x)       { return(~x);  }  // инвертирование битов в переменной
//--- в переменную func_ptr можно сохранить адрес функции, чтобы в дальнейшем ее вызывать
   func_ptr=sub;
   Print(func_ptr(10,5));
   func_ptr=add;
   Print(func_ptr(10,5));
   func_ptr=neg;           // ошибка: neg не имеет тип  int (int,int)
   Print(func_ptr(10));    // ошибка: должно быть два параметра

В данном примере переменной func_ptr можно присвоить функции sub и add, поскольку они имеют по два входных параметра типа int, как это указано в определении указателя на функцию TFunc. А вот функция neg не может быть присвоена указателю func_ptr, так как ее сигнатура отличается.

Организации событийных моделей в пользовательском интерфейсе

С помощью указателей на функции удобно строить обработку событий при создании пользовательского интерфейса. Сначала определим указатель на функцию TAction, которая будет вызываться по нажатию кнопки, и создадим три функции в соответствии с описанием TAction.

//--- создадим пользовательский тип функции
typedef int(*TAction)(string,int);
//+------------------------------------------------------------------+
//|  Открывает файл                                                  |
//+------------------------------------------------------------------+
int Open(string name,int id)
  {
   PrintFormat("Вызвана функция %s (name=%s id=%d)",__FUNCTION__,name,id);
   return(1);
  }
//+------------------------------------------------------------------+
//|  Сохраняет файл                                                  |
//+------------------------------------------------------------------+
int Save(string name,int id)
  {
   PrintFormat("Вызвана функция %s (name=%s id=%d)",__FUNCTION__,name,id);
   return(2);
  }
//+------------------------------------------------------------------+
//|  Закрывает файл                                                  |
//+------------------------------------------------------------------+
int Close(string name,int id)
  {
   PrintFormat("Вызвана функция %s (name=%s id=%d)",__FUNCTION__,name,id);
   return(3);
  }
 

Затем произведем класс MyButton от CButton, в котором добавим член TAction, являющийся указателем на функцию.

//+------------------------------------------------------------------+
//| Создадим свой класс кнопки с функцией обработки событий          |
//+------------------------------------------------------------------+
class MyButton: public CButton
  {
private:
   TAction           m_action;                    // обработчик событий графика
public:
                     MyButton(void){}
                    ~MyButton(void){}
   //--- конструктор с указанием текста кнопки и указателя на функцию для обработки событий
                     MyButton(string text, TAction act)
     {
      Text(text);
      m_action=act;
     }
   //--- установка собственной функции, которая будет вызываться из обработчика событий OnEvent()
   void              SetAction(TAction act){m_action=act;}
   //--- стандартный обработчик событий графика
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) override
     {      
      if(m_action!=NULL & lparam==Id())
        { 
         //--- вызовем собственный обработчик m_action() 
         m_action(sparam,(int)lparam);
         return(true);
        }
      else
      //--- вернем  результат вызова обработчика из родительского класса CButton
         return(CButton::OnEvent(id,lparam,dparam,sparam));
     }
  };

Далее создадим производный класс CControlsDialog от CAppDialog, в котором добавим массив m_buttons для хранения кнопок типа MyButton,  а также методы AddButton(MyButton &button) и CreateButtons().

//+------------------------------------------------------------------+
//| Класс CControlsDialog                                            |
//| Назвначение: графическая панель для управления приложением       |
//+------------------------------------------------------------------+
class CControlsDialog : public CAppDialog
  {
private:
   CArrayObj         m_buttons;                     // массив кнопок
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;
   //--- добавление кнопки
   bool              AddButton(MyButton &button){return(m_buttons.Add(GetPointer(button)));m_buttons.Sort();};
protected:
   //--- создание кнопок 
   bool              CreateButtons(void);
  };
//+------------------------------------------------------------------+
//| Создание объекта CControlsDialog на графике                      |
//+------------------------------------------------------------------+
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
//+------------------------------------------------------------------+
//| Создание и добавление кнопок на панель CControlsDialog           |
//+------------------------------------------------------------------+
bool CControlsDialog::CreateButtons(void)
  {
//--- расчет координат кнопок
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y);
   int x2;
   int y2=y1+BUTTON_HEIGHT;
//--- добавим объекты кнопок вместе с указателями на функции
   AddButton(new MyButton("Open",Open));
   AddButton(new MyButton("Save",Save));
   AddButton(new MyButton("Close",Close));
//--- создадим кнопки графически
   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);
        }
      //--- добавим каждую кнопку в контейнер CControlsDialog
      if(!Add(b))
         return(false);
     }
//--- succeed
   return(true);
  }

Теперь мы можем написать программу с использованием панели управления CControlsDialog, в которой создается 3 кнопки "Open", "Save" и "Close". При нажатии на кнопку вызывается соответствующая ей функция, которая прописана в виде указателя на функцию TAction.

//--- объявим объект на глобальном уровне, чтобы создать его автоматически при запуске программы
CControlsDialog MyDialog;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- теперь создадим объект на графике
   if(!MyDialog.Create(0,"Controls",0,40,40,380,344))
      return(INIT_FAILED);
//--- запускаем приложение
   MyDialog.Run();
//--- успешная инициализация приложения
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- очистим комментарии при завершении работы приложения
   Comment(");
//--- 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
  {
//--- для событий графика вызываем обработчик из родительского класса (CAppDialog в данном случае)
   MyDialog.ChartEvent(id,lparam,dparam,sparam);
  }

Внешний вид запущенного приложения и результаты нажатия кнопок представлены на картинке.

panel_buttons

 

Полный исходный код программы

//+------------------------------------------------------------------+
//|                                                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 "Панель с несколькими кнопками 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
 
//--- создадим пользовательский тип функции
typedef int(*TAction)(string,int);
//+------------------------------------------------------------------+
//|  Открывает файл                                                  |
//+------------------------------------------------------------------+
int Open(string name,int id)
  {
   PrintFormat("Вызвана функция %s (name=%s id=%d)",__FUNCTION__,name,id);
   return(1);
  }
//+------------------------------------------------------------------+
//|  Сохраняет файл                                                  |
//+------------------------------------------------------------------+
int Save(string name,int id)
  {
   PrintFormat("Вызвана функция %s (name=%s id=%d)",__FUNCTION__,name,id);
   return(2);
  }
//+------------------------------------------------------------------+
//|  Закрывает файл                                                  |
//+------------------------------------------------------------------+
int Close(string name,int id)
  {
   PrintFormat("Вызвана функция %s (name=%s id=%d)",__FUNCTION__,name,id);
   return(3);
  }
//+------------------------------------------------------------------+
//| Создадим свой класс кнопки с функцией обработки событий          |
//+------------------------------------------------------------------+
class MyButton: public CButton
  {
private:
   TAction           m_action;                    // обработчик событий графика
public:
                     MyButton(void){}
                    ~MyButton(void){}
   //--- конструктор с указанием текста кнопки и указателя на функцию для обработки событий
                     MyButton(string text,TAction act)
     {
      Text(text);
      m_action=act;
     }
   //--- установка собственной функции, которая будет вызываться из обработчика событий OnEvent()
   void              SetAction(TAction act){m_action=act;}
   //--- стандартный обработчик событий графика
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) override
     {
      if(m_action!=NULL & lparam==Id())
        {
         //--- вызовем собственный обработчик
         m_action(sparam,(int)lparam);
         return(true);
        }
      else
      //--- вернем  результат вызова обработчика из родительского класса CButton
         return(CButton::OnEvent(id,lparam,dparam,sparam));
     }
  };
//+------------------------------------------------------------------+
//| Класс CControlsDialog                                            |
//| Назвначение: графическая панель для управления приложением       |
//+------------------------------------------------------------------+
class CControlsDialog : public CAppDialog
  {
private:
   CArrayObj         m_buttons;                     // массив кнопок
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;
   //--- добавление кнопки
   bool              AddButton(MyButton &button){return(m_buttons.Add(GetPointer(button)));m_buttons.Sort();};
protected:
   //--- создание кнопок 
   bool              CreateButtons(void);
  };
//+------------------------------------------------------------------+
//| Создание объекта CControlsDialog на графике                      |
//+------------------------------------------------------------------+
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());
//---
  }
//+------------------------------------------------------------------+
//| Создание и добавление кнопок на панель CControlsDialog           |
//+------------------------------------------------------------------+
bool CControlsDialog::CreateButtons(void)
  {
//--- расчет координат кнопок
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y);
   int x2;
   int y2=y1+BUTTON_HEIGHT;
//--- добавим объекты кнопок вместе с указателями на функции
   AddButton(new MyButton("Open",Open));
   AddButton(new MyButton("Save",Save));
   AddButton(new MyButton("Close",Close));
//--- создадим кнопки графически
   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);
        }
      //--- добавим каждую кнопку в контейнер CControlsDialog
      if(!Add(b))
         return(false);
     }
//--- succeed
   return(true);
  }
//--- объявим объект на глобальном уровне, чтобы создать его автоматически при запуске программы
CControlsDialog MyDialog;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- теперь создадим объект на графике
   if(!MyDialog.Create(0,"Controls",0,40,40,380,344))
      return(INIT_FAILED);
//--- запускаем приложение
   MyDialog.Run();
//--- успешная инициализация приложения
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- очистим комментарии при завершении работы приложения
   Comment(");
//--- 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
  {
//--- для событий графика вызываем обработчика из родительского класса (CAppDialog в данном случае)
   MyDialog.ChartEvent(id,lparam,dparam,sparam);
  }

 

Смотри также

Переменные, Функции