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

 
Maxim Kuznetsov #:

from where, to where, what and under what conditions was measured ?

just OrderSendAsync MODIFY and to the immediate next operation 5 sec ????

very strange, frightening, improbable result - we need to double-check the tests

I measured the time before and after the function, calculated the difference and got 5 seconds. In combat advisors everything is measured, so that there is more information to sort out an abnormal situation. Saw this.

 
An empty structure endows descendants with the appropriate assignment operator.
struct BASE {};

template <typename T>
struct A : public BASE
{
  T Tmp;
};

void OnStart()
{  
  A<double> d;
  A<int> a = d;
}
 

It was necessary to create structures that could be given different rules of actions inside, but manipulated among themselves as identical.

The technique used was formalised on this example.

struct NUMBERS
{
  int Num1;
  int Num2;
};

template <typename T>
struct A : public NUMBERS
{
  int Get1() { return(T::Action1(this.Num1, this.Num2)); }
  int Get2() { return(T::Action2(this.Num1, this.Num2)); }
};

class ADDITION
{
public:
  static int Action1( const int Num1, const int Num2 ) { return(Num1 + Num2); }
  static int Action2( const int Num1, const int Num2 ) { return(Num1 - Num2); }
};

class MULTIPLICATION
{
public:  
  static int Action1( const int Num1, const int Num2 ) { return(Num1 * Num2); }  
  static int Action2( const int Num1, const int Num2 ) { return(Num1 / Num2); }  
};

void OnStart()
{
  NUMBERS a = {5, 2};  
  
  A<ADDITION> b = a;
  Print(b.Get1()); // 7
  Print(b.Get2()); // 3
  
  A<MULTIPLICATION> c = b;
  Print(c.Get1()); // 10
  Print(c.Get2()); // 2
}


Unfortunately, I didn't understand why OOP languages don't have interfaces for static methods.

interface A
{
public:
  static void Func() {} // 'Func' - cannot be declared static
};
 
fxsaber static methods.

I have to create such a horror.

#define  INTERFACE \
  static void Func();
  
class A
{
public:
  INTERFACE
  
  static void f() { Print(typename(A)); }
};

class B
{
public:
  INTERFACE
  
  static void g() { Print(typename(B)); }  
};

static void A::Func() {}
static void B::Func() {}

template <typename T>
void Tmp() { T::Func(); }

void OnStart()
{
  Tmp<A>();
  Tmp<B>();
}
 
fxsaber static methods.

How do you imagine it?

Any function has its address in the .text segment.

Any member function (method), implicitly accepts this pointer as its first parameter.

Static methods do not accept the this pointer and are essentially syntactic "sugar", being, de facto, ordinary functions.

When a virtual function is called, the address of the executable function is taken from the virtual function table, the pointer to which is implicitly contained in the class in which the virtual function is declared. Initialisation of the pointer to the executable function occurs when creating an instance of the object, the logic is like this (I write it in mql on purpose so that it would be clear to all neophytes:

class Base;
class A;
class B;

void FooClassA(Base* p);
void FooClassC(Base* p);

typedef void(*_Foo)(Base*);

class Base{
public:
   Base(): foo(NULL){}
   void Foo() {foo(&this);}
protected:
   _Foo foo;
};

class A: public Base{
public:
   A():a(100){foo = FooClassA;}
   int a;
};

class B: public A{
public:
   B():b(-100){}
   int b;
};

class C: public B{
public:
   C(){foo = FooClassC;}
   int Get() {return a+b;}
};

void FooClassA(Base* p){PrintFormat("%s: %i",__FUNCTION__,dynamic_cast<A*>(p).a);}
void FooClassC(Base* p){PrintFormat("%s: %i",__FUNCTION__,dynamic_cast<C*>(p).Get());}

void OnStart(){
   A a;
   B b;
   C c;
   Base* p = &a;
   p.Foo();
   p=&b;
   p.Foo();
   p=&c;
   p.Foo();
}

Naturally, in reality, everything is not like this, but the mechanism of initialisation of the pointer to the function is exactly like this. Accordingly, there is no way, from the word "at all", to do it the way you want in a compiled language.

C++ has such template magic:

#include <iostream>
#include <variant>

struct A
{
    int a = 100;
    int Get() { return a; }
};

struct B :A
{
    int b = -100;
};

struct C :B
{
    int Get() { return a + b; }
};

using BaseImpl = std::variant<A, B, C>;
struct Base : BaseImpl
{
    using BaseImpl::BaseImpl;
    int Get() { return std::visit([](auto&& arg) { return arg.Get(); }, static_cast<BaseImpl&>(*this)); }
};

int main()
{
    Base a = A();
    Base b = B();
    Base c = C();
    std::cout << a.Get() << ", " << b.Get() << ", " << c.Get() << std::endl; //100, 100, 0
    b = C();
    std::cout << a.Get() << ", " << b.Get() << ", " << c.Get() << std::endl; //100, 0, 0
    return 0;
}
 
Vladimir Simakov #:

Any function has its address in the .text segment.

Any member function (method) implicitly accepts this pointer as its first parameter.

Static methods do not accept this pointer and are essentially syntactic "sugar", being de facto ordinary functions.

When a virtual function is called, the address of the executable function is taken from the virtual function table, the pointer to which is implicitly contained in the class in which the virtual function is declared. Initialisation of the pointer to the executable function occurs when creating an instance of the object, the logic is like this (I write it in mql to make it clear to all neophytes:

Naturally, in reality, it is not like this, but the mechanism of initialisation of the pointer to the function is exactly like this.

Thanks for the detailed explanation with an example!

 
Vladimir Simakov #:

What's your idea of that?

If you are talking about my regret about the possibilities of interfaces. I want to impose only syntactic restrictions on classes/structures. That is, only at the compilation stage, as it happens with the same const modifier. For self-control, in short.

There is no way, from the word "at all", to do what you want in a compiled language.

I wrote a crutch above. I wanted to get something already built in for such cases.

 

Forum on trading, automated trading systems and testing trading strategies

New version of MetaTrader 5 build 3950: Withdrawal/Fill in the terminal and updated trading report

fxsaber, 2023.09.19 23:25

How to get rid of errors?
#define  TEMPNAME(A) A##__LINE__
#define  TEMPNAME2(A) Tmp##A
   
void OnStart()
{
  int TEMPNAME(Tmp); // variable 'Tmp__LINE__' not used
  
  int TEMPNAME2(__LINE__); // 'Tmp__LINE__' - variable already defined
  int TEMPNAME2(__LINE__); // variable 'Tmp__LINE__' not used
}
#define  TEMPNAME2(A) Tmp##A
#define  TEMPNAME1(A) TEMPNAME2(A)
   
void OnStart()
{
  int TEMPNAME1(__LINE__);
  int TEMPNAME1(__LINE__);
}

The first time __LINE__/__COUNTER__ are passed inside the markos as text, the second time - numbers.

 
Probably invented a bicycle, I don't know. If someone knows more elegant solutions, please share.

Previously (in old compiler builds) it was possible to use a class before its declaration:
class A;

class B
{
   public: A a;
   public: int Val;
};

class A
{
   public: B * b;
   public: int Test() {return b.Val + 1;}
};

//+------------------------------------------------------------------+
//|                                                                                |
//+------------------------------------------------------------------+
void OnStart()
{
   B b;
   b.a.b = GetPointer(b);
   b.Val = 1;
   
   Print(b.a.Test());
}
But now we get an error when compiling:
undefined class 'A' cannot be used

I came up with two solutions to get round this error.

1. Through the base class:

class A0
{
   public: virtual int Test() {return 0;}
};

class B
{
   public: A0 * a;
   public: int Val;
};

class A: public A0
{
   public: A(B * source) {b = source;}
   public: B * b;
   public: virtual int Test() {return b.Val + 1;}
};

//+------------------------------------------------------------------+
//|                                                                                |
//+------------------------------------------------------------------+
void OnStart()
{
   B b;
   A a(GetPointer(b));
   
   b.a = GetPointer(a);
   b.Val = 1;
   
   Print(b.a.Test());
}

2. Through a nested class:

class B
{
   class A
   {
      public: B * b;
      public: int Test() {return b.Val + 1;}
   };

   public: A a;
   public: int Val;
};

//+------------------------------------------------------------------+
//|                                                                                |
//+------------------------------------------------------------------+
void OnStart()
{
   B b;
   b.a.b = GetPointer(b);
   b.Val = 1;
   
   Print(b.a.Test());
}

The second solution, to my mind, is more optimal than the first one.
 
Marat Sultanov #:
It used to be possible (even in older compiler builds) to use a class before it was declared:
But now we get an error when compiling:
undefined class 'A' cannot be used

If it stops working, it would be good to know if it is correct.

If you make pointers instead of objects, the old version will work too.

Reason: