※本記事では、「ファイルを読み込み、成否をbool型で返すLoadFile関数」があると仮定して解説する。
assertの概要
(Visual)C++のassertマクロは、エラー箇所で自動的に実行を停止することができる、便利なデバッグ用機能だ。
// ※LoadFile関数は、ファイルを読み込み、成否をbool型で返す関数とする
bool loadResult = LoadFile("config.txt");
// assertマクロ関数は、引数がtrueだったときのみ動作する
assert(loadResult);
//---------------------------------------
// assert出力結果抜粋:LoadFile(loadResult)
上記のソースではエラーメッセージとして分かりづらい。
そこで、assertの動作条件がtrueかfalseかであることを利用して、このような使い方もできる。
// ※LoadFile関数は、ファイルを読み込み、成否をbool型で返す関数とする
if (!LoadFile("config.txt"))
{
// assertマクロ関数は、引数がtrueだったときのみ動作する
assert(0 && "設定ファイル読み込み失敗");
}
//---------------------------------------
// assert出力結果抜粋:0 && "設定ファイル読み込み失敗"
実行中に作成した文字列をassertできない
さて、ここで本題である。
今回の例の場合、「読み込みに失敗したファイル名」をassert画面で表示したいときがあるだろう。
とりあえず文字列を渡しておけば良さそうに見えるので、思った通りに書いてみる。
// ※LoadFile関数は、ファイルを読み込み、成否をbool型で返す関数とする
std::string filename = "config.txt";
if (!LoadFile(filename))
{
std::string errorMsg = "読み込み失敗:" + filename;
assert(0 && errorMsg.c_str());
}
//---------------------------------------
// assert出力結果抜粋:0 && errorMsg.c_str()
errorMsg.c_str()の戻り値が展開されず、そのままソースコードの内容が展開されてしまった。
とりあえず解決方法
先にちゃんと動いてくれる例を提示しておく。
assertではなく、_wassertを使うことになる。
結論としては、使いやすい自作assert作るしかないんですね……
// ※LoadFile関数は、ファイルを読み込み、成否をbool型で返す関数とする
std::string filename = "config.txt";
if (!LoadFile(filename))
{
// エラーメッセージ
std::string errorMsg = "読み込み失敗:" + filename;
//ワイド文字列に変換
WCHAR* _wtext = new WCHAR[strlen(errorMsg.c_str()) + 1];
mbstowcs_s(nullptr, _wtext, strlen(errorMsg.c_str()) + 1, errorMsg.c_str(), _TRUNCATE);
// _wassertを使って、文字列出力
// (現在のファイルや行数は__FILE__マクロと__LINE__マクロで取得可能)
_wassert(_wtext, _CRT_WIDE(__FILE__), (unsigned)(__LINE__));
// ワイド文字列を解放
delete[] _wtext;
}
//---------------------------------------
// assert出力結果抜粋:読み込み失敗:config.txt
超絶簡易な解説
以下、上記のソースの簡易な解説。
assertマクロの中身
まずは、assertマクロが何故このような挙動をするのか、中身を見て確認してみよう。
#define assert(expression) (void)( \
(!!(expression)) || \
(_wassert(_CRT_WIDE(#expression), _CRT_WIDE(__FILE__), (unsigned)(__LINE__)), 0) \
)
ここで着目したい点は、以下の4点。
- 本質的には**「_wassert」関数を使ってassertを出力している**こと。
- _wassert関数の各引数の文字列は、「_CRT_WIDE」マクロ関数でワイド文字列に変換してから渡していること。
- assertマクロの引数expressionが、「#」でソースコード上の記述そのままで出すようになっていること。
- 該当する箇所のファイル名と行数を、マクロ「__FILE__」「__LINE__」で取得していること。
これを踏まえると、「自力で_wassert関数を呼び出す。ただし、文字列はワイド文字列に変換する必要がある。」ということだ。
ただし、ワイド文字列に変換する**「_CRT_WIDE」マクロ関数は、本質的には「文字列リテラルをの先頭に「L」を追加しているだけ」**である。
つまり、「_CRT_WIDE(errorMsg.c_str())」 のようにシンボル名を入れてしまうと「LerrorMsg.c_str()」としてコンパイルされることになり、コンパイルエラーが発生する。
すなわち、自分でワイド文字列に変換する処理を書いてあげる必要がある。
今回はmbstowcs_s関数を使用した。
結論
つまり、自作assert作れってことですね、ハイ。