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

 
amrali #:
Wrong!

I write in the Russian-language thread, which is automatically translated into the English-language thread that you read.

Unfortunately, deleting the erroneous post in the Russian-language thread had no effect on the English-language thread.

 
amrali #:
2. Faster access times that were achieved here are never due to an object or a structure, it is merely due to compiler's optimization for the "switch" statement.

3. Optimization of switch depends on the number of cases + optimization mode. Large number or complex switches do not get this jump table optimization and will remain as multiple if-else.

4. Lookup table using an array will always be the standard and the most reliable method not depending on the number of cases, their complexity or the compiler's optimization mode.
int Lookup_Table(int x)
  {
   static int table[16] = {0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150};
   return table[x];
  }

// When compiling an small to medium switch with consecutive cases (0 to n), under
// optimization mode, the compiler converts the series of these multiple if-else
// statements O(n) into a jump table, where an array of pointers to code blocks
// is indexed using the cases number at runtime, giving O(1) direct access similar
// to the lookup table using an array.
int Switch(int x)
  {
   switch(x)
     {
      case 0: return 0;
      case 1: return 10;
      case 2: return 20;
      case 3: return 30;
      case 4: return 40;
      case 5: return 50;
      case 6: return 60;
      case 7: return 70;
      case 8: return 80;
      case 9: return 90;
      case 10: return 100;
      case 11: return 110;
      case 12: return 120;
      case 13: return 130;
      case 14: return 140;
      case 15: return 150;
     }
   return -1;
  }

void OnStart()
  {
   long sum1 = 0;
   long sum2 = 0;
   int slots = 16;
   int test_count = 1e8;

   int seed = MathRand();

   ulong time1 = GetMicrosecondCount();
   for(int i = 0; i < test_count; i++)
      for(int x = 0; x < slots; x++) {
         //int j = (x * 7 + seed) % slots;  // uncomment two lines to test lookups in a random order (Miller Shuffle algorithm)
         //sum1 += Lookup_Table(j);         // in a real situation, the lookups are not sequential
         sum1 += Lookup_Table(x);
      }
   time1 = GetMicrosecondCount() - time1;
   PrintFormat("%6ull microsec,  sum:%ull %s", time1, sum1, " // Lookup table");

   ulong time2 = GetMicrosecondCount();
   for(int i = 0; i < test_count; i++)
      for(int x = 0; x < slots; x++) {
         //int j = (x * 7 + seed) % slots;
         //sum2 += Switch(j);
         sum2 += Switch(x);
      }
   time2 = GetMicrosecondCount() - time2;
   PrintFormat("%6ull microsec,  sum:%ull %s", time2, sum2, " // Switch statement");
  }

Maximum Optimization:

//  43834ll microsec,  sum:4035883008ll  // Lookup table
//   6179ll microsec,  sum:4035883008ll  // Switch statement

Faster access times are due to the generated jump table under the full optimization mode.

No Optimization:

// 2636121ll microsec,  sum:4035883008ll  // Lookup table
// 4120042ll microsec,  sum:4035883008ll  // Switch statement
 

A simple and reliable piece of code to generate a full 32-bit unsigned random number in [0, UINT_MAX]:

MathRand()^_RandomSeed;

we can also use it as functions

uint random32(void)
{ 
   return MathRand()^_RandomSeed;
}

ulong random64(void) 
{ 
   return ((ulong)random32 << 32) + random32();  // [0, ULONG_MAX]
} 
 

Old bug coming back again.

No proof given, but printf() to the expert-journal is again truncating strings to be printed, abitrarily.

Obviously the limit of printing strings beyond size of 16K is again truncated, as it was quite some builds ago, and now came back. - Only possible fix is round about this:


            void mq5_fixed_printf(string p1)
            {
                string ml_out[];
                const int lines = StringSplit(p1, 0x0A, ml_out);

                for(int cnt = NULL; (cnt < lines); cnt++)
                { printf("%s", ml_out[cnt]); }
            }

Above code actually needs more complex wrapping with templated calls, such that you can pass through StringFormat() before sending your string to above function.

Examples for this Bug have been given before, please search the forum, if you are in doubt of my findings and reporting.

 

I needed to determine the length of the test interval.


I couldn't find it on the forum, so I wrote such a function.

// Returns the difference between the dates.
string GetDiffTime( const datetime From, const datetime To )
{
  string Str = NULL;
  
  MqlDateTime sFrom;
  MqlDateTime sTo;
  
  if (TimeToStruct(From, sFrom) && TimeToStruct(To, sTo))
  {
    int Days = sTo.day - sFrom.day;
    int Months = (sTo.year - sFrom.year) * 12 + (sTo.mon - sFrom.mon) - (Days < 0);
    
    const int Years = Months / 12;    
    Months %= 12;
    
    if (Days < 0)
    {
      if (sFrom.mon++ == 12)
      {
        sFrom.mon = 1;
        sFrom.year++;        
      }
      
      sFrom.day = 1;
      
    #define  DAY (24 * 3600)
      Days = (int)((StructToTime(sFrom) - From) / DAY) + sTo.day - 1;
    #undef  DAY
    }
        
  #define  TIMETOSTRING(A) Str += (A ? ((Str == NULL) ? NULL : " ") + (string)A + " " + #A : NULL);
    TIMETOSTRING(Years);
    TIMETOSTRING(Months);
    TIMETOSTRING(Days);
  #undef  TIMETOSTRING      
  }
  
  return(Str);
}


void OnStart()
{
  Print(GetDiffTime(D'2022.02.24', D'2025.08.24')); // 3 Years 6 Months
  Print(GetDiffTime(D'2025.02.28', D'2025.03.01')); // 1 Days
  Print(GetDiffTime(D'2024.02.28', D'2025.03.01')); // 1 Years 2 Days
  Print(GetDiffTime(D'2024.02.29', D'2025.03.01')); // 1 Years 1 Days
}

It seems to show correctly.

 
fxsaber #:
Couldn't find it on the forum, so I wrote such a function.
Nice, but as a minimalist I would write Y3 M6 D2, or 03.06.02 12:43 (if you add time). Well, it's a matter of taste.
 
fxsaber #:

We needed to determine the length of the testing interval.


I couldn't find it on the forum, so I wrote such a function.


It seems to show correctly.

What is the point of such contrivances?

/****************************GetDiffTime*****************************/
string GetDiffTime(const datetime From, const datetime To)
 {
  string Str = NULL;
  MqlDateTime delta;
  TimeToStruct((To-From), delta);
  string y1=delta.year-1970>0?string(delta.year-1970)+" Years ":NULL;
  string m1=delta.mon-1>0?string(delta.mon-1)+" Months ":NULL;
  string d1=delta.day-1>0?string(delta.day-1)+" Days":NULL;
  StringConcatenate(Str,y1,m1,d1);
  return(Str);
 }/******************************************************************/

The result is the same

2025.08.24 11:18:40.544 Test shablon (AUDCAD,M5)        3 Years 6 Months 
2025.08.24 11:18:40.544 Test shablon (AUDCAD,M5)        1 Days
2025.08.24 11:18:40.544 Test shablon (AUDCAD,M5)        1 Years 2 Days
2025.08.24 11:18:40.544 Test shablon (AUDCAD,M5)        1 Years 1 Days
 
Alexey Viktorov #:

Why all the fancy stuff?

The result is the same

Print(GetDiffTime(D'2024.02.28', D'2025.02.27')); // 1 Years
 
A small example showing changes in MQL5 after b5200.

Forum on trading, automated trading systems and testing trading strategies

New version of MetaTrader 5 build 5200: OpenBLAS extension and increased control in MQL5

Ilyas, 2025.08.04 14:08

To be clear, the operation 'TYPE NAME = INITIALIZER' will no longer be split into two operations DECL + ASSIGNMENT, but will remain a single 'DECL [has initializer]'
If you need to declare a variable and call the = operator for it, split the operation into two parts manually: DECL; ASSIGNMENT; (yes, you will have to part with the variable constant).

struct A
{
  int Tmp;

// A() {}
// A( const A& ) { Print(__FUNCSIG__); } 
  
  void operator =( const A& ) { Print(__FUNCSIG__); }
};

void OnStart () 
{ 
  A a1;
  
  // const - allowed.
  /*const */ A a2 = a1;     // The implicit copy constructor A::A(const A&) is called
  
  // const - not allowed.
  /*const */ A a3; a3 = a1; // The copy operator A::operator=(const A&) is called
}


Similarly.

class B
{
  A a;

public:  
  B( const A &Tmp) : a(Tmp) // The implicit copy constructor A::A(const A&) is called
  {
    a = Tmp;                // The copy operator A::operator=(const A&) is called
  }
};

void OnStart()
{
  A a;
  B b(a);
}


If there is a need to initialise a static object through a copy operator, it can be done by this technique.

class B
{
  static A a1;
  static A a2;
  static A a3;
  
  static bool Init;
  
  template <typename T>
  static bool Operator( T &Dest, const T& Source )
  {
    Dest = Source; // The copy operator T::operator=(const T&) is called
    
    return(true);
  }
};

static A B::a1;
static A B::a2; // Need B::a2 = B::a1 via A::operator=(const A&)
static A B::a3; // Need B::a3 = B::a1 via A::operator=(const A&)

static bool B::Init = B::Operator(B::a2, B::a1) && // B::a2 = B::a1 via A::operator=(const A&)
                      B::Operator(B::a3, B::a1);   // B::a3 = B::a1 via A::operator=(const A&)
 

Forum on trading, automated trading systems and testing trading strategies

Peculiarities of mql5 language, subtleties and techniques of work

fxsaber, 2025.08.26 08:32

A a2 = a1;     // The implicit copy constructor A::A(const A&) is called
Pay attention to such constructions in your sources. After b5200 they may stop working as before. The compiler will not report anything about it.