繁体   English   中英

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

[英]How to document all exceptions a function might throw?

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

像这样(使用Doxygen):

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

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

为此,您可以:

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

我想使用选项4,但我还没有找到一个好的解决方案,也许它可以用Doxygen吗? 或许我只是想要记录多少???

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

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

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

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

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

我提出了以下手动解决方案。 基本上我只是从我打电话的成员那里复制@throw文档。 如果Doxygen有一个类似于@copydoc@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配置文件中添加*_throwsEXCLUDE_SYMBOLS 这可以确保这些组不会显示为模块。

然后B::bar()导致此文档:

void B :: bar()
做点别的。

例外:
FooException
例外:
BarException

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM