Características del lenguaje mql5, sutilezas y técnicas - página 234

 
fxsaber #:

Mides la duración y obtienes el resultado. Me pasó esto en TRADE_ACTION_MODIFY.

¿desde dónde, hasta dónde, qué y en qué condiciones se midió?

sólo OrderSendAsync MODIFY y a la operación inmediatamente siguiente 5 seg.

resultado muy extraño, aterrador, improbable - tenemos que volver a comprobar las pruebas

 
Maxim Kuznetsov #:

¿desde dónde, hasta dónde, qué y en qué condiciones se midió?

sólo OrderSendAsync MODIFY y a la operación inmediatamente siguiente 5 seg ????

muy extraño, aterrador, improbable resultado - tenemos que volver a comprobar las pruebas

Medí el tiempo antes y después de la función, calculé la diferencia y obtuve 5 segundos. En los asesores de combate todo se mide, por lo que hay más información para resolver una situación anormal. Vi esto.

 
Una estructura vacía dota a los descendientes del operador de asignación apropiado.
struct BASE {};

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

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

Era necesario crear estructuras a las que se pudieran aplicar distintas reglas de actuación en su interior, pero que se manipularan entre sí como idénticas.

La técnica utilizada se formalizó en este ejemplo.

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
}


Por desgracia, no entendía por qué los lenguajes de programación orientada a objetos no tienen interfaces para los métodos estáticos.

interface A
{
public:
  static void Func() {} // 'Func' - cannot be declared static
};
 
fxsaber métodos estáticos.

Tengo que crear tal 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 métodos estáticos.

¿Cómo te lo imaginas?

Cualquier función tiene su dirección en el segmento .text.

Cualquier función miembro (método), implícitamente acepta este puntero como su primer parámetro.

Los métodos estáticos no aceptan este puntero y son esencialmente "azúcar" sintáctico, siendo, de facto, funciones ordinarias.

Cuando se llama a una función virtual, la dirección de la función ejecutable se toma de la tabla de funciones virtuales, cuyo puntero está implícitamente contenido en la clase en la que se declara la función virtual. La inicialización del puntero a la función ejecutable se produce al crear una instancia del objeto, la lógica es así (lo escribo en mql a propósito para que quede claro a todos los neófitos:

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, en la realidad, no todo es así, pero el mecanismo de inicialización del puntero a la función es exactamente así. En consecuencia, no hay manera, de la palabra "en absoluto", para hacerlo de la manera que desee en un lenguaje compilado.

C++ tiene esa magia de las plantillas:

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

Cualquier función tiene su dirección en el segmento .text.

Cualquier función miembro (método) acepta implícitamente este puntero como primer parámetro.

Los métodos estáticos no aceptan este puntero y son esencialmente "azúcar" sintáctico, siendo de facto funciones ordinarias.

Cuando se llama a una función virtual, la dirección de la función ejecutable se toma de la tabla de funciones virtuales, cuyo puntero está implícitamente contenido en la clase en la que se declara la función virtual. La inicialización del puntero a la función ejecutable se produce al crear una instancia del objeto, la lógica es así (lo escribo en mql para que quede claro a todos los neófitos:

Naturalmente en la realidad no es así, pero el mecanismo de inicialización del puntero a la función es exactamente así.

¡Gracias por la explicación detallada con un ejemplo!

 
Vladimir Simakov #:

¿Cuál es tu idea al respecto?

Si te refieres a mi lamento sobre las posibilidades de las interfaces. Quiero imponer sólo restricciones sintácticas a las clases/estructuras. Es decir, sólo en la etapa de compilación, como ocurre con el mismo modificador const. Por autocontrol, en definitiva.

No hay manera, de la palabra "en absoluto", para hacer lo que quieres en un lenguaje compilado.

Escribí una muletilla arriba. Quería tener algo ya incorporado para estos casos.

 

Foro sobre negociación, sistemas automatizados de negociación y ensayo de estrategias de negociación

Nueva versión de MetaTrader 5 build 3950: retirada/relleno en el terminal y actualización del informe comercial

fxsaber, 2023.09.19 23:25

¿Cómo deshacerse de los errores?
#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 primera vez __LINE__/__COUNTER__ se pasan dentro de los markos como texto, la segunda vez - números.

 
Probablemente inventó una bicicleta, no lo sé. Si alguien conoce soluciones más elegantes, por favor compártalas.

Anteriormente (en compiladores antiguos) era posible usar una clase antes de su declaración:
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());
}
Pero ahora obtenemos un error al compilar:
no se puede usar la clase indefinida 'A

Se me ocurrieron dos soluciones para evitar este error.

1. A través de la clase 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. A través de una clase anidada:

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 segunda solución, en mi opinión, es más óptima que la primera.
Razón de la queja: