Features of the mql5 language, subtleties and tricks - page 307

 
amrali #:


Another (indirect) second-level macro is required to separate, combine and structure the parameters of nested macros.

You propose to create a different version of the string translation macro for each case. But then the solution is not universal, because it depends on the type of macro.

It turns out that there is no general mechanism of macro to string translation.

 
fxsaber #:

You propose to create a different version of the string translation macro for each case. But then the solution is not universal, because it depends on the type of macro.

It turns out that there is no general mechanism of macro to string translation.

Watch for for nested macro calls. Parameters to macros which themselves are macros get expanded on 2 stages;

void func(int arg) {  }   

func(MACROS1); 

if arg is a macro, then it will be totally expanded (i.e., evaluated, or textual replacement happens) at compiler/parser time.

#define MACROS1 (......) 

#define TOSTRING(arg) (......) 

TOSTRING(MACROS1));

if arg is a macro (i.e., nested macro calls), then it will not be expanded (no textual replacement inside macro body) on the first pass.

Also in the first pass # stringify operator and ## concat operator get ON HOLD. So, that you need an 2nd level indirection macro.

In the second pass, the macro body is re-evaluated again for further expansions (textual replacement, stringify and concat).

It is a limitation of macros, because they are evaluated by the compiler at runtime as text (not as executing code).

 
amrali #:

This is a limitation of macros, as they are evaluated by the compiler at runtime as text (rather than executable code).

Solution.

#define  TOSTRING(A) TOSTRING2(( A))
#define  TOSTRING2(A) StringSubstr(#A, 1, StringLen(#A) - 2)

int Num1 = 123;
int Num2 = 456;
int Num3 = 789;

#define  MACROS1 Num1
#define  MACROS2 Num2, Num3

void f( int i, int j = 0 ) { Print((string)i + "," + (string)j); }

template <typename T>
void f2( T ) {}

void OnStart()
{
  f(MACROS1); // 123,0
  f(MACROS2); // 456,789
  
  Print(TOSTRING(MACROS1)); // Num1
  Print(TOSTRING(MACROS2)); // Num1, Num2
}
 

If we're talking about parametric macros, here's an interesting question: how to expand a comment in a macro. In such a macro

#define  INPUT_PAR(TYPE, NAME, VAL, MIN, STEP, HI, MAX)  	\
        const TYPE #NAME##_MIN  = (MIN);			\
        const TYPE #NAME##_HI   = (HI);				\
        const TYPE #NAME##_MAX  = (MAX);			\
        input TYPE NAME = (VAL); // #NAME [#MIN/#STEP/#HI]

the comment is needed to replace the text in input in the terminal. However, the macro does not expand the comment, but omits it.

 
Edgar Akhmadeev #:

How to expand a comment in a macro.

You can't.
 
fxsaber #:
No way.

I do. Thank you.

 
fxsaber # :

Solution.

I don't consider it a solution using pure macros. Using a helper function like StringSubst() or Print() that is evaluated at runtime can help to escape the default order of macro parameter expansion (2 steps). These are the semantics of how the C compiler works.

Stringifying the args can be helpful for displaying debug messages. But in order to keep the meaning of args (work on them), you have to use other indirection macros.

FYI, comments are removed by the parser from the source code file before any macro is expanded, so comments inside macros are deleted.


 
amrali #:
I don't believe that using pure macros is the solution. Using a helper function like StringSubst() or Print() that evaluates at runtime can help avoid the standard macro parameter disclosure order (2 steps). This is the semantics of how the C compiler works.
I don't get it. Problem solved. A universal macro to translate another macro to a string is provided, regardless of the number of commas in the original macro.
 
amrali #:
I don't consider it a solution using pure macros. Using a helper function like StringSubst() or Print() that is evaluated at runtime can help to escape the default order of macro parameter expansion (2 steps). These are the semantics of how the C compiler works.

Stringifying the args can be helpful for displaying debug messages. But in order to keep the meaning of args (work on them), you have to use other indirection macros.

FYI, comments are removed by the parser from the source code file before any macro is expanded, so comments inside macros are deleted.


Pure macros ? 

Macros in MQL doesn't have all the features of C++. And even C++ macros are very limited, compared to some existing solutions (like M4 for example).

What I will never understand is why a company choose to create a proprietary language (MQL) to finally decide to use an archaic macros system beside it. All of these features should be better implemented directly in the language.

Anyway, we have to deal with it.

 
Another differencebetween MQL5and MQL4.
void f( const string& ) {}

void OnStart()
{
  string Str = NULL;
  
// f("Hello" + Str); // '+' - parameter passed as reference, variable expected
  f("Hello"); // OK.
}  
MQL4 does not compile this kind of code, unlike MQL5.