如何记录函数可能抛出的所有异常?

时间:2010-12-25 08:59:43

标签: c++ exception documentation doxygen

如果你有一个公共函数可能会抛出一个异常,它使用其他(私有或公共)帮助函数也会抛出异常我认为你应该记录公共函数可以抛出的异常,这包括抛出的异常辅助函数

像这样(使用Doxygen):

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

helperWhichMayThrowException()也调用可能抛出异常的其他函数。

要做到这一点,你可以:

  1. 以递归方式跟踪所有函数theFunction()调用,并查找该函数所引发的异常。这是一项很多工作,当您向助手添加异常时,您可能忘记在某处记录异常。
  2. 捕获theFunction()中帮助程序抛出的所有异常并转换它们,以确保只抛出指定的异常。但那么为什么要使用例外?
  3. 不要担心辅助函数抛出的异常,但是你不能对所有异常进行单元测试,因为你不知道公共函数可以抛出哪些异常
  4. 有一些工具(半)自动列出帮助者抛出的所有异常等。我查看了Doxygen的文档,但没有找到办法做到这一点。
  5. 我想使用选项4,但我还没有找到一个好的解决方案,也许它可以用Doxygen实现?或许我只想记录很多???

    编辑:也许它不是很清楚,但我正在寻找一种简单的方法来记录所有异常(最好使用Doxygen),函数可能会抛出而无需手动检查所有辅助函数。一种简单的方法包括“不记录所有异常”或“捕获并转换theFunction()'

    中的所有异常

2 个答案:

答案 0 :(得分:14)

从根本上说,在几乎每一个现实世界的情况下,你所要求的都是不可能的。

记录抛出的异常有两个部分。

1)简单的一点。记录方法中直接引发的异常。您可以手动执行此操作,但这非常费力,如果您未能通过代码保持文档同步,则文档会产生误导(可能比完全没有文档更糟糕,因为您只能真正信任您确定的文档是100%准确)。我的AtomineerUtils加载项使这更容易实现,因为它可以使代码和文档注释保持同步,而且工作量最小。

2)不可能的一点。记录可能“通过”您的方法的所有异常。这意味着通过您的方法调用的整个方法子树进行递归,以查看它们可能抛出的内容。为什么不可能?好吧,在最简单的情况下,您将静态绑定到已知方法,因此可以扫描它们以查看它们抛出的内容 - 适度容易。但是大多数情况最终调用动态绑定方法(例如虚拟方法,反射或COM接口,dll中的外部库方法,操作系统API等),你无法明确地解决可能抛出的问题(因为你不会知道)直到你真正在最终用户的PC上运行程序才会被调用 - 每台PC都不同,并且在(例如)WinXP和Win7上执行的代码可能完全不同。或者想象你调用虚拟方法然后有人添加插件-in到您的程序,它会覆盖该方法并抛出一种新类型的异常)。可靠处理这种情况的唯一方法是捕获方法中的所有异常,然后重新抛出可以精确记录的特定异常 - 如果你不能这样做,那么异常的记录几乎限于“通常抛出并且通常预期的异常“在你的方法中,留下”异常错误“基本上没有记录,只是简单地传递给更高级别的未处理异常捕获块。 (正是这种可怕的“未定义”异常行为经常导致使用catch(...)的必要性 - 在学术上它是“邪恶的”,但如果你想让你的程序成为防弹,你有时必须使用catch - 以确保意外情况不会刺杀您的应用程序。)

答案 1 :(得分:6)

我提出了以下手动解决方案。基本上我只是复制我呼叫的成员的@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