例外指向プログラミングのすゝめ
例外指向プログラミングとは
例外指向プログラミングとは、 C++ ではエラー処理にのみ使われる例外構文を制御構文として用いるプログラミング手法です。
制御構造としての例外を用いることで、従来の分岐・繰り返しのみの制御構造と比べ、より柔軟な制御が可能です。
ループの脱出
みなさんは多重にネストしたループから一気に抜ける場合、どのような方法を用いるでしょうか。
一般的には多重ループを丸ごと関数にして return したり、 goto を使うといった方法が考えられます。
例外指向プログラミングでは、例外送出をループ脱出の方法をとして用います。
コード例
int main(int argc, char* args[]) { class EscapeLoop{}; try { while (true) { for (int i=0; i<10; i++) { for (;;) { throw EscapeLoop(); } } } } catch (EscapeLoop e) { } }
このように、関数内のローカルクラスを定義、これをループ脱出時に送出することで飛び先が明確になります。
このような例外によるループ脱出は Python でも採用されており*1、一般的な記法であると言えます。
パターンマッチとしての例外
関数型言語を使用したことがある方ならばおわかりいただけると思いますが、関数型言語においてはパターンマッチが非常に重要です。
いったんパターンマッチに慣れてしまうと、通常の条件分岐や switch 文を用いて記述するのが億劫になるほど便利なものです。
例外指向プログラミングにおいては、try 内で送出された例外を値と見なし、型によるパターンマッチを実現します。
コード例
int main(int argc, char* args[]) { try { if (cond) { throw 1; } else { throw } } catch (int i) { std::cout << i << std::endl; } catch (std::string s) { std::cout << s << std::endl; } }
関数の返値としての例外
例外指向プログラミングにおいては、関数内から値を返す際にも例外を用います。
例外を用いることで、関数から返値として返すことのできる型が一つだけでなくなります。
返値として返す可能性のある型は、関数定義の throw 節に記述することで明示できますので、キャッチするべき型がわからない、といったことはありません。
コード例
void func(int x) throw (std::string, SomeClass) { if (x == 10) { throw std::string("test"); } else { throw SomeClass(10); } } int main(int argc, char* args[]) { try { func(0); } catch (std::string e) { std::cout << e << std::endl; } catch (SomeClass s) { std::cout << s.value << std::endl; } return 0; }