简体   繁体   English

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

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

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): 像这样(使用Doxygen):

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

and helperWhichMayThrowException() also calls other functions which may throw exceptions. helperWhichMayThrowException()也调用可能抛出异常的其他函数。

To do this you can: 为此,您可以:

  1. recursively follow all functions theFunction() calls and look for exceptions thown by that function. 递归地遵循函数theFunction()函数theFunction()调用的所有函数,并查找该函数所引发的异常。 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. 捕获theFunction()助手抛出的所有异常并转换它们,以确保只抛出指定的异常。 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. 有一些工具(半)自动列出助手抛出的所有异常等。我查看了Doxygen的文档,但没有找到办法做到这一点。

I would like to use option 4 but I have not found a good solution yet, maybe it is doable with Doxygen? 我想使用选项4,但我还没有找到一个好的解决方案,也许它可以用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. 编辑:也许它不是很清楚,但我正在寻找一种简单的方法来记录所有异常(最好使用Doxygen)函数可能抛出而无需手动检查所有辅助函数。 An easy way includes 'do not document all exceptions' or 'catch and transform all exceptions in theFunction() ' 一种简单的方法包括'不记录所有异常'或'捕获并转换theFunction()的所有异常

Fundamentally, what you ask is impossible in virtually every real-world situation. 从根本上说,在几乎每一个现实世界的情况下,你所要求的都是不可能的。

There are two parts to documenting thrown exceptions. 记录抛出异常有两个部分。

1) The easy bit. 1)简单的一点。 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). 您可以手动执行此操作,但这非常费力,如果您未能通过代码保持文档同步,则文档会产生误导(可能比完全没有文档更糟糕,因为您只能真正信任您确定的文档是100%准确)。 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. 我的AtomineerUtils加载项使这更容易实现,因为它可以使代码和文档注释保持同步,而且工作量最小。

2) The impossible bit. 2)不可能的一点。 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 (eg 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 (eg) 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). 但是大多数情况最终调用动态绑定方法(例如虚拟方法,反射或COM接口,dll中的外部库方法,操作系统API等),你无法明确地解决可能抛出的问题(因为你不会知道)直到你真正在最终用户的PC上运行程序才会被调用 - 每台PC都不同,并且在(例如)WinXP和Win7上执行的代码可能完全不同。或者想象你调用虚拟方法然后有人添加插件-in到您的程序,它会覆盖该方法并抛出一种新类型的异常)。 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). (正是这种可怕的“未定义”异常行为经常导致使用catch(...)的必要性 - 在学术上它是“邪恶的”,但如果你想让你的程序成为防弹,你有时必须使用catch - 以确保意外情况不会刺杀您的应用程序)。

I came up with the following manual solution. 我提出了以下手动解决方案。 Basically I just copy the @throw documentation from members I call. 基本上我只是从我打电话的成员那里复制@throw文档。 It would be nice if Doxygen had a @copythrows similar to @copydoc , but the following will work: 如果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();
};  

Then in the Doxyfile configuration file add *_throws to EXCLUDE_SYMBOLS . 然后在Doxyfile配置文件中添加*_throwsEXCLUDE_SYMBOLS This makes sure these groups do not show up as modules. 这可以确保这些组不会显示为模块。

Then B::bar() results in this documentation: 然后B::bar()导致此文档:

void B::bar() void B :: bar()
Do something else. 做点别的。

Exceptions: 例外:
FooException FooException
Exceptions: 例外:
BarException BarException

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

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