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セグメントにそのアドレスを持っている。

メンバ関数(メソッド)は、暗黙のうちにこのポインタを最初のパラメータとして受け取ります。

静的メソッドはこのポインターを受け取らず、本質的に構文上の「砂糖」であり、事実上普通の関数である。

仮想関数が呼び出されると、実行関数のアドレスは仮想関数テーブルから取得され、そのポインタは仮想関数が宣言されたクラスに暗黙のうちに含まれています。実行可能関数へのポインタの初期化は、オブジェクトのインスタンスを生成するときに行われる:

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セグメントにある。

メンバ関数(メソッド)は、暗黙のうちにこのポインタを最初のパラメータとして受け取ります。

スタティック・メソッドはこのポインターを受け取らず、実質的に構文上の「砂糖」であり、事実上普通の関数です。

仮想関数が呼び出されると、実行関数のアドレスは仮想関数テーブルから取得され、そのポインタは仮想関数が宣言されたクラスに暗黙的に含まれています。実行可能関数へのポインタの初期化は、オブジェクトのインスタンスを生成するときに行われる:

当然、現実にはこのようにはならないが、関数へのポインタの初期化のメカニズムはまさにこのようなものである。

例を挙げて詳しく説明してくれてありがとう!

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

1回目は__LINE__/__COUNTER__をテキストとして、2回目は数値としてマーコス内に渡します。

 
おそらく自転車を発明したのだろう。もっとエレガントな解決策を知っている人がいたら、ぜひ教えてほしい。

以前は(古いコンパイラのビルドでは)宣言の前にクラスを使うことができました:
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' は使用できません。

このエラーを回避するために、2つの解決策を思いついた。

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

私の考えでは、2つ目の解決策は1つ目の解決策よりも最適である。
理由: