web-dev-qa-db-ja.com

関数がスローする可能性のあるすべての例外を文書化するにはどうすればよいですか?

例外をスローできる他の(プライベートまたはパブリック)ヘルパー関数を使用する例外をスローする可能性のあるパブリック関数がある場合は、パブリック関数がスローできる例外を文書化する必要があると思います。ヘルパー関数によってスローされる例外

このようなもの(Doxygenを使用):

_/** 
 * @throw Exception ...
 * @throw ExceptionThrownByHelper ...
 * @throw ExceptionThrownByHelpersHelper ...
 */
void theFunction() 
{ 
    helperWhichMayThrowException();
}
_

helperWhichMayThrowException()は、例外をスローする可能性のある他の関数も呼び出します。

これを行うには、次のことができます。

  1. すべての関数theFunction()呼び出しを再帰的に追跡し、その関数によってスローされた例外を探します。これは大変な作業であり、ヘルパーに例外を追加するときに、どこかに例外を文書化するのを忘れる可能性があります。
  2. ヘルパーによってスローされたすべての例外をtheFunction()でキャッチし、それらを変換して、指定した例外のみがスローされるようにします。しかし、なぜ例外を使用するのでしょうか。
  3. ヘルパー関数によってスローされる例外について心配する必要はありませんが、パブリック関数によってスローされる可能性のある例外がわからないため、すべての例外を単体テストすることはできません。
  4. ヘルパーなどによってスローされたすべての例外を(半)自動的に一覧表示するツールがあります。Doxygenのドキュメントを調べましたが、これを行う方法が見つかりませんでした。

オプション4を使用したいのですが、まだ良い解決策が見つかりません。Doxygenで実行できるのでしょうか。または多分私は多くに文書化したいですか?

edit:あまり明確ではないかもしれませんが、すべてのヘルパー関数を手動でチェックせずに関数がスローする可能性のあるすべての例外を文書化する簡単な方法を探しています(できればDoxygenを使用)。簡単な方法には、「すべての例外を文書化しない」または「theFunction()ですべての例外をキャッチして変換する」が含まれます。

27
rve

私は次の手動の解決策を思いついた。基本的には、呼び出すメンバーから_@throw_ドキュメントをコピーするだけです。 Doxygenに_@copythrows_と同様の_@copydoc_があればいいのですが、以下は機能します。

_class A {
    public:
        /** @defgroup A_foo_throws
         *
         * @throws FooException
         */

        /** 
         * @brief Do something.
         *
         * @copydetails A_foo_throws
         */
        void foo();
};

class B {
    public:
        // This group contains all exceptions thrown by B::bar()
        // Since B::bar() calls A::foo(), we also copy the exceptions
        // thrown by A::foo().

        /** @defgroup B_bar_throws
         *
         * @copydetails A_foo_throws
         * @throws BarException
         */

        /**
         * @brief Do something else.
         *
         * @copydetails B_bar_throws
         */
        void bar();
};  
_

次に、Doxyfile構成ファイルで_*_throws_を_EXCLUDE_SYMBOLS_に追加します。これにより、これらのグループがモジュールとして表示されないようになります。

次に、B::bar()は次のドキュメントになります。

void B :: bar()
何か他のことをしなさい。

例外:
FooException
例外:
BarException

6
rve

基本的に、あなたが求めることは、事実上すべての現実の状況では不可能です。

スローされた例外を文書化するには、2つの部分があります。

1)簡単なビット。メソッドで直接スローされる例外を文書化します。これは手作業で行うことができますが、かなり面倒であり、ドキュメントをコードと同期させないと、ドキュメントが誤解を招く可能性があります(ドキュメントがまったくない場合よりも悪い可能性があります。信頼できるドキュメントは確実にしか信頼できないためです100%正確です)。私の AtomineerUtils アドインは、最小限の労力でコードとドキュメントのコメントの同期を維持するため、これをはるかに簡単に実現できます。

2)不可能なビット。メソッドを「通過」する可能性のあるすべての例外を文書化します。これは、メソッドによって呼び出されたメソッドのサブツリー全体を繰り返し処理して、何がスローされるかを確認することを意味します。なぜそれは不可能ですか?さて、最も単純なケースでは、既知のメソッドに静的にバインドするため、それらをスキャンして、スローされるものを確認できます-適度に簡単です。ただし、ほとんどの場合、最終的に動的にバインドされたメソッド(仮想メソッド、リフレクトまたはCOMインターフェイス、dll内の外部ライブラリメソッド、オペレーティングシステムAPIなど)が呼び出され、スローされる可能性のあるものを明確に把握できません(わかりません)エンドユーザーのPCで実際にプログラムを実行するまでの呼び出し-すべてのPCは異なり、(たとえば)WinXPとWin7で実行されるコードはまったく異なる可能性があります。または、仮想メソッドを呼び出してから誰かがプラグを追加するとします。 -メソッドをオーバーライドし、新しいタイプの例外をスローするプログラムに)。この状況を確実に処理する唯一の方法は、メソッド内のすべての例外をキャッチしてから、正確に文書化できる特定の例外を再スローすることです。これができない場合、例外の文書化は「一般的に」にかなり制限されます。メソッドで「スローされ、通常は予期される例外」が発生し、「例外エラー」はほとんど文書化されず、より高いレベルの未処理の例外キャッチブロックに渡されます。 (catch(...)を使用する必要性につながることが多いのは、例外のこの恐ろしい「未定義」の動作です-学術的には「悪」ですが、プログラムを防弾にしたい場合は、catchを使用する必要があります-予期しない状況がアプリケーションを暗殺しないことを確認するためのすべて)。

14
Jason Williams