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

 
for (long Chart = ChartFirst(); Chart != -1; Chart = ChartNext(Chart))

Such a loop will loop through all charts, including even nested OBJ_CHART variants (for which OBJPROP_CHART_ID was called)

 
The function to get a list of all charts and the function to close a chart.
// List of all charts, including CHART_IS_OBJECT.
int GetCharts( long &Charts[], long Chart = 0 )
{
  if (Chart)
    for (uint i = ObjectsTotal(Chart, -1, OBJ_CHART); (bool)i--;)
    {  
      const long NewChart = ObjectGetInteger(Chart, ObjectName(Chart, i, -1, OBJ_CHART), OBJPROP_CHART_ID);
      
      if (NewChart)
      {
        Charts[ArrayResize(Charts, ArraySize(Charts) + 1) - 1] = NewChart;
        
        GetCharts(Charts, NewChart);      
      }
    }
  else
  {
    ArrayFree(Charts);
    
    for (long Chart = ChartFirst(); Chart != -1; Chart = ChartNext(Chart))
    {
      long Tmp;
      
      if (!ChartGetInteger(Chart, CHART_IS_OBJECT, -1, Tmp))
        ResetLastError();
      else if (!Tmp) 
      {
        Charts[ArrayResize(Charts, ArraySize(Charts) + 1) - 1] = Chart;
        
        GetCharts(Charts, Chart);
      }
    }
  }
  
  return(ArraySize(Charts));
}

// Closes and CHART_IS_OBJECT charts.
bool ChartCloseFull( long chart_id = 0, long Chart = 0 )
{
  bool Res = false;
  long Tmp;
  
  if (Chart)  
    for (uint i = ObjectsTotal(Chart, -1, OBJ_CHART); !Res && (bool)i--;)
    {
      const string Name = ObjectName(Chart, i, -1, OBJ_CHART);      
      const long NewChart = ObjectGetInteger(Chart, Name, OBJPROP_CHART_ID);
      
      ResetLastError();
      Res = ((NewChart == chart_id) && (ObjectDelete(Chart, Name) ||
                                        (_LastError == ERR_OBJECT_ERROR) ||
                                        (_LastError == ERR_OBJECT_NOT_FOUND))) ||
            ChartCloseFull(chart_id, NewChart);
    }
  else if (!ChartGetInteger(chart_id, CHART_IS_OBJECT, -1, Tmp) || Tmp)
  {
    ResetLastError();
    
    if (!chart_id)
      chart_id = ChartID();
    
    for (long Chart = ChartFirst(); !Res && (Chart != -1); Chart = ChartNext(Chart))
      if (!ChartGetInteger(Chart, CHART_IS_OBJECT, -1, Tmp))
        ResetLastError();
      else if (!Tmp)
        Res = ChartCloseFull(chart_id, Chart);
  }
  else
    Res = ChartClose(chart_id);
  
  return(Res);
}


Example.

long CreateChart( const long Chart = 0 )
{
  const string Name = (string)MathRand();
  
  return(ObjectCreate(Chart, Name, OBJ_CHART, 0, 0, 0) ?  ObjectGetInteger(Chart, Name, OBJPROP_CHART_ID) : -1);
}

void OnStart()
{  
  long NewCharts[];
  const int Size = ArrayResize(NewCharts, 9);
  
  for (int i = 0; i < Size; i++)
  {
    NewCharts[i] = CreateChart((bool)(i %3) ? NewCharts[i - 1] : 0);
    
    Sleep(100);
  }
  
  Print("\nCreated charts:");
  ArrayPrint(NewCharts);

  long Charts[];
  
  Print("\nList of all charts:");
  GetCharts(Charts);
  ArrayPrint(Charts);

  for (int i = Size; (bool)i--;)
  {
    Print("\nDelete " + (string)NewCharts[i] + " - " + (string)ChartCloseFull(NewCharts[i]));
    
    Sleep(100);
    
    Print("List of all charts:");
    GetCharts(Charts);
    ArrayPrint(Charts);
  }  
}


Result.

Created charts:
[0] 132503570123939383 132503570123939384 132503570123939385 132503570123939386 132503570123939387
[5] 132503570123939388 132503570123939389 132503570123939390 132503570123939391

List of all charts:
[0] 132503570123939382 132503570123939386 132503570123939387 132503570123939388 132503570123939389
[5] 132503570123939390 132503570123939391 132503570123939383 132503570123939384 132503570123939385

Delete 132503570123939391 - true
List of all charts:
[0] 132503570123939382 132503570123939386 132503570123939387 132503570123939388 132503570123939389
[5] 132503570123939390 132503570123939383 132503570123939384 132503570123939385

Delete 132503570123939390 - true
List of all charts:
[0] 132503570123939382 132503570123939386 132503570123939387 132503570123939388
[4] 132503570123939389 132503570123939383 132503570123939384 132503570123939385

Delete 132503570123939389 - true
List of all charts:
132503570123939382 132503570123939386 132503570123939387 132503570123939388 132503570123939383 132503570123939384 132503570123939385

Delete 132503570123939388 - true
List of all charts:
132503570123939382 132503570123939386 132503570123939387 132503570123939383 132503570123939384 132503570123939385

Delete 132503570123939387 - true
List of all charts:
132503570123939382 132503570123939386 132503570123939383 132503570123939384 132503570123939385

Delete 132503570123939386 - true
List of all charts:
132503570123939382 132503570123939383 132503570123939384 132503570123939385

Delete 132503570123939385 - true
List of all charts:
132503570123939382 132503570123939383 132503570123939384

Delete 132503570123939384 - true
List of all charts:
132503570123939382 132503570123939383

Delete 132503570123939383 - true
List of all charts:
132503570123939382
 

I use such a universal construction of setting on/off Common-folder via file name.

int FileOpen2( string file_name, int open_flags,
               const short delimiter = '\t', const uint codepage = CP_ACP )
{
  open_flags |= ::StringReplace(file_name, "*", NULL) ? FILE_COMMON : 0;
  
  return(::FileOpen(file_name, open_flags, delimiter, codepage));
}                       

If the file name contains an asterisk, it will open in the Common folder, otherwise - as specified.

 

Who knows how to make this work ?

// тестовый класс
class Foo {
public:
   Foo() { payload=MathRand(); }
   ~Foo() {}
public:
   int payload;   
};

int CompareByPayload(Foo *one,Foo *two)
{
   return two.payload - one.payload; 
}

/* // КСТАТИ, если раскоментить, будет "fatal error" компилятора
template <typename T>
int CompareByPayload(T *one,T *two)
{
   return two.payload - one.payload; 
}
*/

// ШАБЛОННАЯ СОРТИРОВКА
// для массива с указателями на объекты
template <typename T>
typedef int (*CompareFunction)(T *,T *);

template <typename T>
int Sort(T *&_arr[],CompareFunction _cmp)
{
   // тут какая-то сортировка
   return 0;
}

Foo *arr[];
void OnStart()
{
   ArrayResize(arr,100);

   for(int i=0;i<100;i++)
      arr[i]=new Foo();

   Sort(arr,CompareByPayload);

   for(int i=0;i<100;i++) {
      Print("payload=%d",arr[i].payload);
      delete arr[i];
   }
}

to get a Sort template ( array_pointers, compare_function )

 
Maxim Kuznetsov #:

Who knows how to make something like this work ?

to get a Sort template ( array_pointers, compare_function )

Template pointers to functions do not work in MQL5.

 
fxsaber #:

Template pointers to functions do not work in MQL5.

sadly... :-(

so until there is a pointer to a member-function (what a word that is), it is premature to talk about human OO in MQL.

 
Maxim Kuznetsov #:

It is premature to talk about human OO in MQL

Forum on trading, automated trading systems and testing trading strategies

Do I need multiple inheritance of interfaces in MQL?

Koldun Zloy, 2025.01.23 14:29

It is a feature that can easily be done without.

It will not untie anyone's hands.

It has nothing to do with development.

 
fxsaber #:

Is there a modern language where you can't make a template Sort() ?

PS/ unlike multiple inheritance it can be done ugly through #define SORT(arr,func). But that's cheesy from the 80's. Why it's not in templates is a mystery to me
 
fxsaber #:

Template pointers to functions do not work in MQL5.

There is a nice solution to overcome that limitation of MQL. The trick is to pass the templated CompareFunc to the generic sort function both as a function pointer +  type parameter T .

This trick helps the compiler to resolve the typename parameters..

GenericSortTemplated.mq5

// templated class
template <typename T>
class Foo {
public:
   Foo() { payload=(T)10*MathRand()/MathRand(); }
   ~Foo() {}
public:
   T payload;
};

// templated compare function for different types of payloads
template <typename T>
bool CompareByPayload(T one, T two)
{

   return one.payload < two.payload;
}

//--------------------------------------------------------------------------------------------
// TEMPLATE SORTING
// function is templated for any type of array (e.g., int[], double[], pointers to objects[]).
// The CompareFunc type is a pointer to comparison function that can accept different types.
//--------------------------------------------------------------------------------------------
template <typename T, typename CompareFunc>
void GenericSort(T &a[], CompareFunc compare)
{
   for(int i=1; i < ArraySize(a); ++i)
      for(int j=i; j>0 && compare( a[j], a[j-1] ); j--)
      {
         T tmp=a[j]; a[j]=a[j-1]; a[j-1]=tmp;
      }
}

// custom sort function for different types of payloads
template< typename T >
void Sort(T& a[])
{
   typedef bool (*CompareFunc)( T, T );

   GenericSort< T, CompareFunc >( a, CompareByPayload< T > );
}

Foo<int>   *arr[];
Foo<float> *flt[];

void OnStart()
{
   int n = 7;
   ArrayResize(arr,n);
   ArrayResize(flt,n);

   for(int i=0;i<n;i++) {
      arr[i]=new Foo<int>();
      flt[i]=new Foo<float>();
   }

   Sort(arr);
   Sort(flt);

   for(int i=0;i<n;i++) {
      printf("%d. payload=%s",i,(string)arr[i].payload);
      delete arr[i];
   }

   for(int i=0;i<n;i++) {
      printf("%d. payload=%s",i,(string)flt[i].payload);
      delete flt[i];
   }
}

The sort function is templated. The compare function is also templated. The highlight colors indicate how the typename parameters are specialized and resolved.

output:

GenericSort (EURUSD,H1) 0. payload=1
GenericSort (EURUSD,H1) 1. payload=5
GenericSort (EURUSD,H1) 2. payload=6
GenericSort (EURUSD,H1) 3. payload=7
GenericSort (EURUSD,H1) 4. payload=10
GenericSort (EURUSD,H1) 5. payload=30
GenericSort (EURUSD,H1) 6. payload=230

GenericSort (EURUSD,H1) 0. payload=1.2722387313842773
GenericSort (EURUSD,H1) 1. payload=2.1265437602996826
GenericSort (EURUSD,H1) 2. payload=2.4967143535614014
GenericSort (EURUSD,H1) 3. payload=4.115740776062012
GenericSort (EURUSD,H1) 4. payload=7.864346981048584
GenericSort (EURUSD,H1) 5. payload=11.456646919250488
GenericSort (EURUSD,H1) 6. payload=18.580562591552734
 
Hello everyone.
I created a WPF application using .net. While I was reading the documentation about imports at https://www.mql5.com/en/book/advanced/libraries/libraries_import and it clearly states:
Functions are imported from compiled MQL5 modules (*.ex5 files) and from Windows dynamic library modules (*.dll files). 

While I was conducting some tests I tried to build my application as .exe instead of .dll and imported it into my mql5 EA and saw that the compiler doesn't give me any errors and also I can use static functions inside the program.

Since I couldn't find anything about importing .exe programs in the documentation I was wondering if that implies something I am now aware of. Does it impact the lifecycle of the EA? Does the terminal manage .exe imports differently from .dlls in any way?

Thanks for your time.