Benutzerdefinierte Typen

Das typedef Schlüsselwort in C++ ermöglicht die Erstellung benutzerdefinierter Typen. Dafür reicht es, einem bereits existierenden Datentyp einen neuen Namen zu geben. Dabei wird kein neuer Datentyp erstellt, es wird nur ein neuer Name einem bereits existierenden Typ gegeben. Dank der Anwendung benutzerdefinierter Typen kann man Programme flexibler machen: dafür reicht es manchmal, typedef-Anweisungen mithilfe von Makros (#define) zu modifizieren. Darüber hinaus erlaubt die Anwendung benutzerdefinierter Typen, die Lesbarkeit des Codes zu verbessern, weil man mithilfe von typedef eigene beschreibende Namen für Standarddatentypen verwenden kann. Das allgemeine Format einer Anweisung für die Erstellung eines benutzerdefinierten Typs:

   typedef Typ neuer_Name;

Das Element Typ bezeichnet hier jeden zulässigen Datentyp, und das Element neuer_Name – den neuen Namen für diesen Typ. Es ist wichtig zu betonen, dass der neue Name nur ein Zusatz zum bereits existierenden Namen eines Typs und kein Ersatz ist. In der MQL5 Programmiersprache können mithilfe von typedef Funktionspointer erstellt werden.

Funktionspointer

Ein Funktionspointer hat das folgende Format

   typedef Typ_des_Ergebnisses_der_Funktion (*Name_des_Funktionstyps)(Liste_der_Typen_der_Eingabeparameter);

wo nach dem Wort typedef die Signatur der Funktion gesetzt wird: die Anzahl und der Typ der Eingabeparameter sowie der Typ des Ergebnisses der Funktion. Führen wir ein einfaches Beispiel für die Erstellung und Verwendung eines Funktionspointers:

//--- deklarieren wir einen Pointer auf die Funktion, die zwei Parameter vom Typ int akzeptiert
   typedef int (*TFunc)(int,int);
//--- TFunc ist ein Typ, und wir können die Pointer-Variable deklarieren
   TFunc func_ptr; // Funktionspointer
//--- Deklarieren wir die Funktionen, die der TFunc Beschreibung entsprechen
   int sub(int x,int y) { return(x-y); }  // Subtraktion einer Zahl von der anderen
   int add(int x,int y) { return(x+y); }  // Addition von zwei Zahlen
   int neg(int x)       { return(~x);  }  // Invertieren von Bits in der Variablen
//--- in der func_ptr Variablen kann man die Funktionsadresse speichern, um diese später aufzurufen
   func_ptr=sub;
   Print(func_ptr(10,5));
   func_ptr=add;
   Print(func_ptr(10,5));
   func_ptr=neg;           // Fehler: neg hat nicht den Typ  int (int,int)
   Print(func_ptr(10));    // Fehler: es sind zwei Parameter erforderlich

In diesem Beispiel kann man der func_ptr Variablen die Funktionen sub und add zuweisen, denn sie habe je zwei Inputparameter vom Typ int, wie dies in der Definition des TFunc Funktionspointers angegeben ist. Die Funktion neg kann nicht dem Bezeichner func_ptr zugewiesen werden, denn ihre Signatur ist anders.

Gestaltung von Event-Modellen in der Benutzerschnittstelle

Mithilfe von Funktionspointern ist es einfach, die Verarbeitung von Ereignissen bei der Erstellung einer Benutzerschnittstell zu gestalten. Anhand eines Beispiels aus dem CButton Bereich veranschaulichen wir, wie man Buttons erstellen und ihnen Funktionen für die Verarbeitung von Klicks hinzufügen kann. Zuerst definieren wir den Pointer auf die Funktion TAction, die beim Klicken auf den Button aufgerufen wird, und erstellen wir drei Funktionen entsprechend der Beschreibung von TAction.

//--- erstellen wir einen benutzerdefinierten Funktionstyp
typedef int(*TAction)(string,int);
//+------------------------------------------------------------------+
//|  Öffnet die Datei                                                |
//+------------------------------------------------------------------+
int Open(string name,int id)
  {
   PrintFormat("Funktion %s (name=%s id=%d) aufgerufen",__FUNCTION__,name,id);
   return(1);
  }
//+------------------------------------------------------------------+
//|  Speichert die Datei                                             |
//+------------------------------------------------------------------+
int Save(string name,int id)
  {
   PrintFormat("Funktion %s (name=%s id=%d) aufgerufen",__FUNCTION__,name,id);
   return(2);
  }
//+------------------------------------------------------------------+
//|  Schließt die Datei                                              |
//+------------------------------------------------------------------+
int Close(string name,int id)
  {
   PrintFormat("Funktion %s (name=%s id=%d) aufgerufen",__FUNCTION__,name,id);
   return(3);
  }
 

Danach leiten wir die MyButton Klasse von der CButton Klasse ab, welcher wir TAction, den Funktionspointer, hinzufügen.

//+------------------------------------------------------------------+
//| Erstellen wir eine eigene Button-Klasse mit der Funktion         |
//+------------------------------------------------------------------+
class MyButton: public CButton
  {
private:
   TAction           m_action;                    // Handler von Chartereignissen
public:
                     MyButton(void){}
                    ~MyButton(void){}
   //--- Konstruktor mit der Angabe des Button-Textes und des Funktionspointers für die Verarbeitung von Events
                     MyButton(string text, TAction act)
     {
      Text(text);
      m_action=act;
     }
   //--- setzen wir die eigene Funktion, die aus dem OnEvent() Event Handler aufgerufen wird
   void              SetAction(TAction act){m_action=act;}
   //--- Standard Chart Events Handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) override
     {      
      if(m_action!=NULL && lparam==Id())
        { 
         //--- rufen wir den benutzerdefinierten m_action() Handler auf
         m_action(sparam,(int)lparam);
         return(true);
        }
      else
      //--- geben wir das Ergebnis des Aufrufs des Handlers aus der CButton Basisklasse zurück
         return(CButton::OnEvent(id,lparam,dparam,sparam));
     }
  };

Erstellen wir die von der CAppDialog Klasse abgeleitete CControlsDialog Klasse, welcher wir das m_buttons Array für das Speichern von Buttons vom Typ MyButton sowie die Methoden AddButton(MyButton &button) und CreateButtons() hinzufügen.

//+------------------------------------------------------------------+
//| CControlsDialog Klasse                                           |
//| Verwendungszweck: grafisches Panel für die Steuerung             |
//+------------------------------------------------------------------+
class CControlsDialog : public CAppDialog
  {
private:
   CArrayObj         m_buttons;                     // Array für Tasten
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;
   //--- Button hinzufügen
   bool              AddButton(MyButton &button){return(m_buttons.Add(GetPointer(button)));m_buttons.Sort();};
protected:
   //--- Buttons erstellen 
   bool              CreateButtons(void);
  };
//+------------------------------------------------------------------+
//| Erstellung des ControlsDialog Objekts auf dem 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());
//---
  }
//+------------------------------------------------------------------+
//| 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
//+------------------------------------------------------------------+
//| Erstellung und Hinzufügung von Buttons auf das CControlsDialog Panel     |
//+------------------------------------------------------------------+
bool CControlsDialog::CreateButtons(void)
  {
//--- Koordinaten der Buttons berechnen
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y);
   int x2;
   int y2=y1+BUTTON_HEIGHT;
//--- Button-Objekte gemeinsam mit Funktionspointern hinzufügen
   AddButton(new MyButton("Open",Open));
   AddButton(new MyButton("Save",Save));
   AddButton(new MyButton("Close",Close));
//--- erstellen wir Buttons grafisch
   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);
        }
      //--- fügen wir jeden Button in den Container CControlsDialog hinzu
      if(!Add(b))
         return(false);
     }
//--- succeed
   return(true);
  }

Nun können wir ein Programm unter Verwendung des CControlsDialog Panels schreiben, in welchem 3 Buttons "Open", "Save" und "Close" erstellt werden. Beim Klicken auf einen Button wird die entsprechende Funktion aufgerufen, welche als TAction Funktionspointer vorgegeben ist.

//--- deklarieren wir dieses Objekt an der globalen Ebene, um es automatisch beim Starten des Programms zu erstellen
CControlsDialog MyDialog;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- nun erstellen wir das Objekt auf dem Chart
   if(!MyDialog.Create(0,"Controls",0,40,40,380,344))
      return(INIT_FAILED);
//--- Applikation starten
   MyDialog.Run();
//--- erfolgreiche Initialisierung der Applikation
   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
  {
//--- für Chartereignisse rufen wir den Event Handler aus der Basislklasse auf (in diesem Fall CAppDialog)
   MyDialog.ChartEvent(id,lparam,dparam,sparam);
  }

Das Design der gestarteten Applikation und die Ergebnisse der Klicks auf die Buttons sind auf dem Bild unten zu sehen.

panel_buttons

 

Vollständiger Quellcode des Programms

//+------------------------------------------------------------------+
//|                                                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 mit mehreren CButton Buttons"
#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
 
//--- erstellen wir einen benutzerdefinierten Funktionstyp
typedef int(*TAction)(string,int);
//+------------------------------------------------------------------+
//|  Öffnet die Datei                                                |
//+------------------------------------------------------------------+
int Open(string name,int id)
  {
   PrintFormat("Funktion %s (name=%s id=%d) aufgerufen",__FUNCTION__,name,id);
   return(1);
  }
//+------------------------------------------------------------------+
//|  Speichert die Datei                                             |
//+------------------------------------------------------------------+
int Save(string name,int id)
  {
   PrintFormat("Funktion %s (name=%s id=%d) aufgerufen",__FUNCTION__,name,id);
   return(2);
  }
//+------------------------------------------------------------------+
//|  Schließt die Datei                                              |
//+------------------------------------------------------------------+
int Close(string name,int id)
  {
   PrintFormat("Funktion %s (name=%s id=%d) aufgerufen",__FUNCTION__,name,id);
   return(3);
  }
//+------------------------------------------------------------------+
//| Erstellen wir eine eigene Button-Klasse mit der Funktion der Verarbeitung von Events 
//+------------------------------------------------------------------+
class MyButton: public CButton
  {
private:
   TAction           m_action;                    // Handler von Chartereignissen
public:
                     MyButton(void){}
                    ~MyButton(void){}
   //--- Konstruktor mit der Angabe des Button-Textes und des Funktionspointers für die Verarbeitung von Events
                     MyButton(string text,TAction act)
     {
      Text(text);
      m_action=act;
     }
   //--- setzen wir die eigene Funktion, die aus dem OnEvent() Event Handler aufgerufen wird
   void              SetAction(TAction act){m_action=act;}
   //--- Standard Chart Events Handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) override
     {
      if(m_action!=NULL && lparam==Id())
        {
         //--- rufen wir den eigenen Handler auf
         m_action(sparam,(int)lparam);
         return(true);
        }
      else
      //--- geben wir das Ergebnis des Aufrufs des Handlers aus der CButton Basisklasse zurück
         return(CButton::OnEvent(id,lparam,dparam,sparam));
     }
  };
//+------------------------------------------------------------------+
//| CControlsDialog Klasse                                           |
//| Verwendungszweck: grafisches Panel für die Steuerung             |
//+------------------------------------------------------------------+
class CControlsDialog : public CAppDialog
  {
private:
   CArrayObj         m_buttons;                     // Array für Tasten
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;
   //--- Button hinzufügen
   bool              AddButton(MyButton &button){return(m_buttons.Add(GetPointer(button)));m_buttons.Sort();};
protected:
   //--- Buttons erstellen 
   bool              CreateButtons(void);
  };
//+------------------------------------------------------------------+
//| Erstellung des ControlsDialog Objekts auf dem 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());
//---
  }
//+------------------------------------------------------------------+
//| Erstellung und Hinzufügung von Buttons auf das CControlsDialog   |
//+------------------------------------------------------------------+
bool CControlsDialog::CreateButtons(void)
  {
//--- Koordinaten der Buttons berechnen
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y);
   int x2;
   int y2=y1+BUTTON_HEIGHT;
//--- Button-Objekte gemeinsam mit Funktionspointern hinzufügen
   AddButton(new MyButton("Open",Open));
   AddButton(new MyButton("Save",Save));
   AddButton(new MyButton("Close",Close));
//--- erstellen wir Buttons grafisch
   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);
        }
      //--- fügen wir jeden Button in den Container CControlsDialog hinzu
      if(!Add(b))
         return(false);
     }
//--- succeed
   return(true);
  }
//--- deklarieren wir dieses Objekt an der globalen Ebene, um es automatisch beim Starten des Programms zu erstellen
CControlsDialog MyDialog;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- nun erstellen wir das Objekt auf dem Chart
   if(!MyDialog.Create(0,"Controls",0,40,40,380,344))
      return(INIT_FAILED);
//--- Applikation starten
   MyDialog.Run();
//--- erfolgreiche Initialisierung der Applikation
   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
  {
//--- für Chartereignisse rufen wir den Event Handler aus der Basislklasse auf (in diesem Fall CAppDialog)
   MyDialog.ChartEvent(id,lparam,dparam,sparam);
  }

 

Siehe auch

Variablen, Funktionen