web-dev-qa-db-ja.com

あまりにも多くのアサーションを書くことは可能ですか?

私はC++コードでassertチェックを書くことの大ファンで、開発中に発生する可能性はないが、プログラムの論理バグのために発生するケースをキャッチする方法として使用しています。これは一般的に良い習慣です。

しかし、私が書いた一部の関数(複雑なクラスの一部)には5つ以上のアサートがあり、可読性と保守性の点でプログラミングプラクティスが悪い可能性があると感じていることに気付きました。それぞれが私に関数の事前条件と事後条件について考える必要があり、それらは本当にバグを見つけるのに役立つので、私はそれはまだ素晴らしいと思います。しかし、私はこれをそこに出して、多数のチェックが必要な場合に論理エラーをキャッチするためのより良いパラダイムがあるかどうかを尋ねたかっただけです。

Emacsのコメント:Emacsは私のIDEを選択したので、assertステートメントが少し灰色になっていて、提供できる混乱を軽減するのに役立ちます。 .emacsファイルに追加します。

; gray out the "assert(...)" wrapper
(add-hook 'c-mode-common-hook
  (lambda () (font-lock-add-keywords nil
    '(("\\<\\(assert\(.*\);\\)" 1 '(:foreground "#444444") t)))))

; gray out the stuff inside parenthesis with a slightly lighter color
(add-hook 'c-mode-common-hook
  (lambda () (font-lock-add-keywords nil
    '(("\\<assert\\(\(.*\);\\)" 1 '(:foreground "#666666") t)))))
34
Alan Turing

私は、誰かがmoreアサートを書いた場合に速く解決されるであろう数百のバグを見てきました。書く少ない

[C]可能性[アサートが多すぎる]は、可読性と保守性の点で、不適切なプログラミング手法である可能性があります[?]

可読性が問題になる可能性があります-良いアサーションを書く人も可読コードを書くのは私の経験ですが。そして、関数の最初がアサートのブロックで始まり、引数がガベージではないことを確認するのを気にすることは決してありません。その後に空白行を置くだけです。

また、私の経験では、ユニットテストと同様に、保守性はalwaysアサートによって改善されます。アサートは、コードが意図された方法で使用されていることの健全性チェックを提供します。

45
Bob Murphy

アサーションが少なすぎる:そのコードを変更する幸運は隠された仮定でだらけです。

アサーションが多すぎる:読みやすさの問題を引き起こし、コードの臭いを引き起こす可能性があります-クラス、関数、APIは、assertステートメントに非常に多くの仮定が置かれている場合に正しく設計されていますか?

実際には何もチェックしないか、各関数のコンパイラ設定などをチェックするアサーションがある可能性もあります:/

スイートスポットを目指してください。ただし、他の誰かがすでに言っているように、主張が「多ければ多いほど」、「神が私たちを助けてくれるより少ない」というより害はありません。

4
MaR

アサーションの多くは「コンパイラーが機能している」および「ライブラリーが機能している」ため、アサートを作成する時間が少なくなることがわかりました。正確に何をテストしているのかを考え始めたら、アサートを書く回数が減ると思います。

たとえば、(たとえば)コレクションに何かを追加するメソッドは、コレクションが存在することをアサートする必要はありません。これは通常、メッセージを所有するクラスの前提条件であるか、ユーザーに戻す致命的なエラーです。したがって、非常に早い段階で一度確認してから、それを想定してください。

アサーションはデバッグツールであり、通常は2つの方法で使用します。自分のデスクでバグを見つける(そしてチェックインされない)。そして顧客の机のバグを見つけます(そして彼らはチェックインされます)。どちらの場合も、アサーションを使用して、できるだけ早く例外を強制した後にスタックトレースを生成しています。この方法でアサーションを使用すると、簡単に heisenbugs になる可能性があることに注意してください。アサーションが有効になっているデバッグビルドでは、バグが発生することはありません。

4
Мסž

ブール値のCONSTメソッドへの参照のみをとるAssert関数を記述できれば、すばらしいことです。このようにして、ブール値のconstメソッドを使用してアサートをテストすることで、アサートに副作用がないことが確実になります。

特に、ラムダ(c ++ 0xの場合)をあるクラスへのconstとして注釈を付けることはできないので、そのためにラムダを使用することはできません。

あなたが私に尋ねたらやりすぎですが、私が主張のために一定レベルの汚染を見始めたら、私は2つのことに注意するでしょう:

  • アサートで副作用が発生していないことを確認します(上記で説明した構成によって提供されます)。
  • 開発テスト中のパフォーマンス。これは、アサート機能にレベル(ロギングなど)を追加することで対処できます。パフォーマンスを向上させるために、開発ビルドから一部のアサートを無効にすることができます
3
lurscher

一緒に働きたい! assertsをたくさん書く人は素晴らしいです。 「多すぎる」などあるかわかりません。私にはるかに一般的であるのは、書く量が少なすぎて、最終的には時折致命的なUBの問題に遭遇する人です。このUBは、単純なassertで繰り返し簡単に再現できた満月にしか現れません。

失敗メッセージ

私が考えることができる1つのことは、次のように失敗情報をassertに埋め込むことです(まだ行っていない場合)。

_assert(n >= 0 && n < num && "Index is out of bounds.");
_

これにより、仮定と前提条件を文書化する際にアサーションがより強力な役割を果たすようになるため、まだこれを行っていなかったとしても、多すぎると感じることはもうありません。

副作用

もちろん、assertは実際には誤用され、次のようなエラーが発生する可能性があります。

_assert(foo() && "Call to foo failed!");
_

... foo()が副作用を引き起こす場合、そのことについては十分注意する必要がありますが、あなたはすでに非常に自由に主張する人(「経験豊富なアサーター」)であると確信しています。うまくいけば、あなたのテスト手順は、仮定を主張するためのあなたの注意深い注意と同じくらい良いです。

デバッグ速度

デバッグの速度は通常、優先度リストの一番下にあるはずですが、デバッガを介したデバッグビルドの実行が100回を超える前に、コードベースで多くのことをアサートすることが一度ありました100回リリースよりも遅い

それは主に私がこのような機能を持っていたからです:

_vec3f cross_product(const vec3f& lhs, const vec3f& rhs)
{
    return vec3f
    (
        lhs[1] * rhs[2] - lhs[2] * rhs[1],
        lhs[2] * rhs[0] - lhs[0] * rhs[2],
        lhs[0] * rhs[1] - lhs[1] * rhs[0]
    );
}
_

... _operator[]_を呼び出すたびに、境界チェックアサーションが実行されます。私は結局、それらのいくつかのパフォーマンスクリティカルなものを、デバッグビルドを劇的にスピードアップするために、実装の詳細レベルの安全のためにわずかなコストで劇的にスピードアップすることを主張しない安全でない同等のものに置き換えました、そしてそれはそれのスピードヒットが始まったからです生産性を著しく低下させます(デバッグを高速化する利点は、いくつかのアサートを失うコストを上回りますが、_operator[]_ではなく、最も重要な測定パスで使用されていたこのクロス積関数のような関数についてのみです。一般に)。

単一の責任の原則

私はあなたがより多くのアサートで本当にうまくいくとは思いませんが(少なくともそれは少なすぎるよりも多すぎる側でエラーを起こす方がはるかに良いです)、アサート自体は問題ではないかもしれませんが、それを示しているかもしれません。

たとえば、単一の関数呼び出しに対して5つのアサーションがある場合、それは多すぎる可能性があります。そのインターフェースには、前提条件と入力パラメーターが多すぎる可能性があります。私はそれが主張の健全な数を構成するもののトピックとは関係がないと考えます(私は通常、「より多くの陽気な人!」と答えます)、それは可能性のあるレッドフラグかもしれません(またはおそらくない可能性があります)。

2
user204677

C++で書いたよりもC#で書いたのですが、2つの言語はそれほど離れていません。 .Netでは、発生しないはずの条件にアサートを使用しますが、続行する方法がない場合にも例外をスローすることがよくあります。 VS2010デバッガーは、リリースビルドがどのように最適化されていても、例外に関する多くの優れた情報を表示します。可能であれば、単体テストを追加することもお勧めします。ロギングは、デバッグの補助としても役立つ場合があります。

では、アサートが多すぎるのでしょうか?はい。中止/無視/続行のいずれかを1分間に15回選択すると、煩わしくなります。例外は1回だけスローされます。アサーションが多すぎるポイントを定量化することは困難ですが、アサーションがアサーション、例外、単体テスト、およびロギングの役割を果たす場合、何かがおかしいです。

発生してはならないシナリオのためにアサーションを予約します。アサーションの方が高速なので、最初は過剰にアサートすることができますが、後でコードをリファクタリングします。それらの一部を例外に、一部をテストに変換します。すべてのTODOコメントをクリーンアップする十分な規則がある場合は、やり直す予定のそれぞれの横にコメントし、後でTODOに対処することを忘れないでください。

2
Job