How to document all exceptions a function might throw?

给你一囗甜甜゛ 提交于 2019-12-02 20:15:42

Fundamentally, what you ask is impossible in virtually every real-world situation.

There are two parts to documenting thrown exceptions.

1) The easy bit. Document the exceptions that are directly thrown in your method. You can do this by hand, but it's pretty laborious and if you fail to keep the docs in sync wiht the code the documentation becomes misleading (potentially worse than having no documentation at all, as you can only really trust documentation that you're sure is 100% accurate). My AtomineerUtils add-in makes this much easier to achieve, as it keeps the code and doc comments in sync with a minimum of effort.

2) The impossible bit. Document all exceptions that might "pass through" your method. This means recursing through the entire subtree of methods called by your method to see what they might throw. Why is it impossible? Well, in the simplest cases you will be statically binding to known methods, and can therefore scan them to see what they throw - moderately easy. But the majority of cases ultimately call dynamically bound methods (e.g. virtual methods, reflected or COM interfaces, external library methods in dlls, operating system APIs, etc) for which you cannot definitively work out what might be thrown (as you won't know what is called until you actually run the program on the end-user's PC - every PC is different, and the code executed on (e.g.) WinXP and Win7 could be quite different. Or imagine you call a virtual method and then somebody adds a plug-in to your program that overrides the method and throws a new type of exception). The only way to reliably handle this situation is to catch all exceptions in your method, and then re-throw specific ones that can then be documented precisely - if you can't do this, then documentation of exceptions is pretty much restricted to "commonly thrown and typically expected exceptions" in your method, leaving "exceptional errors" to be left largely undocumented and simply passed up to a higher level unhandled-exception catch blocks. (It is this horrible "undefined" behaviour of exceptions that often leads to the necessity of using catch(...) - academically it is "evil", but if you want your program to be bullet proof, you sometimes have to use catch-alls to be sure that unexpected situations don't assassinate your application).

I came up with the following manual solution. Basically I just copy the @throw documentation from members I call. It would be nice if Doxygen had a @copythrows similar to @copydoc, but the following will work:

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();
};  

Then in the Doxyfile configuration file add *_throws to EXCLUDE_SYMBOLS. This makes sure these groups do not show up as modules.

Then B::bar() results in this documentation:

void B::bar()
Do something else.

Exceptions:
  FooException
Exceptions:
  BarException

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!