How to document all exceptions a function might throw?

25,810

Solution 1

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

Solution 2

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).

Share:
25,810
rve
Author by

rve

Updated on July 27, 2020

Comments

  • rve
    rve almost 4 years

    If you have a public function which may throw an exception which uses other (private or public) helper functions which can also throw exceptions I think you should document what exceptions the public function can throw and this includes exceptions thrown by the helper functions.

    Something like this (using Doxygen):

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

    and helperWhichMayThrowException() also calls other functions which may throw exceptions.

    To do this you can:

    1. recursively follow all functions theFunction() calls and look for exceptions thown by that function. This is a lot of work and you might forget to document an exception somewhere when you add an exception to a helper.
    2. catch all exceptions thrown by helpers in theFunction() and convert them so you are sure only the exceptions you specify are thrown. But then why use exceptions?
    3. do not worry about exceptions thrown by helper functions but then you can not unittest all exceptions because you do not know which exceptions can be thrown by the public function
    4. have some tool which (semi)automatically lists all exceptions thrown by helpers etc. I looked in the documentation of Doxygen but did not find a way to do this.

    I would like to use option 4 but I have not found a good solution yet, maybe it is doable with Doxygen? Or maybe I just want to document to much???

    edit: Maybe its not really clear but I am looking for an easy way to document all exceptions (preferably using Doxygen) a function might throw without manually checking all helper functions. An easy way includes 'do not document all exceptions' or 'catch and transform all exceptions in theFunction()'