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

rve*_*rve 26 c++ documentation doxygen exception

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

像这样(使用Doxygen):

/** 
 * @throw Exception ...
 * @throw ExceptionThrownByHelper ...
 * @throw ExceptionThrownByHelpersHelper ...
 */
void theFunction() 
{ 
    helperWhichMayThrowException();
}
Run Code Online (Sandbox Code Playgroud)

并且helperWhichMayThrowException()还调用可能抛出异常的其他函数.

为此,您可以:

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

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

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

Jas*_*ams 14

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

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

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

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

  • 我认为跟踪所有抛出异常的唯一方法是将它们作为方法/函数签名的一部分,就像在Java中完成一样.然后,重写的方法不会抛出意外的异常,因为这将涉及更改方法签名. (2认同)

rve*_*rve 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();
};  
Run Code Online (Sandbox Code Playgroud)

然后在Doxyfile配置文件中添加*_throwsEXCLUDE_SYMBOLS.这可以确保这些组不会显示为模块.

然后B::bar()在此文档中得出结果:

void B :: bar()
做别的事情.

异常:
  FooException
异常:
  BarException