- CharArrayToStruct関数の働き・役割
- CharArrayToStruct関数の引数について
- CharArrayToStruct関数の戻り値について
- CharArrayToStruct関数を使う際の注意点
- CharArrayToStruct関数を使ったサンプルコード
- コード内の解説
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_array
(uchar型の配列)にコピーします。
関数の戻り値は、コピーが成功した場合はtrue
、失敗した場合はfalse
です。
CopyRatesToCharArray関数の中で行われる具体的な処理は以下の通りです。
MqlRates rates[1];:
- MqlRates構造体の配列を1つ作成します。ここにレートデータを一時的に保存します。
uint start = sizeof(MqlRates);:
- MqlRates構造体のサイズ(バイト数)を取得し、それを
start
変数に格納します。
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(...):
- StringFormat関数を使用して、MqlRates構造体の各フィールドの値をフォーマットし、出力用の文字列を作成します。StringFormat
関数
は複数の引数を受け取り、それを指定された形式で文字列に変換します。
コメント