« Doxygenの日本語対策状況 | トップページ | VS.NETマクロで、VS6のときのようなイベントルーチンを記述する方法 »

2003年5月24日 (土)

AssertionからEnforceへ。

Andrei Alexandrescuっつー人がいます。
C++とかで有名な人だったような。
C++のテンプレートとマクロを駆使した奇抜なトリックがお得意の人だったような。
よく覚えてません。

この人の記事をいくつか読みました。

Assertions
http://www.cuj.com/documents/s=8248/cujcexp2104alexandr/

Enforcements
http://www.cuj.com/documents/s=8250/cujcexp2106alexandr/

Assertionsは従来のC的なマクロで実現されるアサーションをテンプレートとかを使って便利(というよりC++的)にできないか、といった内容でした。
まぁこっちはこっちで面白いのですが、より興味を引かれたのがEnforcements。
元の記事は英語だし日本ではあんまり議論されてないみたいなんでちょっと紹介してみたり。



ふつーエラー処理は

{
	FILE*	pFile = fopen(...);
	if( ! pFile )
		;//エラー処理。例外投げるとか。returnで関数を終わるとか。

	//以下通常の処理。
}
みたいな感じで書きます。if文を使ってエラーが起こったら回復処理を行うといった感じ。

これの欠点はif文を使って回復処理を書かなければならないこと。そのまんまですが。
例えば以下のような例がわかりやすいかと。
DirectDraw::DirectDraw( ... )
{
	if( DirectDrawCreate( NULL, &lpDirectDraw, NULL ) != DD_OK )
		return;

	//プライマリ作るんでまず強調レベルを普通にセットする。
	if( lpDirectDraw->SetCooperativeLevel( hWnd, DDSCL_NORMAL ) )
		return;

	bool	 bResult = false;
	lpPrimary = new PrimarySurface( bResult, lpDirectDraw, rScreen );
	if( !bResult )
		return;

	if( bFullScreen )
		lpMode = new FullScreenMode( bResult, hWnd, lpDirectDraw );
	else
		lpMode = new WindowMode( bResult, hWnd, lpDirectDraw, lpPrimary->GetSurface() );
	if( !bResult )
		return;

	if( !SetDisplayMode( iWidth, iHeight, iColorDepth, bFullScreen ) )
		return;

	lpSecondary = new TemporarySurface( bResult, lpDirectDraw, iWidth, iHeight );
	if( !bResult )
		return;
}
はるか昔に書いたDirectDrawの初期化部分なんですが、こんなふーに1文書くごとにif文が挟まってます。
仮にif文がなかったら下のような感じ。
DirectDraw::DirectDraw( ... )
{
	DirectDrawCreate( NULL, &lpDirectDraw, NULL );
	lpDirectDraw->SetCooperativeLevel( hWnd, DDSCL_NORMAL );
	lpPrimary = new PrimarySurface( bResult, lpDirectDraw, rScreen );

	if( bFullScreen )
		lpMode = new FullScreenMode( bResult, hWnd, lpDirectDraw );
	else
		lpMode = new WindowMode( bResult, hWnd, lpDirectDraw, lpPrimary->GetSurface() );

	SetDisplayMode( iWidth, iHeight, iColorDepth, bFullScreen );
	lpSecondary = new TemporarySurface( bResult, lpDirectDraw, iWidth, iHeight );
}
後者の方が何やってるか見通しがいいような気がしませんか?
つまり、エラー処理に必要なif文を使った回復処理のコードを何とかできないだろうか。と言うのが記事「Enforcements」の主旨です。

例えば
bool    foo();
と言う関数があって、戻り値がfalseなら例外Exceptionを投げるようなエラー処理を行いたいとします。
普通に書くならこうです。
{
	if( ! foo() )
		throw Exception();
}
Andrei先生は以下のような関数Enforceを導入したらどうかと提案しています。
template< typename tException >
inline void	 Enforce( bool isCondition )
{
	if( !isCondition ) throw tException();
}
これを使うと、
{
	Enforce< Exception >( foo() );
}
とまぁ、こんな感じ。とりあえず1行にまとまります。
今回の記事のメインの主張はこのEnforceです。

if文を使って
{
	if( ! foo() )
		throw Exception();
	if( ! foo() )
		throw Exception();
	if( ! foo() )
		throw Exception();
	if( ! foo() )
		throw Exception();
	if( ! foo() )
		throw Exception();
	if( ! foo() )
		throw Exception();
	if( ! foo() )
		throw Exception();
}
こう書くのと
{
	Enforce< Exception >( foo() );
	Enforce< Exception >( foo() );
	Enforce< Exception >( foo() );
	Enforce< Exception >( foo() );
	Enforce< Exception >( foo() );
	Enforce< Exception >( foo() );
	Enforce< Exception >( foo() );
	Enforce< Exception >( foo() );
}
こう書くの、どちらが本来の処理の流れが見やすいだろうか、と。
毎回Enforceでくくられるのもそれなりに微妙ですが、行数も減るしなかなかナイスではあります。

さて、特に面白いのはここからです。
先ほどのEnforceというアイディアをAndrei先生お得意のトリックで味付けしていきます。

まず、冒頭で紹介した
{
	FILE*	pFile = fopen(...);
	if( ! pFile )
		;//エラー処理。例外投げるとか。returnで関数を終わるとか。

	//以下通常の処理。
}
この処理を考えてみましょう。

この場合戻り値pFileをあとで利用するので、今までのEnfoce関数を使うと、
{
	FILE*	pFile = fopen(...);
	Enforce< Exception >( pFile );
	//以下通常の処理。
}
となってしまい芸がありません。
そこでEnforceを次のように改造します。
template< typename tException, typename tArgument >
inline tArgument&		Enforce( tArgument& argument )
{
	if( !argument ) throw tException();
}
引数に受け取った値をそのまま戻り値として返す、「パイプ」のような関数になりました。
これを使えば
{
	FILE*	pFile = Enforce< Exception >( fopen(...) );
	//以下通常の処理。
}
見事にfopenのエラー処理をenforceにまとめることができます。
あとは手抜き解説。具体的な実装は記事のほう参照してください。

今までは引数をboolとしてチェックしてfalseならエラーとしていましたが、他にもある値と同一ならエラー(DirectDrawならDD_OKとか)といったこと
をしたいときもあるでしょう。
class Predicateを導入してif文の部分を抽象化することでいろんなエラーチェックに対応できるようにできます。
Enforce< HandlerPledicate, Exception >( fopen(...) )
とかって感じ。

あと記事にはないですが、がんばれば多分例外を投げる関数に対応するエラー処理もEnforceできます。
マクロを使ってやります。機会があれば実装したいなぁ。

あと面白いトリックだったんですが、
Enforce< Exception >( fopen(...) ) << "fopen failed.\nfilename : " << __FILE__ << "\nline : "__LINE__ << endl;

こんなこともできます。
これは<<でExceptionにエラー文字列を渡しています。
つまりエラーに応じて任意の情報をエラー処理に渡せると。素敵です。

|

« Doxygenの日本語対策状況 | トップページ | VS.NETマクロで、VS6のときのようなイベントルーチンを記述する方法 »

コメント

コメントを書く



(ウェブ上には掲載しません)


コメントは記事投稿者が公開するまで表示されません。



トラックバック

この記事のトラックバックURL:
http://app.f.cocolog-nifty.com/t/trackback/61222/972567

この記事へのトラックバック一覧です: AssertionからEnforceへ。:

« Doxygenの日本語対策状況 | トップページ | VS.NETマクロで、VS6のときのようなイベントルーチンを記述する方法 »