Tipos personalizados
A palavra-chave typedef em linguagem C++ permite criar tipos personalizados de dados, basta definir o nome do tipo de dados para um tipo de dados existente. Ao fazer isto, não é criado um novo tipo de dados, mas sim é definido um novo nome para o tipo existente. Graças ao uso de tipos personalizados, você pode tornar o programa mais flexível, basta suficiente alterar as instruções typedef usando os macros de substituição (#define). Usar os tipos personalizados também pode melhorar a legibilidade do código, uma vez que, para os tipos de dados padrão, você pode usar seus próprios nomes descritivos com ajuda de typedef. Formato geral para escrever instruções a fim de criar um tipo personalizado:
typedef tipo novo_nome;
Aqui o elemento tipo representa qualquer tipo de dados válido, enquanto o elemento novo_nome é o nome novo para este tipo. É importante notar que o novo nome é definido apenas como um complemento para o nome existente do tipo e não para substituí-lo. Na linguagem MQL5, você pode criar um ponteiro para a função, usando typedef.
Ponteiro para a função
Ponteiro para a função geralmente é determinado pelo formato de registro
typedef tipo_de_resultado_de_função (*Nome_de_tipo_de_função)(lista_de_tipos_de_par metros_de_entrada);
onde, após a palavra typedef, é definida a assinatura da função, isto é, o número e tipo de parâmetros de entrada, bem como o tipo de resultado a ser retornado pela função. Aqui está uma explicação de como criar e usar um ponteiro para uma função:
// --- declaramos o ponteiro para uma função que aceita dois parâmetros do tipo int
typedef int (*TFunc)(int,int);
//--- TFunc é o tipo, e nós podemos declarar o ponteiro-variável para a função
TFunc func_ptr; // ponteiro para a função
//--- declaramos as funções que correspondem à descrição TFunc
int sub(int x,int y) { return(x-y); } // / subtração de um número a partir de outro
int add(int x,int y) { return(x+y); } // adição de dois números
int neg(int x) { return(~x); } // inversão de bits na variável
//--- você pode armazenas o endereço da função na variável func_ptr, para chamá-la no futuro
func_ptr=sub;
Print(func_ptr(10,5));
func_ptr=add;
Print(func_ptr(10,5));
func_ptr=neg; // erro: neg não tem o tipo int (int,int)
Print(func_ptr(10)); // erro: deve haver dois parâmetros
Neste exemplo, à variável func_ptr podem ser atribuídas as funções sub e add, uma vez que cada uma delas tem dois parâmetros de entrada do tipo int, conforme especificado na definição do ponteiro para a função TFunc. Aqui à função neg não pode ser atribuído o ponteiro func_ptr, uma vez que sua assinatura é diferente.
Organização dos modelos de eventos na interface personalizada
Usando os ponteiros para a função você pode facilmente construir a manipulação de eventos ao criar a interface personalizada. Mostraremos um exemplo a partir da seção CButton sobre como criar botões e adicionar neles uma função para processamento do carregamento do botão. Em primeiro lugar, definimos o ponteiro para a função TAction, ela será chamada pressionando um botão, e criaremos três funções em conformidade com a descrição TAction.
//--- criamos o tipo personalizado de função
typedef int(*TAction)(string,int);
//+------------------------------------------------------------------+
//| Abre o arquivo |
//+------------------------------------------------------------------+
int Open(string name,int id)
{
PrintFormat("Função chamada %s (name=%s id=%d)",__FUNCTION__,name,id);
return(1);
}
//+------------------------------------------------------------------+
//| Salva o arquivo |
//+------------------------------------------------------------------+
int Save(string name,int id)
{
PrintFormat("Função chamada %s (name=%s id=%d)",__FUNCTION__,name,id);
return(2);
}
//+------------------------------------------------------------------+
//| Fecha o arquivo |
//+------------------------------------------------------------------+
int Close(string name,int id)
{
PrintFormat("Função chamada %s (name=%s id=%d)",__FUNCTION__,name,id);
return(3);
}
Logo realizamos a classe MyButton a partir do CButton, em que adicionamos o membro TAction que, por sua vez, é o ponteiro para a função.
//+------------------------------------------------------------------+
//| Criamos nossa classe de botão com a função de manipulador de eventos |
//+------------------------------------------------------------------+
class MyButton: public CButton
{
private:
TAction m_action; // manipulador de eventos para o gráfico
public:
MyButton(void){}
~MyButton(void){}
//--- construtor com indicação do texto do botão e ponteiro para a função a fim de manipular eventos
MyButton(string text, TAction act)
{
Text(text);
m_action=act;
}
//--- definição de função que será chamada a partir do manipulador de eventos OnEvent()
void SetAction(TAction act){m_action=act;}
//--- manipulador padrão de eventos de gráfico
virtual bool OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) override
{
if(m_action!=NULL & lparam==Id())
{
//--- chamamos o manipulador próprio m_action()
m_action(sparam,(int)lparam);
return(true);
}
else
//--- retornamos o resultado da chamada do manipulador a partir da classe mão CButton
return(CButton::OnEvent(id,lparam,dparam,sparam));
}
};
Em seguida, criamos a classe derivada CControlsDialog a partir da CAppDialog, à qual adicionamos a matriz m_buttons para armazenas os botões do tipo MyButton, bem como os métodos AddButton(MyButton &button) e CreateButtons().
//+------------------------------------------------------------------+
//| Classe CControlsDialog |
//| Designação: painel gráfico para controle do aplicativo |
//+------------------------------------------------------------------+
class CControlsDialog : public CAppDialog
{
private:
CArrayObj m_buttons; // matriz de botões
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;
//--- adição de botão
bool AddButton(MyButton &button){return(m_buttons.Add(GetPointer(button))) ;m_buttons.Sort();};
protected:
//--- criação de botões
bool CreateButtons(void);
};
//+------------------------------------------------------------------+
//| Criação do objeto CControlsDialog no 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
//+------------------------------------------------------------------+
//| Criação e adição de botões para o painel CControlsDialog |
//+------------------------------------------------------------------+
bool CControlsDialog::CreateButtons(void)
{
//--- cálculo de coordenadas de botões
int x1=INDENT_LEFT;
int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y);
int x2;
int y2=y1+BUTTON_HEIGHT;
//--- adicionamos os objetos dos botões juntamente com os ponteiros para as funções
AddButton(new MyButton("Open",Open));
AddButton(new MyButton("Save",Save));
AddButton(new MyButton("Close",Close));
//--- criamos os botões 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_sub win,x1,y1,x2,y2))
{
PrintFormat("Failed to create button %s %d",b.Text(),i);
return(false);
}
//--- adicionamos cada botão no recipiente CControlsDialog
if(!Add(b))
return(false);
}
//--- succeed
return(true);
}
Agora podemos escrever o programa usando o painel de controle CControlsDialog, no qual são criados 3 botões "Open", "Save" e "Close". Ao pressionar o botão, é chamada a função correspondente que está escrita como um ponteiro para a função TAction.
//--- declaramos o objeto no nível global para criá-lo automaticamente ao inciar o programa
CControlsDialog MyDialog;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- agora criamos o objeto no gráfico
if(!MyDialog.Create(0,"Controls",0,40,40,380,344))
return(INIT_FAILED);
//--- executamos o aplicativo
MyDialog.Run();
//--- inicialização bem-sucedida do aplicativo
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 os eventos do gráfico, chamamos o manipulador a partir da classe mãe (neste caso, CAppDialog)
MyDialog.ChartEvent(id,lparam,dparam,sparam);
}
A aparência do aplicativo em execução e os resultados dos botões pressionados são mostrados na imagem.