2009年10月05日

フリーズ対策

ソフトウェア開発に幸せな未来はあるのか: 第15話 コーディング規約は必要か?


本文とはさほど関係ないのだが、
if(flag == true)
{
  // 処理1
}
else if(flag == false)
{
  // 処理2
}
else
{
  // 処理3
}
を見て思ったことをつらつらと書く。

あ、その前に、↑のはアホすぎだな(筆者が、ではなく、こんなことを本当に書くヤツが)。flagがbool型である以上、trueとfalseの2つしか取りえない。筆者は、
どう考えても「処理3」は通りません。
(中略)
true/falseしか条件がないのに3つ処理が用意されている事の方が不自然です。
と書いているが、俺的に不要なのは1か2のどっちかだと思うけどなぁ。


さて、昔のファミコンのゲームって、本体を蹴飛ばすとかコントローラを激しく引っ張る(結果、本体が動く)とかでもしない限り、フリーズはしなかったように思う。しかし、現代のゲームは、実によくフリーズする。これはなぜなのだろう。

あくまで、俺のプログラマとしての経験だが、大型のプロジェクトに初めて関わったとき、以下のようなコーディング規約があった。
 Debug_Assert (System/debug.h) を使う.

 「この変数は...だから,...だよな」というような「前提」がある場合には積極的にアサートを使ってください.前提が崩れた状態で処理を続行すると,問題の発生地点から根本原因にたどり着くのに余分な時間を取られます.リリースビルド時にはなくなりますので最終的な実行速度には影響しません.

 ただし,あくまでも「絶対にありえない」条件に対して記述すべきものです,「例外」とは違います.リリースビルド時にはなくなります.
で、俺がコレを勘違いして使っていたのが、以下のような時だ。
switch( theFlag )
{
 case 1:  // Menu
  inWork->mDivide  = eMatrix_Divide_Menu;
  STATE_CHANGE( ( u8 )eMatrix_Divide_Menu_State_Init );
  break;

 case 2:  // Transform_Start
  inWork->mDivide  = eMatrix_Divide_TransformStart;
  STATE_CHANGE( ( u8 )eMatrix_Divide_TransformStart_State_Init );
  break;

 case 0:  // Transform_End
  inWork->mDivide  = eMatrix_Divide_TransformEnd;
  STATE_CHANGE( ( u8 )eMatrix_Divide_TransformEnd_State_Init );
  break;

 default:
  DEBUG_ASSERT( false );
  break;
}
STATE_CHANGEマクロは、場面を次へ進めるマクロだ。もし、このままこの状態でリリースされてしまい、かつ、何らかのバグないしは暴走などによって、theFlag の値が3以上になったとき、おそらく、この関数を何度も通り、結果、無限ループ≒フリーズする。

とりあえず何とかするならば、
 default:
  DEBUG_ASSERT( false );
  STATE_CHANGE( ( u8 )eMatrix_Divide_Menu_State_Init ); // case1 と同じように進める。
  break;
とでもしておくのが無難だろう。

Assertは、
あくまでも「絶対にありえない」条件に対して記述すべきもの
だ。開発中にバグの原因を突き止めるための便利な命令に過ぎない。しかしながら、条件式の中で、起こりうる値を無視するってのもどうか。理論上、起こらないハズだが、自分の担当箇所以外の何らかの要因があれば、起こりうる。他人が参照する関数ならば尚更だ。対策を置いておけば、少なくともフリーズという憂き目は防げるのではないか、などと思った。


有名だが、こんな話がある。
ファミリーコンピュータ版 ドラゴンクエスト3
普通にプレイすると、HP自動回復の恩恵でどうやっても死なないキングヒドラだが、万が一、億が一にもオルテガが勝ってしまった場合の処理が実装されていた。RPGにおいて、負けることによって話が進むケースはしばしばあるが、もしも勝ってしまったらどうなるのか・・・・・・そんなときの処理まで組んでいたFC版ドラクエ3のプログラマを、俺は尊敬する。起こりうるケースを想定して、一応組んでおく。この考え方は、大切な事だと思う。


洗濯物を干すとき、なんとなく「やきゅあそ」のCPUvsCPUを流しっぱなしにしている。「やきゅあそ」のCPUvsCPUは、両チームに介入することができる。アホなCPUの粗末な選手起用に異を挟みたいときに、勝手に代打や代走、守備固めを送りつけたりできるわけだ。

昨日、ロッテvs中日(DH制あり)でプレイしていた。7回ウラ、ロッテの攻撃。6番の大村巌が塁に出たので、介入して代走に諸積を出した。すると何故か「代打ボーリック」の画面になる。「んんん?」と思いながら放置していたら、ボーリックは左打席の後ろにいて、打席にはそのまま次打者7番佐藤幸彦が入った。画面にはボーリックのバットが微妙に映ってること以外問題なし。なんじゃこりゃ。よくよく考えたら、ボーリックは3番DHでスタメンじゃないか。意味わからん。

その後ボーリックは、左打席の後ろに位置取ったまま、ロッテの攻撃中ならバットを持ち、ロッテの守備中ならバットを持たず、イニングをまたいでも居座り続けた。んで、打順が回って次は3番DHボーリックというところで、フリーズした。


ま、こんなこともあるさ。とりあえず、俺が開発に携わる際には、Assert関数と、その後の念のため対処プログラムを組み込もうと思った。
posted by 小川 at 12:44| Comment(3) | TrackBack(0) | プログラム/マ | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
私は
if(flag==true)
の書き方はしないですねぇ。。。
やはりC使いだからだろう。

なのでJavaやPHPなどの他言語でも
if(flag==false)
{
}
else
{
}
という書き方をしますね。

これは確かプログラミング作法に載ってたはずだ。
Posted by アレックス at 2009年10月05日 23:58
こまるのは

if(!$hoge){
}



if($hoge === false){
}



if($hoge === ""){
}



if(!isset($hoge)){
}

をすべて実現させようとする人間の存在です。これだからPHPerはといわれること請け合い。
Posted by miracleさん at 2009年10月06日 10:45
■アレックスさん
C++では、
#define TRUE  1
#define FALSE 0
となっていることがあり、この場合は
if( flag == TRUE )
も、ありかもしれませんが、
flag = 2;
とかしてたら終了なので、やっぱダメですな。

■miracleさん
これは、変数に型が無いことによる弊害なんですかねぇ。(C系では)それぞれ明確に異なりますからねぇ。
Posted by 小川@管理人 at 2009年10月06日 12:22
コメントを書く
お名前: [必須入力]

メールアドレス:

ホームページアドレス:

コメント: [必須入力]

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。

この記事へのトラックバック