Skip to content

Instantly share code, notes, and snippets.

@hidsh
Last active February 12, 2021 10:42
Show Gist options
  • Save hidsh/3f4a0d0e205c7b13eb43e8e3e6a5305e to your computer and use it in GitHub Desktop.
Save hidsh/3f4a0d0e205c7b13eb43e8e3e6a5305e to your computer and use it in GitHub Desktop.
MQL4では参照を返せないっぽいので、ポインタを返すテストプログラム。fixできるが、再発防止という点でイマイチ。他にいいやり方はないのか。
//+------------------------------------------------------------------+
//| obj-pointer-test-bugggggg.mq4 |
// バグっているコード。
// 参照のつもりでコンテナ内のメンバ変数を書き換えようとしても、書き換えたのは参照先ではなくコピーしたオブジェクトのほうなので、
// メンバ変数は書き換えできない。--> 32行目付近
//+------------------------------------------------------------------+
#property strict
class TObj {
int m_;
public:
TObj() :m_(0) {}
TObj(const TObj& o) :m_(o.m_) {Print("コピーコンストラクタ");}
int get_m() {return m_;}
void set_m(int val) {m_ = val;}
};
class TCont {
TObj arr[1];
public:
TCont() {arr[0] = TObj();}
TObj peep() {return arr[0];}
};
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
TCont cont;
void OnStart()
{
TObj o = cont.peep();
printf("o.get_m: %d cont.peep().get_m(): %d", o.get_m(), cont.peep().get_m());
o.set_m(99); // バグ!! 参照のつもりで元の値を変更しようとしている
printf("o.get_m: %d cont.peep().get_m(): %d", o.get_m(), cont.peep().get_m());
/*
o.get_m: 99 cont.peep().get_m(): 0 // 2回目 TObjのコピーのメンバに代入しているだけなので、元の配列内のTObjは変更されない
コピーコンストラクタ // コピーコンストラクタが呼ばれているので参照ではなく、新しい実体にメンバをコピーしている
o.get_m: 0 cont.peep().get_m(): 0 // 初回
*/
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| obj-pointer-test-fixed.mq4 |
//
// 一応直したコード。
// だが、以下2点の問題があるので、ポインタ変数宣言時にうっかり "*" をつけ忘れただけでですぐバグる危険がある。
//
// 1. MQLでは参照を(引数には渡せるが、)返り値として返すことができないので、返すときはGetPoiinter()でポインタを使う必要がある。--> 29行目
// 2. コンパイラが型チェックをしないので、オブジェクト型の変数にポインタが代入できてしまう--> 49行目(コピーコンストラクタでコピーを作る)→ やはりバグる。
// 3. コピーコンストラクタを無効にするためにprivateにすると、コンパイルできない。--> 16行目(コピーコンストラクタを明示的に書かないと自動的に作られて、1と同じ結果になる)
//
//+------------------------------------------------------------------+
#property strict
class TObj {
int m_;
// TObj(const TObj& o) :m_(o.m_) {} // コピーコンストラクタをprivateにして無効化しようとすると、コンパイルエラーが出る。"TObj::TObj' - cannot access private member function"
public:
TObj() :m_(0) {}
TObj(const TObj& o) :m_(o.m_) {Print("警告!コピーコンストラクタは危ないので使用禁止!");} // MQLでは、コピーコンストラクタをprivateにすることでコピーコンストラクタを無効化することもできないので、とりあえずエラーログを吐かすしかないか。
int get_m() {return m_;}
void set_m(int val) {m_ = val;}
};
class TCont {
TObj arr[1];
public:
TCont() {arr[0] = TObj();}
// TObj& peep() {return arr[0];} // 参照を返そうとすると、コンパイルエラーになる "'&' - reference cannot used"
TObj* peep() {return GetPointer(arr[0]);} // <-- しかたないので、ポインタを返す
};
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
TCont cont;
void OnStart()
{
TObj* po = cont.peep(); // ポインタの代入なので、コピーコンストラクタは使われない
printf("po.get_m: %d cont.peep().get_m(): %d", po.get_m(), cont.peep().get_m());
po.set_m(99);
printf("po.get_m: %d cont.peep().get_m(): %d", po.get_m(), cont.peep().get_m());
/*
po.get_m: 99 cont.peep().get_m(): 99 // 2回目 意図通り、元の配列内のTObjが変更されている。
po.get_m: 0 cont.peep().get_m(): 0 // 初回 この行の次の行に「コピーコンストラクタ」と出力されていないので、コピーコンストラクタが呼ばれていないことがわかる
*/
TObj o = cont.peep(); // これはTObj型の変数にポインタを代入しているが、MQLではコンパイルエラーにならない(当然C++では型チェックでコンパイルエラーになる)
o.set_m(1111); // で、しれっとバグるので注意!
printf("o.get_m: %d cont.peep().get_m(): %d", o.get_m(), cont.peep().get_m());
/*
o.get_m: 1111 cont.peep().get_m(): 99 // やっぱりバグる!!
*/
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| obj-pointer-test.cpp |
// ちなみに、C++ でどうなるかやってみたコード。やはりコンパイルエラーになる --> 40行目
//+------------------------------------------------------------------+
// #property strict
#include <stdio.h>
#define Print printf
class TObj {
int m_;
public:
TObj() :m_(0) {}
TObj(const TObj& o) :m_(o.m_) {Print("コピーコンストラクタ");}
int get_m() {return m_;}
void set_m(int val) {m_ = val;}
};
class TCont {
TObj arr[1];
public:
TCont() {arr[0] = TObj();}
TObj* peep() {return &arr[0];} // <-- ポインタを返す (C++)
};
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
TCont cont;
void OnStart()
{
TObj* po = cont.peep(); // ポインタの代入なので、コピーコンストラクタは使われない
printf("po->get_m: %d cont.peep().get_m(): %d", po->get_m(), cont.peep()->get_m());
po->set_m(99);
printf("po->get_m: %d cont.peep()->get_m(): %d", po->get_m(), cont.peep()->get_m());
/*
*/
TObj o = cont.peep(); // <-- g++ では当然エラー "error: conversion from ‘TObj*’ to non-scalar type ‘TObj’ requested"
o.set_m(1111);
printf("o.get_m: %d cont.peep().get_m(): %d", o.get_m(), cont.peep().get_m());
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| obj-pointer-test-mq5.mq5 |
// ちなみに、MQL5 でどうなるかやってみたコード。MQL4とまったく同じ動作で同じくバグる。希望なし。MQL6はまだか。
//+------------------------------------------------------------------+
class TObj {
int m_;
public:
TObj() :m_(0) {}
TObj(const TObj& o) :m_(o.m_) {Print("警告!コピーコンストラクタは危ないので使用禁止!");} // MQLでは、コピーコンストラクタをprivateにすることでコピーコンストラクタを無効化することもできないので、とりあえずエラーログを吐かすしかないか。
int get_m() {return m_;}
void set_m(int val) {m_ = val;}
};
class TCont {
TObj arr[1];
public:
TCont() {arr[0] = TObj();}
TObj* peep() {return GetPointer(arr[0]);} // <-- ポインタを返す (MQLでは参照を返せないので、ポインタを使う)
};
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
TCont cont;
void OnStart()
{
TObj* po = cont.peep(); // ポインタの代入なので、コピーコンストラクタは使われない
printf("po.get_m: %d cont.peep().get_m(): %d", po.get_m(), cont.peep().get_m());
po.set_m(99);
printf("po.get_m: %d cont.peep().get_m(): %d", po.get_m(), cont.peep().get_m());
/*
po.get_m: 99 cont.peep().get_m(): 99 // 2回目 意図通り、元の配列内のTObjが変更されている。
po.get_m: 0 cont.peep().get_m(): 0 // 初回 この行の次の行に「コピーコンストラクタ」と出力されていないので、コピーコンストラクタが呼ばれていないことがわかる
*/
TObj o = cont.peep(); // これはTObj型の変数にポインタを代入しているが、MQLではコンパイルエラーにならない(当然C++ではコンパイルエラーになる)
o.set_m(1111); // で、しれっとバグるので注意!
printf("o.get_m: %d cont.peep().get_m(): %d", o.get_m(), cont.peep().get_m());
/*
o.get_m: 1111 cont.peep().get_m(): 99 // やっぱりバグる!!
*/
}
//+------------------------------------------------------------------+
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment