Macro-bug since MT5-Update? - page 2

 

Bodolino #:

it is now finally possible to create variables dynamically in MQL5 like this:

Oh no: I'm wrong, dynamic varibale creation has been possible all along!


What I really wanted to ask is:


1. Please explain __isNew_inner in detail

2. With your new macro string capabilities, is it now somehow possible to do away with my above isChanged1() -helper function and make isNew() a more or less self-contained macro, e.g. by doing something like this (just so it compiles & works):

// macro definition:

#define ISNEW(type, val, id) { SET_LAST(type, id); bool isnew = val != last##id; last##id = val; return isnew; }

#define SET_LAST(typ, uid) static typ last##uid;



// macro call:

void OnTick()
{
   ISNEW(int,    my_random_int_value(),    __COUNTER__);
   ISNEW(string, my_random_string_value(), __COUNTER__);
   ...
}
You know, my point is to not always reuse thes same last-comparison variable because it would be shared across macro calls and mix up results.
 

working version of code

// macro definition:
#define ISNEW_wrap(type, val, id) { static type last##id; bool isnew = val != last##id; last##id = val; return isnew; }
#define ISNEW(type, val, id) ISNEW_wrap(type,val,id)

void OnTick(void)
{
   ISNEW(int,    my_random_int_value(),    __COUNTER__);
   ISNEW(string, my_random_string_value(), __COUNTER__);
   
   ...
}

Your main misunderstand: macro is not a function, it is the part of the text processor:

  1. Parameters passed as text, not as values.
  2. After macro expanding, all text given by macro will be parsed.

In this case lexeme '__COUNTER__' passed to macro ISNEW will be replaced by parser to actual number

See expanding steps
   1. ISNEW(int,    my_random_int_value(),    __COUNTER__);   
   2. ISNEW_wrap(int,my_random_int_value(),0)   
   3. { static int last0; bool isnew = val != last0; last0 = val; return isnew; }
 
Ilyas #:

working version of code

Hi Ilyas,

thank you very much for your answer. I have read everything here and also the docs on MQL5 macros in detail, but I still don't exactly understand e.g. this:

Why is   __COUNTER__   still   __COUNTER__ after the first expansion, but a number (0 in our case) after the second expansion?

I also don't understand what exactly changed with macros with build 3950...


Also, your above code...

#define ISNEW_wrap(type, val, id) { static type last##id; bool isnew = val != last##id; last##id = val; return isnew; }
#define ISNEW(type, val, id) ISNEW_wrap(type,val,id)

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnTick(void)
{
   int    my_random_int_value = 3;
   string my_random_string_value = "abc";
   
   ISNEW(int,    my_random_int_value,    __COUNTER__);
   ISNEW(string, my_random_string_value, __COUNTER__);
   
}

...throws the compilation error 'return' - 'void' function returns a value . So I refactored it into:

#define ISNEW_INNER_INNER(result) result
#define ISNEW_INNER(type, val, id) { static type last##id; bool isnew = val != last##id; last##id = val; ISNEW_INNER_INNER(isnew); }
#define ISNEW(type, val, id) ISNEW_INNER(type,val,id)

Does that look good to you?

To test the above code snippet in action with actual, random values over time, I wove it into the following code (an indicator which each second creates new, random values of different types within a small range and prints whether they have changed or not):

//+-------------------------------------------------------------------+
//+                              Macros                               +
//+-------------------------------------------------------------------+
#define ISNEW_INNER_INNER(result) result
#define ISNEW_INNER(type, val, id) { static type last##id; bool isnew = val != last##id; last##id = val; ISNEW_INNER_INNER(isnew); }
#define ISNEW(type, val, id) ISNEW_INNER(type,val,id)
//+-------------------------------------------------------------------+
//+-                             Global                               +
//+-------------------------------------------------------------------+
enum MY_UP_DOWN_1 {
    up1   = 0,
    down1 = 1
};
//+-------------------------------------------------------------------+
//+                               Init                                +
//+-------------------------------------------------------------------+
int OnInit()
  {
   EventSetTimer(1);

   Print("\r\n--------------------- " + __FILE__ + " ---------------------\r\n");

   return(INIT_SUCCEEDED);
  }

//+-------------------------------------------------------------------+
//+                     Timer: runs each second                       +
//+-------------------------------------------------------------------+
void OnTimer()                                                                          // →                          →                             →                             →                         →                                   →                           →                   ↓  
  {
    int      var0    = rd(1, 2);                                                        static int last_var0;         printf("0 | last_var0: %16i   | var0: %16i   | is new: %s", last_var0, var0,                                                                             (string)(bool) ISNEW (int,          var0, __COUNTER__)); last_var0 = var0;
    double   var1    = (double)rd(3, 4);                                                static int last_var1;         printf("1 | last_var1: %18.1f | var1: %18.1f | is new: %s", last_var1, var1,                                                                             (string)(bool) ISNEW (double,       var1, __COUNTER__)); last_var1 = var1;
    int      var2    = rd(5, 6);                                                        static int last_var2;         printf("2 | last_var2: %16i   | var2: %16i   | is new: %s", last_var2, var2,                                                                             (string)(bool) ISNEW (int,          var2, __COUNTER__)); last_var2 = var2;
    int      var3    = rd(7, 8); string abc; var3 == 7 ? abc = "lorem" : abc = "ipsum"; static string last_abc;       printf("3 | last_abc : %16s   | abc : %16s   | is new: %s", last_abc , abc ,                                                                             (string)(bool) ISNEW (int,          abc , __COUNTER__)); last_abc = abc;
    datetime var4    = iTime(_Symbol, PERIOD_M1, 0);                                    static int last_var4;         printf("4 | last_var4: %16s   | var4: %16s   | is new: %s", TimeToString(last_var4, TIME_DATE|TIME_MINUTES), TimeToString(var4, TIME_DATE|TIME_MINUTES), (string)(bool) ISNEW (datetime,     var4, __COUNTER__)); last_var4 = var4;
    MY_UP_DOWN_1 var5= rd(9,10); MY_UP_DOWN_1 dir; var5 == 9 ? dir = up1 : dir = down1; static MY_UP_DOWN_1 last_dir; printf("5 | last_dir : %16s   | dir : %16s   | is new: %s", EnumToString(last_dir) , EnumToString(dir) ,                                                 (string)(bool) ISNEW (MY_UP_DOWN_1, dir , __COUNTER__)); last_dir = dir;
      
    Print("");
  }

//+------------------------------------------------------------------+
//| Function: Random int number between min & max                    | 
//+------------------------------------------------------------------+

int rd(int min = 0, int max = 32767, bool allValuesTheSame = false)
{  if(allValuesTheSame) srand(GetTickCount()); // srand injects seed for random result replicability (constancy)
   int random = WRONG_VALUE;
   while(random < min || random > max)
   {  random = MathRand();}
   return random; }

However, if you compile that code as it is, you'll get a whole bunch of macro-related errors like:

(35,287) : error 149: 'static' - unexpected token
(35,287) : error 149: 'last0' - unexpected token
(35,287) : error 149: ';' - unexpected token
(35,287) : error 149: 'isnew' - unexpected token
(35,287) : error 149: '=' - unexpected token
(35,287) : error 256: 'last0' - undeclared identifier
(35,287) : error 149: ';' - unexpected token
(35,287) : error 256: 'last0' - undeclared identifier
(35,287) : error 152: 'last0' - some operator expected
(35,287) : error 149: '{' - unexpected token
(35,287) : error 149: '=' - unexpected token
(36,287) : error 149: 'static' - unexpected token
(36,287) : error 149: 'last1' - unexpected token
(36,287) : error 149: ';' - unexpected token
(36,287) : error 149: 'isnew' - unexpected token
(36,287) : error 149: '=' - unexpected token
(36,287) : error 256: 'last1' - undeclared identifier
(36,287) : error 149: ';' - unexpected token
(36,287) : error 256: 'last1' - undeclared identifier
(36,287) : error 152: 'last1' - some operator expected
(36,287) : error 149: '{' - unexpected token
(36,287) : error 149: '=' - unexpected token

...

May I kindly ask you to explain it so that I can fix my code or — if you are especially kind — explain it by fixing my code with comments what you changed and why?


I'm really sorry to bother you, but the docs are a bit thin on macros and so are the articles, too. And you seem to be te only person around who has a deep understanding of this matter.

 

Of course you have errors, you can't declare variable inside a print statement.

Macros processing will now work as C++. You can eventually read about C++ preprocessor to see how it works.

 
Alain Verleyen #:

Of course you have errors, you can't declare variable inside a print statement.

Macros processing will now work as C++. You can eventually read about C++ preprocessor to see how it works.

Yes, true, but my intention to use that isNew-macro inside e.g. if-statements was clear from the start. I already had resolved this with my helper-functions, but it's an unelegant approach, so please let my question stand for Ilyas, the macro-expert right at the source and maker of the MQL5 preprocessor, to respond. Maybe he knows a way around it. And judging from your posts, you were adversely affected by the recent macro-changes in MQL5, too, so why hit at me now.


Anyway, if you know a good C++ tutorial on that matter (although it's not exactly the same as MQL5 and MQL5 is obviously in the process of changing their preprocessor), I'd be grateful for your link.

 
Bodolino #:
so why hit at me now.

Hit at you ?!? Don't take things so personally please. I tried to help explaining you what is happening.

Macros (The C Preprocessor)
  • gcc.gnu.org
Macros (The C Preprocessor)
 
Alain Verleyen #:

Hit at you ?!? Don't take things so personally please. I tried to help explaining you what is happening.

Ok, sorry, I took that the wrong way. You much rather deserve our big thanks for being so helpful over all the years with all your many competent and friendly posts, Alain! :-)

But I'm also seeing my questions still unanswered in important points, and since MQL5 macro documentation is a bit thin, since MQL5 macros are undergoing changes and since we are having their developer here (Ilyas), I would like to once more kindly ask Ilyas to answer as many of them as possible, please, as soon as he's got time. Not many people are macro-experts and even fewer develop the corresponding preprocessor, so I don't want to let go of this seldom chance, please :-)

Reason: