Caratteristiche del linguaggio mql5, sottigliezze e tecniche - pagina 234

 
fxsaber #:

Si misura la durata e si ottiene il risultato. Mi è successo con TRADE_ACTION_MODIFY.

Da dove, verso dove, cosa e in quali condizioni è stato misurato?

solo OrderSendAsync MODIFY e l'operazione immediatamente successiva è durata 5 secondi.

risultato molto strano, spaventoso e improbabile - dobbiamo ricontrollare i test.

 
Maxim Kuznetsov #:

da dove, verso dove, cosa e in quali condizioni è stato misurato?

solo OrderSendAsync MODIFY e all'operazione immediatamente successiva 5 sec ????

risultato molto strano, spaventoso, improbabile - dobbiamo ricontrollare i test

Ho misurato il tempo prima e dopo la funzione, ho calcolato la differenza e ho ottenuto 5 secondi. Nei consiglieri di combattimento si misura tutto, in modo da avere più informazioni per risolvere una situazione anomala. Ho visto questo.

 
Una struttura vuota fornisce ai discendenti l'operatore di assegnazione appropriato.
struct BASE {};

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

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

Era necessario creare strutture che potessero ricevere regole di azione diverse al loro interno, ma che potessero essere manipolate tra loro come se fossero identiche.

La tecnica utilizzata è stata formalizzata in questo esempio.

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
}


Purtroppo non ho capito perché i linguaggi OOP non hanno interfacce per i metodi statici.

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

Devo creare questo orrore.

#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 metodi statici.

Come lo immaginate?

Ogni funzione ha il suo indirizzo nel segmento .text.

Ogni funzione membro (metodo) accetta implicitamente questo puntatore come primo parametro.

I metodi statici non accettano questo puntatore e sono essenzialmente uno "zucchero" sintattico, essendo, di fatto, funzioni ordinarie.

Quando si chiama una funzione virtuale, l'indirizzo della funzione eseguibile viene preso dalla tabella delle funzioni virtuali, il cui puntatore è implicitamente contenuto nella classe in cui la funzione virtuale è dichiarata. L'inizializzazione del puntatore alla funzione eseguibile avviene al momento della creazione di un'istanza dell'oggetto; la logica è la seguente (la scrivo in mql apposta perché sia chiara a tutti i neofiti:

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

Naturalmente, nella realtà, non è tutto così, ma il meccanismo di inizializzazione del puntatore alla funzione è esattamente questo. Di conseguenza, non c'è modo, a partire dalla parola "affatto", di farlo nel modo desiderato in un linguaggio compilato.

Il C++ ha questa magia dei template:

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

Qualsiasi funzione ha il suo indirizzo nel segmento .text.

Ogni funzione membro (metodo) accetta implicitamente questo puntatore come primo parametro.

I metodi statici non accettano questo puntatore e sono essenzialmente uno "zucchero" sintattico, essendo di fatto funzioni ordinarie.

Quando si chiama una funzione virtuale, l'indirizzo della funzione eseguibile viene preso dalla tabella delle funzioni virtuali, il cui puntatore è implicitamente contenuto nella classe in cui la funzione virtuale è dichiarata. L'inizializzazione del puntatore alla funzione eseguibile avviene al momento della creazione di un'istanza dell'oggetto; la logica è la seguente (la scrivo in mql per renderla chiara a tutti i neofiti:

Naturalmente, nella realtà, non è così, ma il meccanismo di inizializzazione del puntatore alla funzione è esattamente questo.

Grazie per la spiegazione dettagliata con un esempio!

 
Vladimir Simakov #:

Qual è la sua idea in merito?

Se ti riferisci al mio rammarico per le possibilità delle interfacce. Voglio imporre solo restrizioni sintattiche alle classi/strutture. Cioè, solo in fase di compilazione, come avviene con lo stesso modificatore const. Per l'autocontrollo, insomma.

Non c'è modo, dalla parola "affatto", di fare quello che si vuole in un linguaggio compilato.

Sopra ho scritto una stampella. Volevo avere qualcosa di già costruito per questi casi.

 

Forum sul trading, sui sistemi di trading automatizzati e sulla verifica delle strategie di trading

Nuova versione di MetaTrader 5 build 3950: prelievo/riempimento nel terminale e rapporto di trading aggiornato

fxsaber, 2023.09.19 23:25

Come eliminare gli errori?
#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 prima volta __LINE__/__COUNTER__ vengono passati all'interno del markos come testo, la seconda volta come numeri.

 
Probabilmente ha inventato una bicicletta, non lo so. Se qualcuno conosce soluzioni più eleganti, è pregato di condividerle.

In precedenza (nelle vecchie versioni del compilatore) era possibile utilizzare una classe prima della sua dichiarazione:
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());
}
Ma ora si ottiene un errore in fase di compilazione:
classe non definita 'A' non può essere usata

Ho trovato due soluzioni per aggirare l'errore.

1. Attraverso la classe 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. Attraverso una classe annidata:

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 seconda soluzione, a mio avviso, è più ottimale della prima.
Motivazione: