Macro-bug since MT5-Update?

 

Dear MetaQuotes and affiliates,


thank you for making MetaTrader!


However, to my great dismay, I just noticed that the following code, specifically the macro definition...

#define isNew(var, ID) isChanged##ID(var)

...which still worked on 2nd August 2023, today no longer works (compiles)!

##

...is the token paste operator which should merge isChanged and ID together, with __COUNTER__ being the ID, thus effectively becoming the function...

isChanged0(var)


This is my complete code:

#define isNew(var, ID) isChanged##ID(var)
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   isNew(3, __COUNTER__);
  }
//+------------------------------------------------------------------+
template <typename T> bool isChanged0  (T value) { static T previous0  ; bool changed = (value != previous0  ); previous0   = value; return changed; }
template <typename T> bool isChanged1  (T value) { static T previous1  ; bool changed = (value != previous1  ); previous1   = value; return changed; }
template <typename T> bool isChanged2  (T value) { static T previous2  ; bool changed = (value != previous2  ); previous2   = value; return changed; }

And this is the compilation-error which I now all of a sudden get (but not on 2nd August 2023 when it still compiled fine):

'isChanged__COUNTER__' - undeclared identifier

Apparently, the __COUNTER__ constant no longer is translated to 0, 1, 2, ... at each encounter by the compiler but just stays __COUNTER__.


Can this be related to the MT5 update from 14th September 2023, because back then macro-related changes were implemented!?


If this is a bug then please fix it.


If there is anything else I can do in the meantime, please let me know. I spent much time coming up with this solution for listening on many variables for changes without every time having to write all that boilerplate code. It allowed me to simply write...

isNew(var1, __COUNTER__);
isNew(var2, __COUNTER__);
isNew(var3, __COUNTER__);
...

...and you could do just the same 😉 if it weren't for this annoying, new compilation error!


Please help!


What's new in MetaTrader 5
  • www.metatrader5.com
New trading report improvements
 

Yes, this was already noticed back in build 3950, and reported ...

However, MetaQuotes has not addressed it.

There are some proposed work-around options on the two threads above, so have a read.

 
Fernando Carreiro #:

Yes, this was already noticed back in build 3950, and reported ...

However, MetaQuotes has not addressed it.

There are some proposed work-around options on the two threads above, so have a read.

Thanks for your promt reply and confirmation that it's a bug, Fernando!

Unfortunatley I found no workaround for me.

If this bug freaks even Alain Verleyen out, then MeatQuotes must certainly fix it and soon, please! I even thought that Alain is MetaQuote's unofficial link to their customers, now that they took their online contact form offline. It's really frightening to see code that worked and that you depend on stop working without any means of reporting it to the developers, that cannot be. This destroys time, value and trust!

So how can we point this out to MetaQuotes? They certainly would want to fix this if they knew about it. But they seem to have completely withdrewn from us customers. MetaQuotes, do you hear us? Please answer!

Now I feel the need to back up not only my code, but also MetaTrader 5 versions and compilers. It's really frightening. Does anybody know where to still get older MetaTrader 5 versions to download?

Alain Verleyen
Alain Verleyen
  • 2023.10.21
  • www.mql5.com
Trader's profile
 

Noted, thank you.

Will be fixed next week.

 
Rashid Umarov #:

Noted, thank you.

Will be fixed next week.

Will be fixed and already next week, too! So great! Thank you!!
 

Fix your code please

#define __isNew_inner(var,ID) isChanged##ID(var)
#define isNew(var, ID) __isNew_inner(var,ID)
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   isNew(3, __COUNTER__);
  }
//+------------------------------------------------------------------+
template <typename T> bool isChanged0  (T value) { static T previous0  ; bool changed = (value != previous0  ); previous0   = value; return changed; }
template <typename T> bool isChanged1  (T value) { static T previous1  ; bool changed = (value != previous1  ); previous1   = value; return changed; }
template <typename T> bool isChanged2  (T value) { static T previous2  ; bool changed = (value != previous2  ); previous2   = value; return changed; }
 
Ilyas #: Fix your code please
Rashid Umarov #: Noted, thank you. Will be fixed next week.

@Rashid Umarov stated that it would be fixed, but @Ilyas asks for the user to change their code anyway. That is a contradiction and does not make any sense.

The whole point was for MetaQuotes to fix their end so that users would not need re-edit all their previous code.

Also the "fix" proposed by @Ilyas makes no sense. Why would it work if the macro name starts with "__" and ends with "_inner" but not in other cases?

What special operation does that format invoke? It does not seem to be documented.

 

We still work on MQL parser, it will be updated soon.

The previous macro expansion is no longer enough and we decided to update it.
In the previous version of the MQL compiler, it was impossible to concatenate or convert to string tokens passed as macro parameters.
Now this is possible, but in some cases you will have to change your macros.

I apologize for the inconvenience - this is a payment for progress.

 
@Ilyas #:We still work on MQL parser, it will be updated soon.The previous macro expansion is no longer enough and we decided to update it. In the previous version of the MQL compiler, it was impossible to concatenate or convert to string tokens passed as macro parameters. Now this is possible, but in some cases you will have to change your macros. I apologize for the inconvenience - this is a payment for progress.
Thank you for explaining it in more detail. We are grateful for your input.
 
Fernando Carreiro #:
Also the "fix" proposed by @Ilyas makes no sense. Why would it work if the macro name starts with "__" and ends with "_inner" but not in other cases?

Macro name doesn't matter, you can use any what you like, here is explanation, how macros expands now:

#define __isNew_inner(var,ID) isChanged##ID(var)
#define isNew(var, ID) __isNew_inner(var,ID)
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   isNew(3, __COUNTER__);
  }
  1. isNew(3, __COUNTER__); // here we start
  2. __isNew_inner(3,0);    // after first expanding
  3. isChanged##0(3);       // after expanding __isNew_inner
  4. isChanged0(3);         // finally

C++ has same behavior


Instead of previous MQL version, now is available to Print __LINE__:

#define MACRO(x) Print("'",#x,"'=",x);

void OnStart()
  {
   MACRO(__LINE__);
  }

Output:

'__LINE__'=5

Before it was

'5'=5
 
Ilyas #:

Hi Ilyas,

thank you for "fixing" my code (that had worked before as it was 😉).

However, although I'm beginning to understand why you did this, I still don't understand how this new syntax does the same as before, but with an additional interim step (the...

__isNew_inner

)?

Can you please explain in more detail what is going on here under the hood (why __COUNTER__ compiles to __COUNTER__ with one single macro but to a number again with that inner call), so that we can generalize the new, underlying logic also to similar, but different problems? Because as I understand it, that specific, new syntax is now final, right?

That being said, I think I have come to realize what actually is the "progress" that you talked about: it is now finally possible to create variables dynamically in MQL5 like this:

#define makeVar(type, var, nr) type var##nr;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   makeVar(int, variable, 1);
   makeVar(string, variable, 2);
   
   variable1 = 7;
   variable2 = "Success!";
   Print(variable1);
   Print(variable2);
  }

/* 
out:
-------------
7
Success!
*/

Is this it? Because that's actually great stuff — why didn't you say so in the first place, code hero? :-)

So my only two remaining wishes (if I may):

1. Please explain why 

__isNew_inner

is now needed in more detail.

2. Please have your release notes and documentation updated to refect those great achievements! :-)

PS: I would also like to express my thanks to Rashid and Fernando Carreiro for moderating this issue!

Reason: