#pragma twice

KAB-studio > プログラミング > #pragma twice > 117 Version 6.17 ASSERT の網

#pragma twice 117 Version 6.17 ASSERT の網

前のページへ 表紙・目次へ 次のページへ

 Version 6.17
ASSERT の網

前回は気に掛ける範囲を狭める方法を紹介しました
変数の使える範囲を小さくするんだよね
そうでした。前回はコンパイルエラーにする方法だったけど、今回はラン
タイムエラーにする方法
へ? ランタイムエラーはまずいんじゃないの?
そうじゃなくて、まずいことが起きたときに、それが発見されないのはま
ずいでしょ
だからそれをランタイムエラーにする?
そゆこと。そこで使うのが、 ASSERT() ってマクロ
アサート? マクロなの?
ま、とりあえず使ってみましょう

void CDebugDlg::OnButton1() 
{
    ASSERT( 1 );
}

……
……
……なんも起きないよ?
そう、何も起きません
なにそれ
何も起きないのは、引数が 1 だから。これを 0 にすると……

void CDebugDlg::OnButton1() 
{
    ASSERT( 0 );
}

うぎゃっ、ヤバそうなダイアログが出たっ!!

Microsoft Visual C++ Debug Library

Debug Assertion Failed!

Program: C:\PROGRAM\DEBUG\DEBUG\DEBUG.EXE
File: C:\Program\Debug\DebugDlg.cpp
Line: 180

For information on how your program can cause an assertion
failure, see the Visual C++ documentation on asserts.

(Press Retry to debug the application)
[中止][再試行][無視]

なんか危なっかしいエラーが……
これが、 ASSERT() の機能
へ?
 ASSERT() は引数が 0 の場合に、こういうダイアログを出して、実行を
一時停止させるんです。だからこれは全然安全な状態
つまりヤバくはない?
そゆこと。たとえば【無視】ボタンを押すと、何事もなかったように一時
停止が解除されるから
あ、普通になった
【中止】は実行を停止、[再試行]は ASSERT() が発生した部分を表示しま

そう言えば、このダイアログにも ASSERT() 呼んでる部分が出てるよね
そう、 File と Line が ASSERT() を呼んでるソースファイルと行番号。
これを使っても ASSERT() が呼ばれた場所が分かるわけ
でも、これの使い道って?
これを使って、バグを網にかけます
網に?
そう。たとえば……こんな感じ

void CDebugDlg::OnButton1() 
{
    CString cStr = "ABCDE";
    cStr.SetAt( -1, 'Z' );
}

ほい。あ、 ASSERT() のダイアログが出た
この CString::SetAt() は、 CString の中の文字列の、単語1個を置き
換えるメンバ関数。第1引数の位置に、第2引数の文字を置きます
って、じゃー第1引数に -1 ってまずいんじゃない? あ、だから!
そゆこと。【再試行】ボタンで、 ASSERT() 呼んでる場所を出して

// MFC のソースコードが置いてある場所、 Strcore.cpp
void CString::SetAt(int nIndex, TCHAR ch)
{
    ASSERT(nIndex >= 0);
// 以下略。

あ、 ASSERT() だ。でもなんか if みたい
そう、 ASSERT() はほとんど if と同じように使います。ただ、 if と
違って、停止する条件が 0 、つまり 0 が当たりってこと
 if は 0 以外が当たり、ってことは正反対? 確かに nIndex >= 0 って
ことは、これが当たってたら ASSERT() に引っかからないんだもんねぇ
だから ASSERT() には〈うまくいく条件〉を書き込む、って憶えて
それ以外なら ASSERT() になるよー、ってこと?
そゆこと。 ASSERT(nIndex >= 0) なら、 nIndex が 0 以上なら問題な
し、そうじゃないなら
こーゆーダイアログが出るんだ
ここで重要なのは、みっつ。まずは、こういうふうに〈あっちゃいけない
場合〉を拾うように使います
確かに、文字列の位置でマイナスはあっちゃいけないもんね
たとえば火美ちゃんがこのメンバ関数を作ろうとしてたその瞬間。引数に
文字の位置を渡してもらおう、って時に、その〈マイナスはあっちゃいけな
い〉に気付いたら、即 ASSERT() を設置!
あっちゃいけない、って思ったら即置く?
そゆこと。こういうふうに関数を作る時、引数やメンバ変数で〈この変数
の中身、こういう値の時はうまく動かない〉って場合があったら
そういう値が来た時に網にかかるように ASSERT() 設置!
そういうこと! よく使う場面は、まず NULL チェック
 NULL ってポインタのだよね
そう。ポインタを受け取ったら ASSERT( pcDate ) とかって
これで引っかかるの?
 NULL は 0 、でしょ
そういえば Ver 5.11 ( No.076 ) でやったね。で、 ASSERT() は 0 で当
たりだから
そゆこと。他に if や switch で〈来ないはず〉のとこに設置するとか
どゆこと?
これは例を見た方がいいかも

void GetNumber( int p_i )
{
    switch( p_i )
    {
    case 1:
        TRACE( "1 です\n" );
        break;
    case 2:
        TRACE( "2 です\n" );
        break;
    default:
        // ここに来ることはあり得ない!
        ASSERT( 0 );
    }
}

つまり引数に 1 と 2 以外が来たら ASSERT() ?
そういうこと。こういうふうに、来ちゃいけない場所に ASSERT( 0 ) 
っていうのもひとつの方法
なんか面白いかも
この辺も、前回言った〈気に掛ける範囲〉を狭めることだからね
そっか、あり得ない値とかを ASSERT() に掛けちゃえばそれは気に掛けな
くていいんだ
そゆこと。さて重要なポイントその2。 ASSERT() は起きる可能性がある
ことには使っちゃダメ
起きる可能性があること?
たとえばファイルを fopen() で開く時。それが成功したかどうか、とか
はダメだから
そりゃファイルがない場合って多いよね。でもなんで ASSERT() じゃダメ
なの?
まず、 ASSERT() に掛かると停止しちゃうってこと
そっか、それこそ〈使ってたらフリーズ!〉ってことになっちゃうんだ
言い換えると、 ASSERT() はプログラマー向け、ってこと。プログラマー
が〈あっちゃいけないこと〉を取り除くためだけに使うようにしてね
ユーザー向けじゃないんだ
それに、最終的には ASSERT() って機能しなくなっちゃうし
どゆことそれ?
今言ったように、ユーザーにとっては ASSERT() はなんの意味もないか
ら、公開する時に無効化するんだよね
かえってなんか不安……
って、そういうエラーには、普通に if で対処して、場面場面で適切な処
理をしておけばいいわけ
そりゃそうだね
この〈無効化する〉って話は次回にね。で、重要ポイントのみっつめ、そ
れは〈気付かなきゃしょうがない〉ってこと
つまり〈あっちゃいけない〉に気付かなきゃ意味なしってことね
 ASSERT() ってプログラムを組みながら設置してくものだから、気が付か
なかったり、そもそもまずい状況を知らなかったりしたら、使う場面がなく
なっちゃうでしょ
そりゃそうだ。さっきの文字の位置なんかも、忘れたりしそう……
それにテストも重要。さっき言ったようにリリースしたあとは ASSERT()
しちゃまずいでしょ
ってことは、普通に使ってたら ASSERT() する状況はないってことね
で、テストを念入りにして、その状況がない! ってことを確認しないと
いけないわけ
そっか、なんか変なことして ASSERT() する状況になったらまずいんだ
もしそれが見つかったら、 ASSERT() しないようにうまくする必要があり
ます。 ASSERT() に引っかからないような値にするとか、 ASSERT() 以外の
方法で処理するとか
〈 ASSERT() 以外の方法〉は可能性のある状況、ってことよね
そゆこと。テストして ASSERT() したら、それは普通に使ってあり得るこ
となのか、もしくはプログラムミスが原因で ASSERT() するような値にし
ちゃってるか、のどちらか
でもそれの区別って難しそう……
そうだね、この辺はかなり難しい話になるかも。最初は分かりやすい場面
に絞って使った方がいいかもね
さっきの CString::SetAt() みたいにね
そゆこと。さて最後に、 ASSERT() は自分で起こせるものってことをちゃ
んと憶えておいてね
そーいえば、あーゆーダイアログ出たら、なんかヤバイことで起きたよう
に思っちゃうよね
そう思っちゃうけど、実は全然そうじゃないってこと。あとで ASSERT()
の中身見ておくといいかも。見ても難しいと思うけど、普通のプログラム
だって思えると
怖くなくなるもんね
で、 ASSERT() は MFC で無茶苦茶多用されてます
さっきの CString も MFC のクラスだもんね
 MFC を使ってると ASSERT() のダイアログが出てくること多いけど、出
てきたらそれは間違った値渡したり使い方が変だったりって理由
つまり注意されてるだけ、直せばいいんだよね
そういうこと!  MFC で ASSERT() が出ても、慌てず騒がず原因を探し
ましょう

/*
    Preview Next Story!
*/
って、 MFC の中見ても全然わかんないんだけど
確かにねー、 MFC って複雑だから自分じゃ解決できないかも
げ、そゆもん?
深いとこで発生すると、原因が分かんないことも……
水希ちゃんが言うんだから、かなり大変なんだね……
というわけで次回
< Version 6.18 デバッグビルドとリリースビルド再び >
につづく!
ま、人に訊いたりするときは大事でしょ
そだね、なんかわけ分かんないエラー、とかじゃないんだもんね
掲示板とかで訊くときは ASSERT() ならそうはっきり書きましょう!
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。