English Русский Español
preview
Do básico ao intermediário: Definições (II)

Do básico ao intermediário: Definições (II)

MetaTrader 5Exemplos |
358 0
CODE X
CODE X

Introdução

O conteúdo exposto aqui, visa e tem como objetivo, pura e simplesmente a didática. De modo algum deve ser encarado como uma aplicação final, onde o objetivo não seja o estudo dos conceitos aqui mostrados.

No artigo anterior Do básico ao intermediário: Definições (I), falamos sobre a diretiva de compilação #define. Vimos como utilizar esta diretiva a fim de tornar tanto nosso código mais simples, rápido e fácil de ser implementado. Como também vimos como utilizar esta mesma diretiva, de modo criativo e perfeitamente possível, para tornar, durante a fase de aprendizagem da linguagem, as coisas um pouco mais simples. Se bem, que para utilizar este mesmo recurso, é preciso entender o que está sendo feito. Mas de uma forma geral, podemos dar ao nosso código MQL5, uma aparência bem mais exótica. Se é que podemos dizer assim.

Muito bem, apesar disto, existe uma segunda forma de se utilizar a diretiva de compilação #define. Mas para não tornar aquele artigo anterior, demasiadamente complicado. Decidi mostrar isto somente agora. Em um artigo totalmente a parte. Desta maneira, poderemos abordar o tema com mais calma. E você, meu caro e estimado leitor, poderá estudar e praticar as coisas de maneira bem mais agradável. Tornando assim as coisas realmente mais fáceis de serem compreendidas e assimiladas. Já que este conhecimento será de suma importância para o que iremos ver no decorrer dos próximos artigo. Principalmente quando começarmos a trabalhar em um nível de programação, que eu já considero ser um nível intermediário.


Entendendo o que seria uma macro

Uma macro de maneira bem simplista, seria uma pequena rotina, ou procedimento, do qual queremos ou podemos vir a utilizar diversas vezes em um código. Isto de uma maneira bem simplista de colocar as coisas. Na verdade, em grande parte das vezes, e isto se estende para outras situações um pouco mais complicadas. Criamos uma macro, sempre que parte de um código se repede de forma muito constante no código. Assim ao invés de codificar sempre a mesma coisa, colocamos tudo em uma unidade, ou procedimento. Que por sua vez recebe o nome de macro.

Porém esta forma de se definir uma macro, não é de fato adequada. Isto por conta de alguns agravantes que tornam criar tal definição um pouco mais complicada, do que isto que foi dito a pouco.

O problema, é que em uma ampla, se não em quase todo momento, macros são definidas de maneira a colocar código inline. Ao invés de os colocar dentro de um procedimento, que iria gerar chamadas empilhando e desempilhando coisas na memória. O meu ver, esta sim seria a melhor definição do que seria uma macro. Mas dizer que estaremos colocando código inline ao utilizar uma macro, não a torna algo realmente especial. Isto devido ao fato de que também, em tese, podemos fazer isto com qualquer procedimento ou função. Digo em tese, por não ter de fato experimentado uma grande diferença em termos de tempo de execução de funções ou procedimentos declarados como sendo inline ou não.

Muito provavelmente você não faz ideia do que estou falando. Então deixe-me tornar as coisas um pouco mais claras. Assim como em C e C++, o MQL5, possui uma palavra reservada, que dificilmente vejo outros programadores utilizando. Pelo menos aqui no MQL5. Tal palavra reservada é a inline. Mas o que esta palavra significa na prática? Bem, meu caro leitor, normalmente quando um programador cria um código, ele pode, caso a linguagem permita isto. Tornar chamadas de procedimento ou até mesmo funções em um código inline. Ou seja, deixamos de ter chamadas de procedimento, para ter um código que cresce de maneira exponencial a fim de tornar sua execução mais rápida. Isto ao custo de utilizar mais memória.

Pode parecer bobagem, ou até mesmo insanidade. Porém quando bem utilizada, esta forma de forçar o compilador a criar um código, mais rápido, ao custo de utilizar mais memória, pode ser o caminho correto. Porém devesse tomar cuidado ao fazer isto. Pois se o código cresce de maneira exponencial. Mais hora ou menos hora, você acabará em um beco sem saída. Já que terá necessidade de cada vez mais memória, e o ganho em termos de velocidade de processamento, não cobre o custo de mais memória sendo necessária.

Para exemplificar, vamos ver como isto seria feito. Para tal, veja o código logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Fibonacci_Element_Default   11
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     Print("Result: ", Fibonacci_Recursive());
09.     Print("Result: ", Fibonacci_Interactive());
10. }
11. //+------------------------------------------------------------------+
12. uint Fibonacci_Recursive(uint arg = def_Fibonacci_Element_Default)
13. {
14.     if (arg <= 1)
15.         return arg;
16. 
17.     return Fibonacci_Recursive(arg - 1) +  Fibonacci_Recursive(arg - 2);
18. }
19. //+------------------------------------------------------------------+
20. inline uint Fibonacci_Interactive(uint arg = def_Fibonacci_Element_Default)
21. {
22.     uint v, i, c;
23. 
24.     for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2)
25.         v += i;
26. 
27.     return (c == arg ? i : v);
28. }
29. //+------------------------------------------------------------------+

Código 01

Agora preste atenção ao que irei explicar, meu caro leitor. Pois isto é muito importante para que você consiga compreender de maneira adequada o que seria uma macro. Isto ao utilizarmos uma diretiva de compilação #define para criar uma rotina ou procedimento a ser utilizado em nosso código.

Observe que este código 01, está sendo muito parecido com o visto nos demais artigos, até agora. Porém na linha 20, temos algo diferente. Este algo diferente, é justamente a palavra reservada inline. Agora, você, olhando e já sabendo como este código 01 funciona. Pode até dizer e me questionar: Mas o que está simples palavra adicionada na linha 20, tem de especial, a fim de tornar o código diferente?

Bem, a resposta é que o código não será criado, ou pelo menos não deveria ser criado como você espera. Isto por que, este mesmo código, se levado para C ou C++, irá gerar um código ligeiramente diferente. Não em termos do que você está conseguindo enxergar. Mas sim em termos de como o compilador irá lidar com esta forma de escrever o código.

Novamente, estou presumindo que o compilador do MQL5, faz as coisas de forma que não consigamos notar diferença entre um código escrito da forma como o código 01 está sendo feito, e outro código visto nos artigos anteriores. Sinceramente não consegui notar mudanças em termos de velocidade de processamento, quando usamos ou não inline na declaração de um procedimento ou função.

De qualquer forma, o que o compilador irá entender ao ver esta linha 20. Que SEMPRE que esta função Fibonacci_Interactive, aparecer no código, QUE TODO O CÓDIGO entre as linhas 20 e 28 deverá substituir a chamada que existir como sendo Fibonacci_Interactive. E ao fazer isto, ele deverá construir toda uma base de variáveis locais, presentes no procedimento ou função. De maneira, a não conflitar com possíveis variáveis locais presentes no local onde o código será adicionado.

Para tornar isto mais claro, este mesmo código 01, seria montado pelo compilador como sendo algo parecido com o que é visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Fibonacci_Element_Default   11
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     Print("Result: ", Fibonacci_Recursive());
09.     
10.     {
11.         uint v, i, c, arg = def_Fibonacci_Element_Default;
12. 
13.         for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2)
14.             v += i;
15. 
16.         Print("Result: ",  (c == arg ? i : v));
17. 
18.     }
19. }
20. //+------------------------------------------------------------------+
21. uint Fibonacci_Recursive(uint arg = def_Fibonacci_Element_Default)
22. {
23.     if (arg <= 1)
24.         return arg;
25. 
26.     return Fibonacci_Recursive(arg - 1) +  Fibonacci_Recursive(arg - 2);
27. }
28. //+------------------------------------------------------------------+

Código 02

Agora este código 02, é que de fato seria o código que seria criado e executado. Porém observe que foi necessário mexer no código de uma forma muito específica. E quem faria isto, seria o compilador. Nós como programadores apenas diríamos ao compilador, que é ou não para colocar uma função ou procedimento inline. Mas que seria o responsável por ajustar as coisas seria o próprio compilador.

Ok, neste momento você pode estar pensando: Certo. Mas não vejo motivo para tal preocupação. Já que aparentemente tudo continuou da mesma maneira. De fato, meu caro leitor. Aqui foi um exemplo simples. Mas considere o fato de que aquela função da linha 20, vista no código 01, fosse utilizada mil vezes em seu código. O compilador irá fazer isto que é mostrado no código 02, mil vezes. Isto tornaria o executável cada vez maior. Além de ele ocupar cada vez mais espaço e precisar de cada vez mais tempo para ser carregado. Mesmo que a velocidade de execução fosse maior, talvez não iria compensar.

Então se você conseguiu entender isto, entender o que seria uma macro, como iremos definir em breve. Será algo muito simples e direto. Inclusive podemos fazer já a definição da primeira macro usando este código visto acima. Note que entre as linhas 10 e 18 temos o que é um código completo. Ou melhor dizendo, um bloco completo e perfeitamente isolado do restante do código. Assim para tornar este código a nossa primeira macro, bastaria que o mudássemos para algo parecido como o que é visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Fibonacci_Element_Default   11
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     Print("Result: ", Fibonacci_Recursive());
09. 
10. #define macro_Fibonacci_Interactive     {                           \
11.         uint v, i, c, arg = def_Fibonacci_Element_Default;          \
12.         for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2) v += i;  \
13.         Print("Result: ",  (c == arg ? i : v));                     \
14.                                         }
15. }
16. //+------------------------------------------------------------------+
17. uint Fibonacci_Recursive(uint arg = def_Fibonacci_Element_Default)
18. {
19.     if (arg <= 1)
20.         return arg;
21. 
22.     return Fibonacci_Recursive(arg - 1) +  Fibonacci_Recursive(arg - 2);
23. }
24. //+------------------------------------------------------------------+

Código 03

Agora vem a parte interessante. Este código 03 é quase, e que fique bem claro, quase, a mesma coisa do código 02. Porém quando executado ele terá um resultado diferente do código 02, quando executado. Você consegue ver qual é a diferença meu caro leitor? Muito provavelmente não. Isto porque, não é algo tão claro e evidente de ser notado, na primeira vez que vemos uma macro sendo declarada.

Note o seguinte meu caro leitor, tudo que fiz de diferente entre o código 02 e o código 03, foi adicionar a diretiva de compilação #define na linha 10. Esta está sendo seguida de uma constante. Sim, a macro É UMA CONSTANTE. Nunca se esqueça disto.

Uma macro ao ser definida deve estar contida em UMA ÚNICA LINHA.

NÃO É POSSIVEL criar uma macro com mais de uma linha de código. Isto é uma regra imposta dela linguagem de programação. Assim, a forma de montar a macro usando para isto, diversas linhas são no final de cada linha adicionar um caractere especial. Este é a barra que você pode ver em cada uma das linhas partindo da linha 10. Mas, tenha o cuidado de na última linha NÃO COLOCAR ESTA BARRA. Caso contrário, a macro não irá terminar onde você esperava. E com isto, pode ser que acontece alertas de erro durante a tentativa de compilar o código.

Bem, então parece ser bem simples de fazer as coisas funcionarem não é mesmo? Bem mais ou menos. Se você tiver os devidos cuidados. Sim, é bem simples de criar macros. Mas se você está pensando que é somente isto que é visto no código 03 se enganou. Pois esta seria a macro, baseada no código 02. Lembre-se, ao executar o código 03, o resultado será diferente do código 02. Mas porquê? O motivo é que a macro NÃO ESTÁ SENDO UTILIZADA. Apenas declaramos ela. Sendo assim, na saída do código 02, teremos duas informações como pode ser visto na imagem abaixo.

Imagem 01

Porém ao executar o código 03, a saída será a vista na imagem na sequência.

Imagem 02

Bem, mas então como podemos resolver isto? Para fazer isto, meu caro leitor, devemos dizer para o compilador utilizar a macro no código. Isto é bem simples como você pode notar logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Fibonacci_Element_Default   11
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     Print("Result: ", Fibonacci_Recursive());
09. 
10. #define macro_Fibonacci_Interactive     {                           \
11.         uint v, i, c, arg = def_Fibonacci_Element_Default;          \
12.         for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2) v += i;  \
13.         Print("Result: ",  (c == arg ? i : v));                     \
14.                                         }
15. 
16.     macro_Fibonacci_Interactive;
17. }
18. //+------------------------------------------------------------------+
19. uint Fibonacci_Recursive(uint arg = def_Fibonacci_Element_Default)
20. {
21.     if (arg <= 1)
22.         return arg;
23. 
24.     return Fibonacci_Recursive(arg - 1) +  Fibonacci_Recursive(arg - 2);
25. }
26. //+------------------------------------------------------------------+

Código 04

Agora sim. Com a inclusão da linha 16 neste código 04 temos finalmente o mesmo resultado que pode ser visto na imagem 01. Mostrando que de fato o código funciona como esperado. Porém esta foi a parte fácil. Agora vem a parte divertida. Mas para isto, vamos separar as coisas em um novo tópico. Isto por que, é preciso que você primeiro entenda o que foi mostrado até aqui, antes de tentar entender o que será feito a seguir.


Passando argumentos a uma macro

Virgem Maria santíssima. Achei que já tínhamos terminado, e aí vem você e me diz que podemos passar argumentos para uma macro? Bem, deixe-me ver se entendi o conceito do que seria uma macro. Me corrija que eu estiver errado.

Uma macro seria o código que existiria em uma rotina que queremos colocar na forma inline no nosso código. Certo? Sim. Você está correto. Então se eu conseguir, criar uma forma de implementar, toda uma função ou procedimento, dentro de uma macro, não precisarei criar a mesma função ou procedimento. Já que para o compilador não haverá diferença para ele montar o código. Estou correto? Novamente correto. Só que tem um pequeno detalhe: Implementar procedimentos é mais simples que funções. Já que funções exigem uma variável extra, na maior parte dos casos. Mas ainda assim está correto.

Então já saquei. Se quero passar um argumento para dentro de uma macro. Basta que eu utilize a mesma forma de declarar as coisas que é feita, na declaração de uma função ou procedimento. Simples assim. Matei a pau.

Bem meu caro leitor, neste caso você está quase correto. Isto por que, não declaramos os argumentos a serem passados para dentro de uma macro, da mesma maneira que passamos quando usamos uma função ou procedimento. Neste caso a coisa funciona de forma um pouco diferente. E é aqui onde o bicho pega. Isto por que, diferente do que acontece, na passagem de parâmetros para dentro de funções e procedimentos. Que podemos dizer, este argumento não pode ser manipulado. Já este pode ser manipulado. Este argumento é de tal tipo, este outro daquele outro tipo. Quando fazemos isto em macros, NÃO TEMOS este controle. E por consequência, não temos ajuda do compilador para evitar alguns tipos de erros bem desagradáveis.

A diferença começa por aí. Por isto, muitos programadores, de maneira geral evitam utilizar macros. Isto por que, qualquer pequeno deslize ou bobeira que você fizer ao logo do código, pode lhe pôr em sérios apuros. Já que erros presentes em macros são muito difíceis de serem corrigidos. Justamente por que são difíceis de serem encontrados. Visto que em uma parte do código, a macro pode estar fornecendo valores corretos. Enquanto em outro ponto do mesmo código, um pouco mais abaixo, a mesma macro pode estar fornecendo valores errados. E conseguir detectar este tipo de falha, a fim de conseguir corrigir a mesma é uma das tarefas, ao meu ver mais complicadas e cansativas que existe.

Então tome muito cuidado ao utilizar macros em seu código. Elas são um recurso valioso. Mas podem lhe tirar horas, tentando resolver um problema, que de outra forma seria simples de ser corrigido.

Ok, então vamos fazer o seguinte: Como o código 04, nada mais é que uma modificação do código 01, a ponto de que possamos utilizar macros. Podemos mexer neste código 04, a fim de entender como passar valores para dentro de uma macro.

Observe o seguinte. No código 01, temos nas linhas oito e nove, a capacidade de utilizar duas funções diferentes. Uma onde a resposta seria recursiva e outra onde a resposta seria interativa. Mas no código 04, apesar de termos tanto a resposta recursiva como a interativa. NÃO PODEMOS passar um valor para a parte do cálculo interativo. A menos que venhamos a mexer no valor informado na definição da linha quatro. Mas isto não vale. Queremos poder passar qualquer valor. Assim como seria feito no caso do código 01.

Para tornar o que acabei de dizer, mais fácil de entender, veja o código logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Fibonacci_Element_Default   11
05. //+------------------------------------------------------------------+
06. #define macro_Fibonacci_Interactive     {                           \
07.         uint v, i, c, arg = def_Fibonacci_Element_Default;          \
08.         for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2) v += i;  \
09.         Print("Result: ",  (c == arg ? i : v));                     \
10.                                         }
11. //+----------------+
12. void OnStart(void)
13. {
14.     Print("Result: ", Fibonacci_Recursive(15));
15.     macro_Fibonacci_Interactive;
16. }
17. //+------------------------------------------------------------------+
18. uint Fibonacci_Recursive(uint arg = def_Fibonacci_Element_Default)
19. {
20.     if (arg <= 1)
21.         return arg;
22. 
23.     return Fibonacci_Recursive(arg - 1) +  Fibonacci_Recursive(arg - 2);
24. }
25. //+------------------------------------------------------------------+

Código 05

Ao executar este código 05, você verá como resultado o que é visto na imagem logo abaixo.

Imagem 03

Obviamente os resultados são diferentes. Isto por que na linha 14 do código 05, estamos passando um valor como parâmetro da função da linha 18. Algo que não acontece na macro. Já que ela está com o valor fixado no que se refere a definição da linha quatro. Então você pode notar que trabalhar com macros exige alguns cuidados e um pouco mais de atenção. Assim para resolver isto, precisamos passar algum argumento para a macro. Mas para fazer isto, precisamos mudar levemente o código da macro. E esta mudança pode ser vista no código logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Fibonacci_Element_Default   11
05. //+------------------------------------------------------------------+
06. #define macro_Fibonacci_Interactive(arg) {                          \
07.         uint v, i, c;                                               \
08.         for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2) v += i;  \
09.         Print("Result: ",  (c == arg ? i : v));                     \
10.                                         }
11. //+----------------+
12. void OnStart(void)
13. {
14.     Print("Result: ", Fibonacci_Recursive(15));
15.     macro_Fibonacci_Interactive(14);
16. }
17. //+------------------------------------------------------------------+
18. uint Fibonacci_Recursive(uint arg = def_Fibonacci_Element_Default)
19. {
20.     if (arg <= 1)
21.         return arg;
22. 
23.     return Fibonacci_Recursive(arg - 1) +  Fibonacci_Recursive(arg - 2);
24. }
25. //+------------------------------------------------------------------+

Código 06

Ao executar este código 06 o resultado é o que podemos ver na imagem logo abaixo.

Imagem 04

Claramente os valores são diferentes, mas isto foi feito de maneira proposital. Justamente para mostrar que podemos passar valores independentes, e que uma coisa não está ligada a outra. Sendo assim, acredito ficar mais claro, que uma macro não está ligada a nenhuma outra função ou procedimento.

Bem isto foi interessante. Mas está longe de ser divertido. Já que a macro, não está se comportando como uma função, como seria no código 01. E tão pouco como um procedimento a parte. Ela na verdade, está apenas sendo um código isolado dos demais. Porém este código 06, continua trabalhando como se fosse o código 02. E isto na maior parte das vezes é algo completamente inútil. Não servindo para absolutamente nada. Salvo o fato de você desejar colocar, o que seria a linha 15 deste código 06 em diversos outros pontos. Porém como estes códigos, visam serem didáticos, os mesmos são simples e não necessitam utilizar tal aparato, que é as macros. Mas estamos fazendo isto aqui, justamente para explicar como utilizar e trabalhar com tal ferramenta.

Sendo assim, vamos pensar como fazer com que a macro se comporte como se fosse uma função. Na verdade, NÃO PODEMOS usar uma macro, como a que está sendo vista no código 06, como uma função. Lembre-se de que uma função é como uma variável especial. Ela sempre devolve um valor quando a usamos para obter um valor com base em alguns argumentos passados a ela. Preste atenção no que foi dito. NÃO ESTOU dizendo que não podemos usar macros como funções. Estou dizendo que NÃO PODEMOS usar ESTÁ MACRO como sendo uma função. Cuidado para não confundir alhos com bugalhos.

Porém apesar desta limitação, inicial, podemos fazer com que ESTÁ MACRO se comporte como um procedimento, onde estamos fazendo passagem por referência. Como? Simples, basta mudar o código como mostrado abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Fibonacci_Element_Default   11
05. //+------------------------------------------------------------------+
06. #define macro_Fibonacci_Interactive(arg) {                          \
07.         uint v, i, c;                                               \
08.         for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2) v += i;  \
09.         arg = (c == arg ? i : v);                                   \
10.                                         }
11. //+----------------+
12. void OnStart(void)
13. {
14.     ulong value;
15. 
16.     Print("Result: ", Fibonacci_Recursive((uint)(value = 15)));
17.     macro_Fibonacci_Interactive(value);
18.     Print("Checking: ", value);
19. }
20. //+------------------------------------------------------------------+
21. uint Fibonacci_Recursive(uint arg = def_Fibonacci_Element_Default)
22. {
23.     if (arg <= 1)
24.         return arg;
25. 
26.     return Fibonacci_Recursive(arg - 1) +  Fibonacci_Recursive(arg - 2);
27. }
28. //+------------------------------------------------------------------+

Código 07

Aqui as coisas começam a ficar divertidas. Isto por que, começamos a usar a macro para um objetivo mais nobre, por assim dizer. Note o seguinte fato, na linha 14 declaramos uma variável. Porém NÃO A INICIALIZAMOS. Fazemos a inicialização da mesma, na linha 16. Agora você pode estar se perguntando: Cara mais que coisa mais maluca é esta, na linha 16? Calma meu caro leitor, não é algo maluco.

Note que a variável da linha 14 é do tipo ulong, e o tipo esperado pela função da linha 21 é do tipo uint. Como estamos querendo usar o mesmo valor, tanto para a função da linha 21 quanto também para a macro que será utilizada a seguir. Precisamos fazer uma conversão explicita de tipo, para que o compilador não gere alertas. Por conta disto, é que você está vendo a declaração da linha 16 ser feita desta maneira.

Muito bem, logo depois disto, temos a linha 17. Aqui é onde a coisa começa a ficar interessante. Isto por que, estamos passando a variável por referência. Assim, quando a macro modificar o valor da mesma, termos isto sendo refletido aqui, no nosso código principal. A mudança de valor se dá na linha nove. Por isto é preciso ficar atento. Caso contrário, podemos acabar com uma bomba relógio nas mãos. E para demonstrar que de fato, este sistema funciona e pode ser utilizado. Temos a linha 18, para imprimir o valor da variável no terminal. Assim ao executar este código 07, teremos como resposta a imagem vista logo abaixo.

Imagem 05

Ok, acredito que deu para entender o fato de que sempre que passamos alguma variável para dentro de uma macro. Estamos fazendo isto, usando uma passagem por referência. E justamente por conta disto, precisamos tomar cuidado para não caímos no erro de termo o valor modificado, de maneira indevida pela macro. Mas espere um pouco. Este tipo de macro que podemos ver no código 07 é uma macro que funciona como sendo um procedimento. Será que não existe uma forma de usarmos uma macro como sendo uma função. Isto é, de forma que podemos enviar um valor para ela e obtermos um valor como sendo uma resposta? Bem, esta é uma dúvida, que muitos programadores de fato possuem no início do aprendizado sobre macro.

Bem lá no fundo, macros são mais voltadas a serem trabalhadas como procedimentos. Porém, dependendo da maneira como construímos o código dentro de uma macro. Podemos sim, fazer com que ela trabalhe como se fosse uma função. Neste caso que estamos vendo nos códigos apresentados nestes artigos. Podemos criar um exemplo disto. Apenas como uma forma de apresentar o mecanismo. Mesmo que não seja algo muito elaborado. E possa aparecer até pouco interessante. Podemos tentar. Bem, um exemplo disto pode ser visto no código logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Fibonacci_Element_Default   11
05. //+------------------------------------------------------------------+
06. #define macro_Ternário(A, B, C, D)  (A == B ? C : D)
07. //+----------------+
08. #define macro_Fibonacci_Interactive(arg) {                          \
09.         uint v, i, c;                                               \
10.         for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2) v += i;  \
11.         arg = macro_Ternário(c, arg, i, v);                         \
12.                                         }
13. //+----------------+
14. void OnStart(void)
15. {
16.     ulong value;
17. 
18.     Print("Result: ", Fibonacci_Recursive((uint)(value = 15)));
19.     macro_Fibonacci_Interactive(value);
20.     Print("Checking: ", value);
21. }
22. //+------------------------------------------------------------------+
23. uint Fibonacci_Recursive(uint arg = def_Fibonacci_Element_Default)
24. {
25.     if (arg <= 1)
26.         return arg;
27. 
28.     return Fibonacci_Recursive(arg - 1) +  Fibonacci_Recursive(arg - 2);
29. }
30. //+------------------------------------------------------------------+

Código 08

Aqui, neste código 08, podemos ver uma pequena demonstração de uma macro trabalhando como função. Está macro definida na linha seis, é um caso simples de macros, que funcionam como uma função. Note que na linha 11 a estamos utilizando. Basicamente isto tende a ocultar parte da complexidade que possa existir no código. Já que dependendo do nome que você venha a dar a macro. Torna bem mais simples, entender o que o código estará fazendo.

Note que o objetivo aqui, NÃO É tornar o código mais eficiente, e sim, mais legível. Por exemplo: Você poderia construir um conjunto de macros para poder manipular valores de data e hora. Isto a fim de utilizar o tipo datetime. Este seria um típico exemplo, bastante útil para ser utilizado em macros com a funcionalidade de uma função. Para tornar isto mais claro e palatável. Vamos criar algumas macros apenas para demonstrar isto que mencionei.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. enum eConvert {
05.     FORMAT_DECIMAL,
06.     FORMAT_OCTAL,
07.     FORMAT_HEX,
08.     FORMAT_BINARY
09. };
10. //+------------------------------------------------------------------+
11. #define macro_GetDate(A)            (A - (A % 86400))
12. #define macro_GetTime(A)            (A % 86400)
13. //+----------------+
14. #define macro_GetSec(A)             (A - (A - (A % 60)))
15. #define macro_GetMin(A)             (int)((A - (A - ((A % 3600) - (A % 60)))) / 60)
16. #define macro_GetHour(A)            (int)((A - (A - ((A % 86400) - (A % 3600)))) / 3600)
17. //+----------------+
18. #define PrintX(X)                   Print(#X, " => ", X);
19. //+------------------------------------------------------------------+
                   .
                   .
                   .

Fragmento 01

Neste fragmento 01, temos o que seria o nosso arquivo de inclusão. Estou colocando as macros nele para mostrar que podemos ampliar de maneira substancial toda a nossa capacidade de programação, conforme o tempo passa. Já que você vai adquirindo experiência, e montando o que poderia ser chamada de uma biblioteca particular.

Bem, para experimentar o que estas macros vistas entre as linhas 11 e 18 fazem. Vamos usar um pequeno código de testes. Este é visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #include "Tutorial\File 01.mqh"
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     datetime dt = D'2024.08.15 12:30:27';
09.     ulong v;
10. 
11.     Print("Date And Time: ", dt);
12.     Print("0x", ValueToString(dt, FORMAT_HEX));
13.     PrintX((datetime)(v = macro_GetDate(dt)));
14.     Print("0x", ValueToString(v, FORMAT_HEX), " :: ", ValueToString(v, FORMAT_DECIMAL));
15.     PrintX((datetime)(v = macro_GetTime(dt)));
16.     Print("0x", ValueToString(v, FORMAT_HEX), " :: ", ValueToString(v, FORMAT_DECIMAL));
17.     PrintX((datetime)(v = macro_GetSec(dt)));
18.     Print("0x", ValueToString(v, FORMAT_HEX), " :: ", ValueToString(v, FORMAT_DECIMAL));
19.     PrintX((datetime)(v = macro_GetMin(dt)));
20.     Print("0x", ValueToString(v, FORMAT_HEX), " :: ", ValueToString(v, FORMAT_DECIMAL));
21.     PrintX((datetime)(v = macro_GetHour(dt)));
22.     Print("0x", ValueToString(v, FORMAT_HEX), " :: ", ValueToString(v, FORMAT_DECIMAL));
23. }
24. //+------------------------------------------------------------------+

Código 09

Isto daqui é de fato algo muito interessante e divertido. Já que com muito pouco trabalho, podemos analisar uma série de coisas, que de outra maneira, precisaria ser feito utilizando outros recursos, que ainda não foram explicados. Ao executar este código 09, você poderá ver o que é mostrado na imagem logo abaixo.

Imagem 06

Agora notem porque eu disse que este código 09 é bastante interessante. Existem nas linhas 13, 15, 17, 19 e 21 uma chamada a uma macro. Está é a macro que está definida na linha 18 do fragmento 01. O objetivo desta macro é nos dizer o nome da variável e o seu valor. No caso, o nome da variável é de fato uma função que utiliza uma macro diferente em cada uma das linhas mencionadas no código 09. Observe que é uma combinação bastante inusitada e interessante. Já que o valor que cada macro retorna é colocada em uma variável local, e logo depois iremos imprimir o valor hexadecimal desta variável, assim como o valor decimal.

Isto para mostrar que de fato estamos conseguindo capturar os valores corretos, que estão no formado datetime. Sei que muitos de vocês podem achar este tipo de coisa bastante maluca. Mas de fato, como você pode observar e poderá constatar. Este tipo de macro é bastante util. Ainda mais quando queremos que um código execute de maneira o mais rápida, segura possível. Além de tornar todo o trabalho de codificação, consideravelmente mais simples, agradável e divertido.


Considerações finais

Neste artigo, vimos o que seria e como devemos pensar em macros. Isto para fins de codificação. Sendo um dos recursos que muitas vezes é mal empregado, e em outras ignorado, devido justamente a falta de um conhecimento adequado e prática na utilização de macros. Muitos programadores, acabam ficando meio que sem entender, por que algo que eles consideravam ser impossíveis, ou até mesmo inalcançável, pode ser feito, por um outro programador, muitas das vezes desconhecido.

Eu mesmo tenho e gosto de pensar, na seguinte tese:

Não existe programador ruim. Existe sim profissional mal capacitado. Ou que se acha preparado, quando na verdade, ainda é um iniciante quando o assunto é programação.

Este tipo de conceito, que estou tentando repassar aqui, nestes artigos. É justamente visando, pessoas que estão começando a estudar programação. Se você começar da forma correta. Compreendendo conceitos e o motivo de esta ou aquela ferramenta existir. Fica mais simples tornar e colocar suas ideias em prática. Então meu caro leitor. Sei que este conteúdo visto neste artigo, pode parecer um tanto quanto bobo e sem muito proposito ou objetivo. Porém, se você compreender e praticar o que foi visto aqui. Verá que muito daquilo que você outrora considerava complicado e difícil de fazer. Será perfeitamente alcançável. Claro que ainda faltam algumas coisas para serem explicadas. Mas já demos muitos passos na direção correta.

Arquivos anexados |
Anexo.zip (4.58 KB)
Criando um Limitador de Drawdown Diário EA em MQL5 Criando um Limitador de Drawdown Diário EA em MQL5
O artigo discute, de forma detalhada, como implementar a criação de um Expert Advisor (EA) baseado no algoritmo de negociação. Isso ajuda a automatizar o sistema em MQL5 e a controlar o Drawdown Diário.
Desenvolvendo um sistema de Replay (Parte 77): Um novo Chart Trade (IV) Desenvolvendo um sistema de Replay (Parte 77): Um novo Chart Trade (IV)
Neste artigo, explicarei alguns detalhes e cuidados que você teve tomar quando for criar um protocolo de comunicação. São coisas bem básicas e simples. Não irei de fato pegar pesado neste artigo. Mas é preciso que você entenda o conteúdo deste artigo para entender o que acontecerá no receptor.
Eigenvetores e autovalores: Análise exploratória de dados no MetaTrader 5 Eigenvetores e autovalores: Análise exploratória de dados no MetaTrader 5
Neste artigo, exploramos diferentes maneiras pelas quais os eigenvetores e os autovalores podem ser aplicados na análise exploratória de dados para revelar relacionamentos únicos nos dados.
Técnicas do MQL5 Wizard que você deve conhecer (Parte 27): Médias Móveis e o Ângulo de Ataque Técnicas do MQL5 Wizard que você deve conhecer (Parte 27): Médias Móveis e o Ângulo de Ataque
O Ângulo de Ataque é uma métrica frequentemente citada, cuja inclinação é entendida como tendo uma forte correlação com a força de uma tendência predominante. Vamos analisar como ele é comumente usado e compreendido e examinar se há mudanças que poderiam ser introduzidas na forma como é medido, para benefício de um sistema de negociação que o utilize.