MQL4の関数でreturnは基本的に1つだけですが、複数の戻り値を返したい場合があります
そういう場合は、参照渡しというやり方で、構造体やクラスを使うことで可能にできます
値渡しと参照渡し
MQL4の関数で使われる引数にはvalue、値渡しとrerence、参照渡しの2種類あります
但し、例外として、配列や構造体、クラスオブジェクトに引数を入れる場合は
参照渡しのみになります
値渡しは、そのパラメータの値をそのまま引数に渡すのに対して
参照渡しは、そのパラメータが格納してあるメモリアドレスを渡します
渡された方はそのアドレスを辿るとその値が確認できるわけです
(ポインターとの違いは初期化などの制約が厳しい)
関数などで何らかの処理がなされそのパラメータに変化があった場合に
関数外ではそのアドレスを辿れば変化した値が分かるわけです
値渡し
例えば値渡しのケースですが、
2つのパラメータにそれぞれ値をシフト+5をさせる場合このようにできます
TestSeveralReturns.mq4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
//●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● // TestSeveralReturns //●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● int param1 = 0; int param2 = 0; int count=0; int OnInit(){ return(INIT_SUCCEEDED); } void OnDeinit(const int reason){ } //●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● // OnTick() //●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● void OnTick(){ // only 1 task if(count==0){ param1 = 3; param2 = 5; param1 = FShift01(param1); param2 = FShift01(param2); Print("param1 =" + IntegerToString(param1)); Print("param2 =" + IntegerToString(param2)); count=1; } } int FShift01(int param){ // shift 5 param = param + 5; return param; } |
ストラテジーテスターでテストしてみると
param1, param2 がそれぞれ+5のシフトがなされているのを確認できます
関数を2回繰り返していますが、パラメータが2つくらいならいいですが
数が多くなったりパラメータの型が変わる場合はとても煩雑になります
参照渡し
このシフト+5を参照渡しで組んでみます
関数の引数にパラメータを入れて、関数側ではその「&」パラメータを参照するアドレスを入れるようにします
1 2 3 4 5 6 7 |
FShift02(param1, param2); ... void FShift02(int &par1, int &par2){ // shift 5 par1 = par1 + 5; par2 = par2 + 5; } |
まとめると
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
... int param1 = 0; int param2 = 0; int count=0; ... //●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● // OnTick() //●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● void OnTick(){ // only 1 task if(count==0){ param1 = 3; param2 = 5; FShift02(param1, param2); Print("param1 =" + IntegerToString(param1)); Print("param2 =" + IntegerToString(param2)); count=1; } } void FShift02(int &par1, int &par2){ // shift 5 par1 = par1 + 5; par2 = par2 + 5; } |
結果は値渡しと同じように、+5シフトした値になっています
蛇足:「&」の呼び方はよくアンパサンドと表記されていますが、綴りは Ampersand なので
アンパーサント[ǽmpərsæ`nd]と言ってましたけどね(欧州人には)
配列の参照渡し
引数が2つならなんとかできますが、6つになると冗長的にならざるえません
その場合は配列にして参照渡しにすれば簡単にできます
配列を使った参照渡しです
1 |
int arrTestA[6] = {1, 2, 3, 4, 5, 6}; |
シフト+5を実行する関数
1 2 3 4 5 6 |
void FShift03(int &arrTestB[]){ for(int i = 0; i < ArrayRange(arrTestB, 0); i++){ // shift 5 arrTestB[i] = arrTestB[i] + 5; } } |
TestSeveralReturns.mq4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
... int arrTestA[6] = {1, 2, 3, 4, 5, 6}; int count=0; ... //●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● // OnTick() //●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● void OnTick(){ // only 1 task if(count==0){ FShift03(arrTestA); for(int i = 0; i < ArrayRange(arrTestA, 0); i++){ Print("arrTestA["+IntegerToString(i)+"] =" + IntegerToString(arrTestA[i])); } count=1; } } void FShift03(int &arrTestB[]){ for(int i = 0; i < ArrayRange(arrTestB, 0); i++){ // shift 5 arrTestB[i] = arrTestB[i] + 5; } } |
ストラテジーテスターでテストしてみると
arrTestAの各要素が+5のシフトがなされているのが確認できます
構造体の参照渡し
引数のパラメータが同じ型であれば配列でいいのですが、異なる型の場合は(struct)構造体で作成します
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
... struct paramSet01{ int paraInt; double paraDouble; string paraString; }; int count=0; ... //●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● // OnTick() //●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● void OnTick(){ // only 1 task if(count==0){ paramSet01 ps1; ps1.paraInt = 4; ps1.paraDouble = 2.2; ps1.paraString = "before"; FShift04(ps1); Print("ps1.paraInt =" + IntegerToString(ps1.paraInt)); Print("ps1.paraDouble =" + DoubleToString(ps1.paraDouble)); Print("ps1.paraString =" + ps1.paraString); count=1; } } void FShift04(paramSet01 ¶mS){ paramS.paraInt = paramS.paraInt + 5; paramS.paraDouble = paramS.paraDouble/2; paramS.paraString = "after"; } |
それぞれのパラメータが処理されているのが分かります
クラスの参照渡し
クラスの引数も参照渡しです
簡単な配列を渡す例です
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
... int arrSet[3] = {1, 2, 3}; int count=0; ... //●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● // OnTick() //●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● void OnTick(){ // only 1 task if(count==0){ FSclass fs; fs.FShift05(arrSet); for(int i = 0; i < ArrayRange(arrSet, 0); i++){ Print("arrSet["+IntegerToString(i)+"] =" + IntegerToString(arrSet[i])); } count=1; } } class FSclass{ private: int par1; public: void FShift05(int &as[]){ par1 = 5; as[0] = as[0] + par1; as[1] = as[1] + par1; as[2] = as[2] + par1; } }; |
この例は配列とあまり変わりません
クラスを使う時は参照渡しであることを考慮しないといけません