前回投稿した、例外機構 (exception mechanism)では、その冒頭で「使用上の制約もあることにも留意。」と書いたが、それについてのまとめ。
final節を省略できない (cannot omit finally clause)
finalは GCCの拡張機構であるネスト関数(GCC nested function)を前方参照で呼び出しているため、存在することを前提として try処理を始める。したがって、final節を省略することはできない。
final節を記述しなかった場合、つぎのような lwd_Environment_finally定義されていないというコンパイルエラーが発生する。
ExceptionExampleTest.c: In function ‘testExceptionWithoutFinal’: ExceptionExampleTest.c:51:1: error: expected declaration or statement at end of input ExceptionExampleTest.c:51:1: error: expected declaration or statement at end of input ExceptionExampleTest.c:42:2: error: nested function ‘lwd_Environment_finally’ declared but never defined
throwsの記述ができない (throws clause cannot describe)
Javaではチェック例外と非チェック例外があり、チェック例外をスローする関数に throwsを付けることにより、catchを強制することができる。C++では例外仕様(exception specifications)を記述して、catchするように促すことができる。
しかし、Cではこのような仕掛けはできそうもない。
1つの関数に複数個並べることができない (multiple try)
finallyはネスト関数名 lwd_Environment_finallyで受けているため1つの関数の中で2つ以上の finallyが書けず、2個以上の tryを書くことができない。void testDoubleTry() { try { throw(new_TestException("test 1")); } catch (TestException, e) { printf("TestException caught, %s\n", e->message); } finally { printf("finally 1\n"); } try { throw(new_TestException("test 2")); } catch (TestException, e) { printf("TestException caught, %s\n", e->message); } finally { printf("finally 2\n"); } }
このファイルをビルドすると、
$ make cc -g -finstrument-functions -I. -I ../../core/include -I ../../experimental/include -I ../../io/include -I ../../lang/include -I ../../message/include -I ../../unit/ include -I ../../util/include -I ../../x11/include -c -o ExceptionExampleTest.o ExceptionExampleTest.c $ビルドエラーにはならがいが、実行してみると、
[CUnit] ./ExceptionExampleTest: testDoubleTry TestException caught, test 1 finally 2 TestException caught, test 2 finally 2
どうやら、GCCは1つの関数内に同じ名前のネスト関数が含まれていてもエラーにしないが、後にある関数を呼び出すようだ。
対策としては、つぎのように各々の tryを { }でくくればよい。(ちょっと格好悪い)
void testDoubleTry() { { try { throw(new_TestException("test 1")); } catch (TestException, e) { printf("TestException caught, %s\n", e->message); } finally { printf("finally 1\n"); }} { try { throw(new_TestException("test 2")); } catch (TestException, e) { printf("TestException caught, %s\n", e->message); } finally { printf("finally 2\n"); }} }
つぎのように期待した通りの結果になる。
[CUnit] ./ExceptionExampleTest: testDoubleTry TestException caught, test 1 finally 1 TestException caught, test 2 finally 2括弧を付けていないときにエラーにならないのは問題なので、tryごとにダミーのローカル変数かなにかを置けばポカ避けにはなるだろう。
tryのネストはできる (nested try)
tryの中に tryがあるときは問題ない。void testNestedTry() { try { try { throw(new_TestException("test inner")); } catch (TestException, e) { printf("TestException caught inner, %s\n", e->message); } finally { printf("finally inner\n"); } throw(new_TestException("test outer")); } catch (TestException, e) { printf("TestException caught outer, %s\n", e->message); } finally { printf("finally outer\n"); } }
実行結果は、
[CUnit] ./ExceptionExampleTest: testNestedTry TestException caught inner, test inner finally inner TestException caught outer, test outer finally outer
catchの中のtryは駄目 (try in catch clause)
void testTryInCatch() { try { throw(new_TestException("test outer")); } catch (TestException, e) { printf("TestException caught outer, %s\n", e->message); try { throw(new_TestException("test inner")); } catch (TestException, e) { printf("TestException caught inner, %s\n", e->message); } finally { printf("finally inner\n"); } } finally { printf("finally outer\n"); } }catchの中の tryは、外部および内部の finallyの関数呼び出しがいづれも内部の finally関数を参照してしまうためうまくいかない。
[CUnit] ./ExceptionExampleTest: testTryInCatch TestException caught outer, test outer finally inner TestException caught inner, test inner finally inner finally outerしかも、外部の finallyも実行してしまってるし。
貧しくて少し照れちゃうけど
0 件のコメント:
コメントを投稿