両方を使用する必要があります。アサートは、開発者としての便宜のためです。例外は、実行時に見逃した、または期待していなかったことをキャッチします。
単純な古いアサートの代わりに、glibのエラー報告関数が好きになりました。これらはassertステートメントのように動作しますが、プログラムを停止するのではなく、単に値を返し、プログラムを続行させます。これは驚くほどうまく機能し、おまけとして、関数が「想定どおり」を返さない場合にプログラムの残りの部分がどうなるかを確認できます。クラッシュした場合は、エラーチェックがどこかで遅れていることがわかります。
前のプロジェクトでは、これらのスタイルの関数を使用して前提条件チェックを実装し、そのうちの1つが失敗した場合は、スタックトレースをログファイルに出力しますが、実行は続けます。デバッグビルドを実行しているときに他の人が問題に遭遇したときのデバッグ時間を大幅に節約しました。
#ifdef DEBUG
#define RETURN_IF_FAIL(expr) do { \
if (!(expr)) \
{ \
fprintf(stderr, \
"file %s: line %d (%s): precondition `%s' failed.", \
__FILE__, \
__LINE__, \
__PRETTY_FUNCTION__, \
#expr); \
::print_stack_trace(2); \
return; \
}; } while(0)
#define RETURN_VAL_IF_FAIL(expr, val) do { \
if (!(expr)) \
{ \
fprintf(stderr, \
"file %s: line %d (%s): precondition `%s' failed.", \
__FILE__, \
__LINE__, \
__PRETTY_FUNCTION__, \
#expr); \
::print_stack_trace(2); \
return val; \
}; } while(0)
#else
#define RETURN_IF_FAIL(expr)
#define RETURN_VAL_IF_FAIL(expr, val)
#endif
引数のランタイムチェックが必要な場合は、次のようにします。
char *doSomething(char *ptr)
{
RETURN_VAL_IF_FAIL(ptr != NULL, NULL); // same as assert(ptr != NULL), but returns NULL if it fails.
// Goes away when debug off.
if( ptr != NULL )
{
...
}
return ptr;
}