きままにブログ

プログラミングを主とした私のメモ帳です。寂しいのでコメントください笑

例外の後始末

次のようにmain関数の中でlock-unlock処理の中に何らかの関数funcがあったとして、funcが例外を投げる可能性があるとする。例外の如何に関わらず、unlockが実行されるにはどうしたら良いだろうか?

スコープを抜けたらデストラクタが呼び出されることを利用

void func() {
	cout << "処理" << endl;
	throw exception();
}

struct Lock {
	bool flag = false;

	void lock() {
		cout << "lock" << endl;
	}

	void unlock() {
		cout << "unlock" << endl;
		flag = true;
	}

	~Lock() {
		if(!flag) {
			unlock();
		}
	}
};

int main() {
	try {
		Lock lock;
		lock.lock();
		func();
		lock.unlock();
	}
	catch(...) {
		cout << "rescue" << endl;
	}

	cin.get();
	return 0;
}

なお、ユーザがunlock関数を使えないことを仮定すれば気持ちの悪いフラグを消し去ることができる。そもそもコンストラクタの時点でlockしてしまえば次のように簡素に記述できる。

struct Lock {
	Lock() {
		cout << "lock" << endl;
	}

	~Lock() {
		cout << "unlock" << endl;
	}
};

この場合、範囲を指定するために、{ Lock lock; ~ }と書く必要がある。同様に、開始ー終了処理が必要な場合、即ちメモリを確保して開放するような処理がある場合、いちいちクラスを作らなければならない。

Boostにはスコープを抜けたら実行するScopeExitというものがあるらしいです。自分で簡単なマクロ/クラスを作ってラムダ式を渡して実行させることだって可能です。しかしそんな変態的な手法は使いたくないので、現状ではこれが限界でしょう。