Características da linguagem mql5, subtilezas e técnicas - página 234

 
fxsaber #:

Você mede a duração e obtém o resultado. Isso aconteceu em TRADE_ACTION_MODIFY.

De onde, para onde, o que e em que condições foi medido?

apenas OrderSendAsync MODIFY e para a operação imediatamente seguinte, 5 segundos ???

resultado muito estranho, assustador e improvável - precisamos verificar novamente os testes

 
Maxim Kuznetsov #:

De onde, para onde, o que e sob quais condições foi medido?

apenas OrderSendAsync MODIFY e a próxima operação imediata 5 segundos ????

resultado muito estranho, assustador e improvável - precisamos verificar novamente os testes

Medi o tempo antes e depois da função, calculei a diferença e obtive 5 segundos. Nos consultores de combate, tudo é medido, para que haja mais informações para resolver uma situação anormal. Vi isso.

 
Uma estrutura vazia concede aos descendentes o operador de atribuição apropriado.
struct BASE {};

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

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

Era necessário criar estruturas que pudessem receber diferentes regras de ações em seu interior, mas que fossem manipuladas entre si como idênticas.

A técnica usada foi formalizada neste exemplo.

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
}


Infelizmente, eu não entendia por que as linguagens OOP não têm interfaces para métodos estáticos.

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

Tenho que criar esse 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.

Como você imagina isso?

Qualquer função tem seu endereço no segmento .text.

Qualquer função membro (método) aceita implicitamente esse ponteiro como seu primeiro parâmetro.

Os métodos estáticos não aceitam esse ponteiro e são essencialmente "açúcar" sintático, sendo, de fato, funções comuns.

Quando uma função virtual é chamada, o endereço da função executável é retirado da tabela de funções virtuais, cujo ponteiro está implicitamente contido na classe em que a função virtual é declarada. A inicialização do ponteiro para a função executável ocorre ao criar uma instância do objeto, e a lógica é a seguinte (escrevi em mql de propósito para que ficasse claro para todos os 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, na realidade, nem tudo é assim, mas o mecanismo de inicialização do ponteiro para a função é exatamente assim. Portanto, não há como, a partir da palavra "de forma alguma", fazer isso da forma desejada em uma linguagem compilada.

O C++ tem essa mágica de modelo:

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

Qualquer função tem seu endereço no segmento .text.

Qualquer função membro (método) aceita implicitamente esse ponteiro como seu primeiro parâmetro.

Os métodos estáticos não aceitam esse ponteiro e são essencialmente "açúcar" sintático, sendo de fato funções comuns.

Quando uma função virtual é chamada, o endereço da função executável é retirado da tabela de funções virtuais, cujo ponteiro está implicitamente contido na classe em que a função virtual é declarada. A inicialização do ponteiro para a função executável ocorre durante a criação de uma instância do objeto, e a lógica é a seguinte (escrevo em mql para deixar claro para todos os neófitos:

Naturalmente, na realidade, não é assim, mas o mecanismo de inicialização do ponteiro para a função é exatamente assim.

Obrigado pela explicação detalhada com um exemplo!

 
Vladimir Simakov #:

Qual é a sua ideia sobre isso?

Se estiver se referindo ao meu pesar sobre as possibilidades das interfaces. Quero impor apenas restrições sintáticas a classes/estruturas. Ou seja, somente no estágio de compilação, como acontece com o mesmo modificador const. Para autocontrole, em resumo.

Não há como, a partir da palavra "de forma alguma", fazer o que você deseja em uma linguagem compilada.

Eu escrevi uma muleta acima. Eu queria ter algo já incorporado para esses casos.

 

Fórum sobre negociação, sistemas de negociação automatizados e teste de estratégias de negociação

Nova versão do MetaTrader 5 build 3950: Retirada/Preenchimento no terminal e relatório de negociação atualizado

fxsaber, 2023.09.19 23:25

Como se livrar dos erros?
#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__);
}

Na primeira vez, __LINE__/__COUNTER__ são passados dentro dos marcadores como texto e, na segunda vez, como números.

 
Provavelmente inventou uma bicicleta, não sei. Se alguém conhecer soluções mais elegantes, por favor, compartilhe.

Anteriormente (em compilações antigas do compilador), era possível usar uma classe antes de sua declaração:
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());
}
Mas agora recebemos um erro ao compilar:
undefined class 'A' cannot be used

Encontrei duas soluções para contornar esse erro.

1. Por meio da 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. Por meio de uma classe aninhada:

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

A segunda solução, na minha opinião, é mais otimizada do que a primeira.