Version 10.11
プログラムの構造を考える
「今回はちょっと逸れて、プログラムの構造について見てみます」
『プログラムの構造?』
「そう。具体的に言うと、関数の分け方、ってところかな。えっと、まず前
回の続きから」
// メッセージループ。
int MessageLoop( HINSTANCE p_hInstance )
{
BOOL bRes;
MSG stMsg;
int iAclTranslated;
// アクセラレーターをリソースから読み込みます。
HACCEL hAccel
= LoadAccelerators
( p_hInstance
, MAKEINTRESOURCE( IDA_MAIN )
);
// メッセージループです。
while( 1 )
{
// メッセージをキューから取り出します。
bRes = GetMessage( &stMsg, NULL, 0, 0 );
if (
( bRes == 0 )
||
( bRes == -1 )
)
{
// 終了するのでループから抜けます。
break;
}
iAclTranslated
= TranslateAccelerator( stMsg.hwnd, hAccel, &stMsg );
if( iAclTranslated != FALSE )
{
// 変換したのでメッセージ変換とディスパッチはしません。
continue;
}
// メッセージを変換します。
TranslateMessage( &stMsg );
// ウィンドウプロシージャに送ります。
DispatchMessage( &stMsg );
}
return stMsg.wParam;
}
『この continue が初めてのだったんだよね』
「そう、まだ教えてなかったんだよね。 continue は break の親戚。
break については Version 2.9 ( No.020 ) を読んで」
『 break に似てるの? continue って』
「 break は for や while から抜けるけど、 continue はそのループの次
の週に進みます。たとえば上の例だと、 continue の所に来たら」
}
// メッセージを変換します。
TranslateMessage( &stMsg );
// ウィンドウプロシージャに送ります。
DispatchMessage( &stMsg );
「は全部飛ばして、 while の最初、つまり」
// メッセージをキューから取り出します。
「に来ます」
『あ、ってことはアクセラレーターのをしちゃったらそれはウィンドウプロ
シージャに送られないんだ』
「 Ctrl + A とかの入力がそのまま残ってウィンドウプロシージャで処理さ
れるとまずいからね」
『なるほどねー。あれ? でもさ、』
// メッセージをキューから取り出します。
『のところに来るって、それって……戻るってこと?』
「うーん、 while だとそうなんだけど、 for だとちょっと違うかな。たと
えば」
for( int iF1 = 0; iF1 < 10; iF1++ )
{
TRACE( "%d\n", iF1 );
continue;
}
「もしこれで、 continue が」
TRACE( "%d\n", iF1 );
「に戻るとすると?」
『そっか、これだとずーっと終わらない……ホントの無限ループになっ
ちゃう』
「でも実際にはなりません。 continue は for の〈次の週〉にいくから、
ちゃんと iF1++ が効いて、 iF1 が増えていくんです」
『なるほどねー、 for だとそういう意味があるんだね』
「さて、話を少し変えて。今回の continue だけど、実際には使わないで済
ませる方法もあります。たとえば次のように」
// メッセージループです。
while( 1 )
{
// メッセージをキューから取り出します。
bRes = GetMessage( &stMsg, NULL, 0, 0 );
if (
( bRes == 0 )
||
( bRes == -1 )
)
{
// 終了するのでループから抜けます。
break;
}
iAclTranslated
= TranslateAccelerator( stMsg.hwnd, hAccel, &stMsg );
if( iAclTranslated == FALSE )
{
// メッセージを変換します。
TranslateMessage( &stMsg );
// ウィンドウプロシージャに送ります。
DispatchMessage( &stMsg );
}
}
『えっと、 continue のあったところに、ウィンドウプロシージャにディス
パッチするところが入ってるんだね』
「あともうひとつ、 if の条件が逆になってるでしょ」
『あ、ホントだ。さっきは != だけど今度は == になってる』
「つまり、最初は〈変換された時に中に入って continue する〉ってなって
たけど、今回のは〈変換されなかった時に中に入ってディスパッチする〉っ
てなってます」
『中と外の違いねー。で、実際に見た目以外にこれって何か違うところある
の?』
「機能としては同じだね。実際、このプログラムだけで言えばどっちもあま
り違いはないと思うよ」
『……このプログラムだけ、ってどういう意味?』
「そう、そこ。たとえば、ディスパッチ前になにかの処理が必要になって」
if( iAclTranslated == FALSE )
{
// ここに200行くらいのコード。
// ...
// メッセージを変換します。
TranslateMessage( &stMsg );
// ウィンドウプロシージャに送ります。
DispatchMessage( &stMsg );
}
「みたいになると」
『 if の中がすごーく長くなっちゃうね』
「 if の中がすごく長いと、 if の範囲がどこまでかとか解りづらくなるか
ら良くないかな」
『そういう時はさっきの方がいいってことね』
「基本的にはそういう事。でも逆に」
if( iAclTranslated == FALSE )
{
// さっきの200行+ディスパッチの部分を入れた
// Dispatcher() という関数に入れて、それを呼びます。
Dispatcher( &stMsg );
}
「って関数にしちゃえば」
『これならシンプルねー。って、これって別に if の外に出した時でもでき
るでしょ?』
「もちろん」
『もちろんって……』
「ここで言いたいのは、色々な方法があるよってこと」
『あー、うん、それはわかる』
「まだここでは細かいことは説明しないけど、プログラムの方法にはいろん
な方法があって、それぞれにメリットデメリットがあります」
『それはあとで教えてもらえるってことね』
「そういうこと。でも、教える前に、プログラムを組む上で〈これは本当に
最善策かな〉っていうのは意識しておいて」
『ってゆーと?』
「たとえばこの章のプログラムだと、ウィンドウプロシージャ以外は
WinMain() の中に全部入れることもできるでしょ」
『確かに入れられるし……そういう例もよく見るね』
「でもそうなると無茶苦茶長くなっちゃうし、変数もぐちゃぐちゃになっ
ちゃう。他では使わない stWndClassEx がそのまま残ったりするから」
『だから関数を分割したわけね』
「そういうこと。こういうのはぱっと見でいいか悪いかわかる例。でも……
たとえば、前回のアクセラレーターの」
// アクセラレーターをリソースから読み込みます。
HACCEL hAccel
= LoadAccelerators
( p_hInstance
, MAKEINTRESOURCE( IDA_MAIN )
);
「って部分。これって今はメッセージループする直前で呼んでるけど、実際
にはメッセージループとはちょっと違うから、他の関数で呼んだ方がいいか
も」
『つまり、 WinMain() とかで呼んで、 hAccel を引数にして渡すとか?』
「そういうこと。まぁ WinMain() で呼ぶのもどうかなって思うし、今ある
関数の中だと、どこで呼べばいいのかとか難しいけど」
『こういうのはぱっと見でどうすればいいかっていうのが難しい例ね』
「そういうこと。この辺はすっぱり割り切れない部分だけど、いつも気に留
めておくようにしてね」
/*
Preview Next Story!
*/
『でもなんか、今回って煮え切らないよねー』
「実際、こういうのって掲示板で言い争いとかになるくらいだし」
『げ、そうなの?』
「だから、僕が教えるのもひとつの例ってことで」
『それって責任放棄って言わない?』
「う”……」
『というわけで次回』
< Version 10.12 コード全体 >
「につづく」
『って、次回こそ責任放棄じゃない!』
「 SDK 編はどうしてもコード量が多くなるから……」