【MQL5】CharArrayToStruct関数について

MQL5リファレンス
スポンサーリンク
スポンサーリンク

CharArrayToStruct関数の働き・役割

CharArrayToStruct関数は、uchar型配列に格納されているデータをPOD(Plain Old Data)構造体にコピーするための関数です。

これにより、「バイト配列メモリ上の連続したバイト(byte)の集まり)を構造体に変換し、より構造化されたデータ形式として利用することができます。

この関数は特に、バイナリデータ(人間が直接理解できない形式のデータ)を効率的に操作する際に役立ちます。

PODとは?

POD(Plain Old Data)は、基本的なデータ型のみを含む単純な構造体を指します。

これは、C++やMQL5などのプログラミング言語でよく使われる概念です。
POD(Plain Old Data構造体にはポインタ(メモリ上のアドレスを指す変数)や動的配列などの複雑なデータ型を含まないため、メモリ上で連続した領域に配置され、バイナリ形式でのコピーや変換が非常に簡単です。
例えば、以下のような構造体POD(Plain Old Data)構造体の一例です。

struct MyStruct {
    int id;
    float value;
    char label;
};

このような構造体は、他の形式への変換や低レベルのメモリ操作を簡素化するため、特にシステムプログラミングや効率を重視する場面でよく使用されます。

構造体についての詳細は↓の記事をご参照ください。

CharArrayToStruct関数の引数について

CharArrayToStruct関数は次の引数を取ります。

bool CharArrayToStruct(
    void& struct_object,       // 構造体
    const uchar& char_array[], // 配列
    uint start_pos=0           // 配列の開始位置
);

第一引数struct_objectです。これは任意のPOD(Plain Old Data)構造体への参照(構造体自体を直接操作するためのもの)です。
この構造体にデータがコピーされます。

※参照という概念にピンとこない方は、下記の参照渡しに関する記事をご確認ください。

第二引数char_arrayです。これはuchar型配列です。この配列にはコピーされるデータが含まれています。

第三引数start_posです。これはuint型で、データコピーを開始する配列内の位置を指定します。初期値は0です。

CharArrayToStruct関数の戻り値について

CharArrayToStruct関数は、データのコピーが成功した場合はtrueを、失敗した場合はfalseを返します。失敗時には、GetLastError関数を使用してエラーコードを取得し、原因を特定することができます。

GetLastError関数についての詳細は↓の記事をご参照ください。

CharArrayToStruct関数を使う際の注意点

POD(Plain Old Data)構造体の使用が必要です。CharArrayToStruct関数は、ポインタや動的配列などの複雑なデータ型を含む構造体には使用できません。

配列サイズの確認が重要です。コピー元であるuchar型配列が十分なサイズを持っていることを確認してください。不足している場合、CharArrayToStruct関数の処理は失敗します。

CharArrayToStruct関数を使ったサンプルコード

以下は、CharArrayToStruct関数を使って簡単なデータを構造体にコピーするサンプルコードです。このコードでは、uchar型である配列のデータを既存のMqlRates構造体にコピーします。

// サンプルコード内で使用する定数を定義
#define DATA_TOTAL 4

// 配列を定義
MqlRates ExtDataRates[DATA_TOTAL]; // MqlRates型の構造体配列
uchar    ExtCharArray[sizeof(MqlRates) * DATA_TOTAL]; // uchar型の配列

// スクリプトの開始時に呼び出される関数
void OnStart()
{
    ResetLastError(); // エラーコードをリセット

    // uchar配列にデータをコピー
    for(int i = 0; i < DATA_TOTAL; i++)
    {
        if(!CopyRatesToCharArray(i, ExtCharArray))
            return; // 失敗した場合は終了
    }

    // uchar配列から構造体にデータをコピー
    for(int i = 0; i < DATA_TOTAL; i++)
    {
        ResetLastError(); // エラーコードをリセット
        if(!CharArrayToStruct(ExtDataRates[i], ExtCharArray, sizeof(MqlRates) * i))
        {
            Print("CharArrayToStruct() failed. Error code: ", GetLastError());
            return; // 失敗した場合は終了
        }
        // 構造体のデータをエキスパートログに出力
        MqlRatesPrint(ExtDataRates[i]);
    }
}

// uchar配列にデータをコピーする関数
bool CopyRatesToCharArray(const int index, uchar &data_array[])
{
    MqlRates rates[1]; // MqlRates構造体の配列
    uint     start = sizeof(MqlRates); // MqlRates構造体のサイズ

    ResetLastError(); // エラーコードをリセット

    // 指定されたインデックスのレートデータをコピー
    if(CopyRates(Symbol(), PERIOD_CURRENT, index, 1, rates) != 1)
    {
        Print("CopyRates() failed. Error code: ", GetLastError());
        return false; // 失敗
    }

    // 構造体からuchar配列にデータをコピー
    if(!StructToCharArray(rates[0], data_array, start * index))
    {
        Print("StructToCharArray() failed. Error code: ", GetLastError());
        return false; // 失敗
    }

    return true; // 成功
}

// MqlRates構造体の内容をエキスパートログに出力する関数
void MqlRatesPrint(MqlRates &rates)
{
    // 出力する文字列を作成
    string text = StringFormat(" Time = %s;\n"
                               " Open = %.*f;\n"
                               " High = %.*f;\n"
                               " Low = %.*f;\n"
                               " Close = %.*f;\n"
                               " Tick volume = %I64u;\n"
                               " Spread = %d;\n"
                               " Real volume = %I64u",
                               TimeToString(rates.time),  // 時刻を文字列に変換
                               _Digits, rates.open,       // 始値
                               _Digits, rates.high,       // 高値
                               _Digits, rates.low,        // 安値
                               _Digits, rates.close,      // 終値
                               rates.tick_volume,         // ティックボリューム
                               rates.spread,              // スプレッド
                               rates.real_volume);        // 実ボリューム

    // エキスパートログにデータを出力
    Print("MqlRates data:\n", text);
}

コード内の解説

今回のコードは少し難しいので、初学者の方向けに上から順番に解説していきます。

プログラム内で使用する定数を定義

// サンプルコード内で使用する定数を定義
#define DATA_TOTAL 4

定数というのは、プログラム内で変更されない値のことです。
定数についての詳細は↓の記事をご参照ください


#defineは、定数を定義するために使われる命令です。この例では、DATA_TOTALという名前の定数を作り、その値を4にしています。これにより、プログラムの中でDATA_TOTALを使うと、常に4という値が参照されます。

define命令についての詳細は↓の記事リンクをご参照ください。

配列を定義

// 配列を定義
MqlRates ExtDataRates[DATA_TOTAL]; // MqlRates型の構造体配列
uchar    ExtCharArray[sizeof(MqlRates) * DATA_TOTAL]; // uchar型の配列

MqlRates構造体配列であるExtDataRatesを定義しています。
この配列は4つのMqlRates構造体を持ちます。先ほど定義した定数DATA_TOTALが4であるため、この配列のサイズは4になります。

uchar型(符号なし文字型)の配列であるExtCharArrayを定義しています。
これはMqlRates構造体のサイズに4を掛けた大きさの配列です。

sizeof(MqlRates)はMqlRates構造体のサイズ(バイト数)を返します。
これにDATA_TOTAL(4)を掛けることで、この配列は4つのMqlRates構造体バイト単位で格納できるだけの大きさを持つことになります。

sizeof演算子についての詳細は↓の記事をご参照ください。

OnStart関数を呼び出す

// スクリプトの開始時に呼び出される関数
void OnStart()

OnStart関数スクリプトが実行されるときに最初に呼び出される関数です。
スクリプトのエントリーポイントとも言えます。この関数内に記述されたコードは、スクリプトの実行が開始されると同時に実行されます。MQL5スクリプトでは、OnStart関数を使ってスクリプトが開始されたときに行いたい処理を記述します。

ResetLastError関数を呼び出す

ResetLastError(); // エラーコードをリセット

ResetLastError関数は以前に発生したエラー情報をクリアします。プログラムの中でエラーが発生した場合、そのエラーコードはシステムに保存されます。

ResetLastError関数を使うことで、過去のエラー情報をリセットし、新しいエラーが発生したときに正確なエラー情報を取得できるようにします。

uchar配列にデータをコピー

 // uchar配列にデータをコピー
    for(int i = 0; i < DATA_TOTAL; i++)
    {
        if(!CopyRatesToCharArray(i, ExtCharArray))
            return; // 失敗した場合は終了
    }

この部分のコードは、uchar型配列にデータをコピーするためのループ処理を行っています。

このループは、定数DATA_TOTAL(ここでは4)の回数だけ繰り返されます。ループ内では、CopyRatesToCharArrayというオリジナルで作った関数を呼び出して、MqlRatesのデータをuchar型配列にコピーしています。※CopyRatesToCharArray関数の挙動については後程説明します。

uchar配列から構造体にデータをコピー

// uchar配列から構造体にデータをコピー
for(int i = 0; i < DATA_TOTAL; i++)
{
    ResetLastError(); // エラーコードをリセット
    if(!CharArrayToStruct(ExtDataRates[i], ExtCharArray, sizeof(MqlRates) * i))
    {
        Print("CharArrayToStruct() failed. Error code: ", GetLastError());
        return; // 失敗した場合は終了
    }
    // 構造体のデータをエキスパートログに出力
    MqlRatesPrint(ExtDataRates[i]);
}

この部分のコードは、uchar型配列のデータを構造体にコピーするためのループ処理を行っています

このループは、定数DATA_TOTAL(ここでは4)の回数だけ繰り返されます。ループ内では、CharArrayToStruct関数を呼び出して、ExtCharArray配列のデータをMqlRates構造体にコピーしています。

for(int i = 0; i < DATA_TOTAL; i++) は、変数iを0からDATA_TOTALの値まで1ずつ増やしながら繰り返すループを作成します。つまり、ここでは4回繰り返します。

ResetLastError() は、エラーコードをリセットします。新しいエラーが発生した場合に正確なエラー情報を取得するためです。

CharArrayToStruct(ExtDataRates[i], ExtCharArray, sizeof(MqlRates) * i) は、uchar型配列からMqlRates構造体にデータをコピーする関数を呼び出します。

CharArrayToStruct関数の第3引数記述「sizeof(MqlRates) * i」は、「MqlRates構造体のサイズ(バイト数)に、iの値(ここでは0から3)をかけた値」です。これは、ExtCharArray配列内でコピーするデータの開始位置を示しています。

具体的には次のようになります。

  • i = 0 のとき、sizeof(MqlRates) * 0 = 0。これはExtCharArray配列の最初からデータをコピーすることを意味します。
  • i = 1 のとき、sizeof(MqlRates) * 1。これはExtCharArray配列MqlRates構造体1つ分のバイト数の位置からデータをコピーすることを意味します。
  • i = 2 のとき、sizeof(MqlRates) * 2。これはExtCharArray配列MqlRates構造体2つ分のバイト数の位置からデータをコピーすることを意味します。
  • i = 3 のとき、sizeof(MqlRates) * 3。これはExtCharArray配列MqlRates構造体3つ分のバイト数の位置からデータをコピーすることを意味します。

このようにして、配列内の異なる位置から構造体データを順番にコピーすることができます。

ここでは、配列ExtCharArrayのデータをExtDataRates[i]にコピーしています。コピーが失敗した場合は false を返すため、その場合にはエラーメッセージを出力して関数の実行を終了します。

MqlRatesPrint(ExtDataRates[i]) は、コピーされた構造体のデータをエキスパートログに出力する関数を呼び出します。各構造体の内容がログに表示されます。

メインプログラムで使われているCopyRatesToCharArray関数について

// uchar配列にデータをコピーする関数
bool CopyRatesToCharArray(const int index, uchar &data_array[])
{
    MqlRates rates[1]; // MqlRates構造体の配列
    uint     start = sizeof(MqlRates); // MqlRates構造体のサイズ

    ResetLastError(); // エラーコードをリセット

    // 指定されたインデックスのレートデータをコピー
    if(CopyRates(Symbol(), PERIOD_CURRENT, index, 1, rates) != 1)
    {
        Print("CopyRates() failed. Error code: ", GetLastError());
        return false; // 失敗
    }

    // 構造体からuchar配列にデータをコピー
    if(!StructToCharArray(rates[0], data_array, start * index))
    {
        Print("StructToCharArray() failed. Error code: ", GetLastError());
        return false; // 失敗
    }

    return true; // 成功
}

この関数(CopyRatesToCharArray関数)は、指定されたインデックスindex)のレートデータを取得し、そのデータをdata_arrayuchar型配列)にコピーします。
関数戻り値は、コピーが成功した場合はtrue、失敗した場合はfalseです。

CopyRatesToCharArray関数の中で行われる具体的な処理は以下の通りです。

MqlRates rates[1];:

uint start = sizeof(MqlRates);:

if(CopyRates(Symbol(), PERIOD_CURRENT, index, 1, rates) != 1):

  • 指定されたシンボルと期間のレートデータを取得し、rates配列にコピーします。取得が失敗した場合はエラーメッセージを出力し、falseを返します。

※処理に使われているCopyRates関数についての詳細は↓の記事をご参照ください。

if(CopyRates(Symbol(), PERIOD_CURRENT, index, 1, rates) != 1):

if(!StructToCharArray(rates[0], data_array, start * index)):

  • rates配列のデータをdata_arrayにコピーします。コピーが失敗した場合はエラーメッセージを出力し、falseを返します。

return true;:

  • 全ての処理が成功した場合にtrueを返します。

メインプログラムで使われているMqlRatesPrint関数について

// MqlRates構造体の内容をエキスパートログに出力する関数
void MqlRatesPrint(MqlRates &rates)
{
    // 出力する文字列を作成
    string text = StringFormat(" Time = %s;\n"
                               " Open = %.*f;\n"
                               " High = %.*f;\n"
                               " Low = %.*f;\n"
                               " Close = %.*f;\n"
                               " Tick volume = %I64u;\n"
                               " Spread = %d;\n"
                               " Real volume = %I64u",
                               TimeToString(rates.time),  // 時刻を文字列に変換
                               _Digits, rates.open,       // 始値
                               _Digits, rates.high,       // 高値
                               _Digits, rates.low,        // 安値
                               _Digits, rates.close,      // 終値
                               rates.tick_volume,         // ティックボリューム
                               rates.spread,              // スプレッド
                               rates.real_volume);        // 実ボリューム

    // エキスパートログにデータを出力
    Print("MqlRates data:\n", text);
}

この関数は、MqlRates構造体のデータを読み取り、それを人間が読みやすい形式でエキスパートログに出力します。レートの時間、開値、高値、安値、終値、ティックボリューム、スプレッド、および実ボリュームを出力します。

string text = StringFormat(...):

Print("MqlRates data:\n", text);:

  • Print関数を使用して、作成した文字列をエキスパートログに出力します。Print関数は、指定されたメッセージをログに出力するための関数です。

コメント

タイトルとURLをコピーしました