Merkmale der Sprache mql5, Feinheiten und Techniken - Seite 234

 
fxsaber #:

Sie messen die Dauer und erhalten das Ergebnis. Ich hatte dies auf TRADE_ACTION_MODIFY passieren.

Von wo, bis wo, was und unter welchen Bedingungen wurde gemessen?

nur OrderSendAsync MODIFY und bei der unmittelbar folgenden Operation 5 Sekunden ?

sehr seltsames, beängstigendes, unwahrscheinliches Ergebnis - wir müssen die Tests doppelt überprüfen

 
Maxim Kuznetsov #:

von wo, wohin, was und unter welchen Bedingungen gemessen wurde?

nur OrderSendAsync MODIFY und zum sofortigen nächsten Vorgang 5 sec ????

sehr seltsames, erschreckendes, unwahrscheinliches Ergebnis - wir müssen die Tests noch einmal überprüfen

Ich habe die Zeit vor und nach der Funktion gemessen, die Differenz berechnet und 5 Sekunden erhalten. Bei Kampfberatern wird alles gemessen, damit es mehr Informationen gibt, um eine anormale Situation zu klären. Ich habe das gesehen.

 
Bei einer leeren Struktur erhalten die Nachkommen den entsprechenden Zuweisungsoperator.
struct BASE {};

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

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

Es war notwendig, Strukturen zu schaffen, die in ihrem Inneren mit unterschiedlichen Aktionsregeln versehen werden konnten, aber untereinander als identisch manipuliert werden konnten.

Die verwendete Technik wurde an diesem Beispiel formalisiert.

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
}


Leider habe ich nicht verstanden, warum OOP-Sprachen keine Schnittstellen für statische Methoden haben.

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

Ich muss einen solchen Horror schaffen.

#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 statische Methoden haben.

Wie kann man sich das vorstellen?

Jede Funktion hat ihre Adresse im .text-Segment.

Jede Mitgliedsfunktion (Methode) akzeptiert implizit diesen Zeiger als ihren ersten Parameter.

Statische Methoden akzeptieren diesen Zeiger nicht und sind im Wesentlichen syntaktischer "Zucker", da sie de facto normale Funktionen sind.

Beim Aufruf einer virtuellen Funktion wird die Adresse der ausführbaren Funktion der Tabelle der virtuellen Funktionen entnommen, deren Zeiger implizit in der Klasse enthalten ist, in der die virtuelle Funktion deklariert ist. Die Initialisierung des Zeigers auf die ausführbare Funktion erfolgt bei der Erstellung einer Instanz des Objekts, die Logik ist wie folgt (ich schreibe es absichtlich in mql, damit es für alle Neulinge verständlich ist:

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

Natürlich ist in der Realität nicht alles so, aber der Mechanismus der Initialisierung des Zeigers auf die Funktion ist genau so. Dementsprechend gibt es in einer kompilierten Sprache überhaupt keine Möglichkeit, das so zu machen, wie man es will.

C++ hat eine solche Vorlagenmagie:

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

Jede Funktion hat ihre Adresse im .text-Segment.

Jede Mitgliedsfunktion (Methode) akzeptiert implizit diesen Zeiger als ihren ersten Parameter.

Statische Methoden akzeptieren diesen Zeiger nicht und sind im Wesentlichen syntaktischer "Zucker", da sie de facto normale Funktionen sind.

Beim Aufruf einer virtuellen Funktion wird die Adresse der ausführbaren Funktion aus der Tabelle der virtuellen Funktionen entnommen, deren Zeiger implizit in der Klasse enthalten ist, in der die virtuelle Funktion deklariert ist. Die Initialisierung des Zeigers auf die ausführbare Funktion erfolgt bei der Erstellung einer Instanz des Objekts, die Logik ist wie folgt (ich schreibe es in mql, um es für alle Neulinge verständlich zu machen:

In Wirklichkeit ist es natürlich nicht so, aber der Mechanismus der Initialisierung des Zeigers auf die Funktion ist genau so.

Vielen Dank für die ausführliche Erklärung mit einem Beispiel!

 
Vladimir Simakov #:

Wie stellst du dir das vor?

Wenn Sie von meinem Bedauern über die Möglichkeiten von Schnittstellen sprechen. Ich möchte den Klassen/Strukturen nur syntaktische Beschränkungen auferlegen. Das heißt, nur in der Kompilierungsphase, wie es mit dem gleichen const-Modifikator geschieht. Für die Selbstkontrolle, kurz gesagt.

In einer kompilierten Sprache gibt es keine Möglichkeit, das zu tun, was Sie wollen.

Ich habe oben eine Krücke geschrieben. Ich wollte etwas, das für solche Fälle bereits eingebaut ist.

 

Forum zum Thema Handel, automatische Handelssysteme und Testen von Handelsstrategien

Neue Version von MetaTrader 5 build 3950: Abhebung/Ausfüllung im Terminal und aktualisierter Handelsbericht

fxsaber, 2023.09.19 23:25

Wie wird man die Fehler los?
#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__);
}

Beim ersten Mal werden __LINE__/__COUNTER__ innerhalb der Markos als Text übergeben, beim zweiten Mal - Zahlen.

 
Wahrscheinlich hat er ein Fahrrad erfunden, ich weiß es nicht. Wenn jemand elegantere Lösungen kennt, bitte mitteilen.

Früher (in alten Compiler-Builds) war es möglich, eine Klasse vor ihrer Deklaration zu verwenden:
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());
}
Aber jetzt bekommen wir einen Fehler beim Kompilieren:
undefinierte Klasse 'A' kann nicht verwendet werden

Ich habe zwei Lösungen gefunden, um diesen Fehler zu umgehen.

1. Über die Basisklasse:

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. Über eine verschachtelte Klasse:

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

Die zweite Lösung ist meiner Meinung nach optimaler als die erste.