両方を使用する必要があります。アサートは、開発者にとって便利です。例外は、実行時に見逃したものや予期していなかったものをキャッチします。
単純な古いアサートではなく、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;
}