Caractéristiques du langage mql5, subtilités et techniques - page 234

 
fxsaber #:

Vous mesurez la durée et obtenez le résultat. C'est ce qui s'est passé avec TRADE_ACTION_MODIFY.

D'où, à où, quoi et dans quelles conditions la durée a-t-elle été mesurée ?

juste OrderSendAsync MODIFY et jusqu'à l'opération suivante immédiate, 5 secondes ? ??

résultat très étrange, effrayant, improbable - nous devons revérifier les tests.

 
Maxim Kuznetsov #:

d'où, à où, à quoi et dans quelles conditions a-t-elle été mesurée ?

juste OrderSendAsync MODIFY et à l'opération suivante immédiate 5 sec ? ???

résultat très étrange, effrayant, improbable - nous devons revérifier les tests

J'ai mesuré le temps avant et après la fonction, j'ai calculé la différence et j'ai obtenu 5 secondes. Les conseillers au combat mesurent tout, afin de disposer de plus d'informations pour résoudre une situation anormale. J'ai vu ça.

 
Une structure vide dote les descendants de l'opérateur d'affectation approprié.
struct BASE {};

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

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

Il fallait créer des structures auxquelles on pouvait donner des règles d'action différentes à l'intérieur, mais qui se manipulaient entre elles de manière identique.

La technique utilisée a été formalisée sur cet exemple.

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
}


Malheureusement, je n'ai pas compris pourquoi les langages OOP n'ont pas d'interfaces pour les méthodes statiques.

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

Je dois créer une telle horreur.

#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 méthodes statiques.

Comment l'imaginez-vous ?

Toute fonction a son adresse dans le segment .text.

Toute fonction membre (méthode) accepte implicitement ce pointeur comme premier paramètre.

Les méthodes statiques n'acceptent pas ce pointeur et sont essentiellement du "sucre" syntaxique, étant, de facto, des fonctions ordinaires.

Lorsqu'une fonction virtuelle est appelée, l'adresse de la fonction exécutable est tirée de la table des fonctions virtuelles, dont le pointeur est implicitement contenu dans la classe dans laquelle la fonction virtuelle est déclarée. L'initialisation du pointeur sur la fonction exécutable se fait lors de la création d'une instance de l'objet, la logique est la suivante (je l'écris en mql volontairement pour que ce soit clair pour tous les néophytes :

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

Naturellement, dans la réalité, tout n'est pas comme ça, mais le mécanisme d'initialisation du pointeur sur la fonction est exactement comme ça. En conséquence, il n'y a aucun moyen, à partir du mot "du tout", de le faire comme vous le souhaitez dans un langage compilé.

Le C++ dispose d'une telle magie des modèles :

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

Toute fonction a son adresse dans le segment .text.

Toute fonction membre (méthode) accepte implicitement ce pointeur comme premier paramètre.

Les méthodes statiques n'acceptent pas ce pointeur et sont essentiellement du "sucre" syntaxique, étant de facto des fonctions ordinaires.

Lorsqu'une fonction virtuelle est appelée, l'adresse de la fonction exécutable est tirée de la table des fonctions virtuelles, dont le pointeur est implicitement contenu dans la classe dans laquelle la fonction virtuelle est déclarée. L'initialisation du pointeur sur la fonction exécutable se fait lors de la création d'une instance de l'objet, la logique est la suivante (je l'écris en mql pour que ce soit clair pour tous les néophytes :

Naturellement, dans la réalité, ce n'est pas comme ça, mais le mécanisme d'initialisation du pointeur sur la fonction est exactement comme ça.

Merci pour cette explication détaillée accompagnée d'un exemple !

 
Vladimir Simakov #:

Qu'en pensez-vous ?

Si vous parlez de mon regret concernant les possibilités des interfaces, je veux imposer uniquement des restrictions syntaxiques aux classes/structures. Je ne veux imposer que des restrictions syntaxiques aux classes/structures. C'est à dire uniquement à la compilation, comme c'est le cas avec le même modificateur const. Pour l'autocontrôle, en somme.

Il n'y a aucun moyen, à partir du mot "du tout", de faire ce que vous voulez dans un langage compilé.

J'ai écrit une béquille plus haut. Je voulais avoir quelque chose de déjà intégré pour de tels cas.

 

Forum sur le trading, les systèmes de trading automatisés et les tests de stratégies de trading

Nouvelle version de MetaTrader 5 build 3950 : Retrait/Remplissage dans le terminal et mise à jour du rapport de trading

fxsaber, 2023.09.19 23:25

Comment se débarrasser des erreurs ?
#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__);
}

La première fois, __LINE__/__COUNTER__ sont passés à l'intérieur des markos sous forme de texte, la seconde fois sous forme de nombres.

 
Il a probablement inventé une bicyclette, je ne sais pas. Si quelqu'un connaît des solutions plus élégantes, merci de les partager.

Auparavant (dans les anciennes versions du compilateur), il était possible d'utiliser une classe avant sa déclaration :
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());
}
Mais maintenant nous obtenons une erreur lors de la compilation :
la classe indéfinie 'A' ne peut pas être utilisée.

J'ai trouvé deux solutions pour contourner cette erreur.

1. Passer par la classe de base :

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. Par une classe imbriquée :

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

La deuxième solution est, à mon avis, plus optimale que la première.
Raison: