(about limitation of softies type exception mechanism)
前回投稿した、
例外機構 (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も実行してしまってるし。
貧しくて少し照れちゃうけど