エラー処理のネスト

C言語のネストの深いエラー処理がとっても嫌だ。

Win32APIを使った例

int error = 0;
HANDLE hFile = CreateFile(filename, ...);

if (hFile != INVALID_HANDLE_VALUE) {
	HANDLE hMap = CreateFileMapping(hFile, ...);
	if (hMap) {
		void * p = MapViewOfFile(hMap, ...);
		if (p) {
			...
			UnmapViewOfFile(p);
		}
		else {
			error = -3;
		}
		CloseHandle(hMap);
	}
	else {
		error = -2;
	}
	CloseHandle(hFile);
}
else {
	error = -1;
}

ここでやりたいのは p を使ってごにょごにょすることなのに、それ以外の外骨格が分厚すぎる。また、判定部(if文)とエラー処理(error=-1とか)が離れているのが読みづらい。

goto を使って書いても

int error = 0;
HANDLE hFile = CreateFile(filename, ...);
HANDLE hMap = NULL;
void *p = NULL;

if (hFile != INVALID_HANDLE_VALUE) {
	error = -1;
	goto FUNC_END;
}

hMap = CreateFileMapping(hFile, ...);
if (hMap == NULL) {
	error = -2;
	goto FUNC_END;
}

p = MapViewOfFile(hMap, ...);
if (p==NULL) {
	error = -3;
	goto FUNC_END;
}

	...

FUNC_END:
if (p) UnmapViewOfFile(p);
if (hMap) CloseHandle(hMap);
if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile);

ネストが浅くなった分、ましな気がするが、分厚さは消えない。使わないかもしれない変数をNULLで初期化して用意しておくのも何となく気持ちが悪い。あと、GOTO文否定派の同僚がいたら不毛な争いが起こりそうだ。

gotoの変形として、次のように書けたらどうだろう?(妄想)

int error = 0;

error_check {
	HANDLE hMap = NULL;
	void *p = NULL;

	HANDLE hFile = CreateFile(filename, ...)
		break if (hFile == INVALID_HANDLE_VALUE) { error = -1; } //案1 break if を付けるとか

	hMap = CreateFileMapping(hFile, ...)
		{hMap == NULL => error = -2}; //案2 {} の中が 判定 => エラー処理 になるとか

	p = MapViewOfFile(hMap, ...)
		[p == NULL => error = -3]; //案3 [] の中がエラー処理とか
	
	...

finally:
	if (p) UnmapViewOfFile(p);
	if (hMap) CloseHandle(hMap);
	if (hFile!=INVALID_HANDLE_VALUE) CloseHandle(hFile);
}

うーん...いまいち。NULLで初期化する変数はそのままだし。まあ、これはエラー処理の問題というよりはリソース管理の問題なので、別の仕組みで救うのかな。

いっそこんなのは?(妄想)

error_check {
	// 関数の戻り値は、変数で受けなければ _ (アンダーバー) に入る
	CreateFile(filename, ...) [_ == INVALID_HANDLE_VALUE => error = -1];
	CreateFileMapping(_, ...) [NULL => error = -2]; // 判定が == の場合は省略も可
	void* p = MapViewOfFile(_, ...) [NULL => error = -3];
	
	...
}

C言語の関数は戻り値でエラーを表現する関数が多いので、わりと便利な気はする。ただ、やはりリソースの解放の仕方が思いつかない。

愚痴

C言語のネストが深いエラー処理が嫌というのは幻想で、きちんと構造化し、ライブラリなどをうまく用いれば、ネストの嵐になるようなことはないのかもしれない。ちょっとしたプログラムのはずがどんどん行数が増えていくのは、私のスキルのなさに起因するところが大きいのは確かだ。それでも、C言語のソースを見ていると、皮ばかり分厚くて実の部分がほんのちょこっとしかない果物を連想してしまう。1瓶のジャムを作るためにトラックいっぱいの果実を摘んでくるようで徒労感に苛まれる。肩こりとめまいがする。歳かな。

C言語オンリーのエラー処理のデザインパターン本とか出ないだろうか?出ないか。最近、HaskellのMaybeモナドなど見ていて、Cにもそんな機構が欲しいなと思うことがある。世間のエラー処理の技術は進歩しているようなのに、恩恵を受けられないなあ。