mql5语言的特点、微妙之处以及技巧 - 页 234

 
fxsaber #:

您测量持续时间并得到结果。我在TRADE_ACTION_MODIFY 上遇到过这种情况。

从哪里、到哪里、在什么条件下测量?

只是 OrderSendAsync MODIFY 和紧接着的下一个操作 5 秒?

非常奇怪、可怕、不可能的结果 - 我们需要重复检查测试

 
Maxim Kuznetsov #:

从哪里、到哪里、在什么条件下测量的?

只需 OrderSendAsync MODIFY 并立即进行下一次操作 5 秒 ????

非常奇怪、可怕、不可能的结果 - 我们需要仔细检查测试结果

我测量了函数执行前后的时间,计算了差值,结果是 5 秒。在作战顾问的工作中,一切都要经过测量,这样才能有更多的信息来理清异常情况。看到这个。

 
空结构赋予后代适当的赋值运算符
struct BASE {};

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

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

我们有必要创建一些结构,这些结构内部可以有不同的操作规则,但它们之间的操作规则是相同的。

在这个例子中,我们正式确定了所使用的技术。

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
}


遗憾的是,我不明白为什么 OOP 语言没有静态方法 接口。

interface A
{
public:
  static void Func() {} // 'Func' - cannot be declared static
};
 
fxsaber 静态方法 接口。

我必须创造这样一个恐怖的东西。

#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 静态方法 接口。

你是如何想象的?

任何函数在 .text 段中都有自己的地址。

任何成员函数(方法)都隐含地接受这个指针作为它的第一个参数。

静态方法不接受 this 指针,本质上是语法上的 "糖",事实上是普通函数。

调用虚函数时,可执行函数的地址取自虚函数表,该表的指针隐含在声明虚函数的类中。可执行函数指针的初始化是在创建对象实例时进行的,其逻辑是这样的(我故意用 mql 来写,以便所有新手都能理解):

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

当然,实际情况并非如此,但函数指针的初始化机制正是如此。因此,从 "根本 "这个词来看,在编译语言中根本无法按照你想要的方式进行初始化。

C++ 就有这样的模板魔法:

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

任何函数的地址都在 .text 段中。

任何成员函数(方法)都隐含地接受该指针作为其第一个参数。

静态方法不接受这个指针,本质上是语法上的 "糖",是事实上的普通函数。

调用虚函数时,可执行函数的地址取自虚函数表,该表的指针隐含在声明虚函数的类中。可执行函数指针的初始化是在创建对象实例时进行的,其逻辑是这样的(我用 mql 写出来是为了让所有新手都能明白):

当然,实际情况并非如此,但函数指针的初始化机制正是如此。

谢谢你的详细解释和举例说明!

 
Vladimir Simakov #:

你怎么看?

如果你说的是我对接口可能性的遗憾。我只想对类/结构施加语法限制。也就是说,只在编译阶段,就像使用相同的 const 修饰符一样。简而言之,就是为了自我控制。

从 "根本 "一词来看,在编译语言中根本无法实现你的愿望。

我在上面写了一个拐杖。我想为这种情况提供一些内置功能。

 

关于交易、自动交易系统和测试交易策略的论坛

新版 MetaTrader 5 第 3950 版:终端提款/填表和更新的交易报告

fxsaber, 2023.09.19 23:25

如何消除错误?
#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__);
}

The first time __LINE__/__COUNTER__ are passed inside the markos as text, the second time - numbers.

 
也许是发明了自行车,我不知道。如果有人知道更优雅的解决方案,请与我们分享。

以前(在旧版编译器中)可以在类声明之前使用类:
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());
}
但现在编译时会出错:
不能使用未定义的类'A

我想了两个办法来解决这个错误。

1.通过基类:

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.通过嵌套类:

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

在我看来,第二种解决方案比第一种更为理想。