Особенности языка mql5, тонкости и приёмы работы - страница 234

 
fxsaber #:

Замеряете длительность и получаете результат. У меня случилось на TRADE_ACTION_MODIFY.

откуда, до куда, что и в каких условиях измерялось ? 

просто OrderSendAsync MODIFY и до непосредственно следующей операции 5 сек ??

очень странный, пугающий, неправдоподобный результат - надо перепроверять тесты

 
Maxim Kuznetsov #:

откуда, до куда, что и в каких условиях измерялось ? 

просто OrderSendAsync MODIFY и до непосредственно следующей операции 5 сек ??

очень странный, пугающий, неправдоподобный результат - надо перепроверять тесты

Замерил время до и после функции, посчитал разницу и получил 5 секунд. В боевых советниках все замеряется, чтобы потом было больше информации разобрать нештатную ситуацию. Увидел это.

 
Пустая структура наделяет потомков соответствующим оператором присваивания.
struct BASE {};

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

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

Понадобилось создать структуры, которым можно было бы задавать различные правила действий внутри, но при этом манипулировать ими между собой, как одинаковыми.

Примененный прием оформил на таком примере.

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
}


К сожалению, не понял, почему в ООП-языках нет интерфейсов для статических методов.

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

К сожалению, не понял, почему в ООП-языках нет интерфейсов для статических методов.

Приходится лепить такой ужас.

#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 #:

Понадобилось создать структуры, которым можно было бы задавать различные правила действий внутри, но при этом манипулировать ими между собой, как одинаковыми.

Примененный прием оформил на таком примере.


К сожалению, не понял, почему в ООП-языках нет интерфейсов для статических методов.

А как ты это представляешь?

Любая функция имеет свой адрес в сегменте .text.

Любая функция-член (метод), первым параметром неявно принимает указатель this.

Статические методы не принимают указатель this и, по сути своей, являются синтаксическим "сахаром", являясь, де-факто обычными функциями.

При вызове виртуальной функции, адрес исполняемой функции берется из таблицы виртуальных функций, указатель на которую неявно содержится в классе, в котором объявлена виртуальная функция. Инициализация указателя на исполняемую функцию происходит при создании экземпляра объекта, логика при этом примерно такая (специально, что бы всем неофитам понятно было, на mql пишу:

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();
}

Естественно, реально там все не так, но механизм инициализации указателя на функцию именно такой. Соответственно, никак, от слова вообще, невозможно, в компилируемом ЯП, сделать так как ты хочешь.

В C++ вот такая шаблонная магия есть:

#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 #:

Любая функция имеет свой адрес в сегменте .text.

Любая функция-член (метод), первым параметром неявно принимает указатель this.

Статические методы не принимают указатель this и, по сути своей, являются синтаксическим "сахаром", являясь, де-факто обычными функциями.

При вызове виртуальной функции, адрес исполняемой функции берется из таблицы виртуальных функций, указатель на которую неявно содержится в классе, в котором объявлена виртуальная функция. Инициализация указателя на исполняемую функцию происходит при создании экземпляра объекта, логика при этом примерно такая (специально, что бы всем неофитам понятно было, на mql пишу:

Естественно, реально там все не так, но механизм инициализации указателя на функцию именно такой.

Спасибо за подробное объяснение с примером!

 
Vladimir Simakov #:

А как ты это представляешь?

Если речь про мое сожалению по поводу возможностей интерфейсов. То мне хочется исключительно синтаксическое ограничение навесить на классы/структуры. Т.е. только на этапе компиляции, как это происходит с тем же модификатором const. Для самоконтроля, короче.

никак, от слова вообще, невозможно, в компилируемом ЯП, сделать так как ты хочешь.

Выше написал костыль. Хотел получить нечто уже встроенное для таких случаев.

 

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Новая версия платформы MetaTrader 5 build 3950: Снятие/пополнение в терминале и обновленный торговый отчет

fxsaber, 2023.09.19 23:25

Как избавиться от ошибок?
#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__);
}

Первый раз __LINE__/__COUNTER__ передаются внутри маркоса в виде текста, второй - числа.

 
Возможно, изобрел велосипед, не знаю. Если кто-то знает более элегантные решения, то прошу поделиться.

Раньше(еще в старых билдах компилятора) можно было использовать класс до его объявления:
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());
}
Но теперь при компиляции получаем ошибку:
undefined class 'A' cannot be used

Придумал два решения, как это можно обойти.

1. Через базовый класс:

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. Через вложенный класс:

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());
}

Второе решение, как по мне, оптимальнее чем первое.
Причина обращения: