C++ 如何记录函数可能抛出的所有异常?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4529810/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to document all exceptions a function might throw?
提问by rve
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:
为此,您可以:
- 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. - 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? - 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
- 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.
- 递归跟踪所有函数
theFunction()
调用并查找该函数抛出的异常。这是一项大量工作,当您向帮助程序添加异常时,您可能会忘记在某处记录异常。 - 捕获助手抛出的所有异常
theFunction()
并转换它们,这样您就可以确定只抛出您指定的异常。但是为什么要使用异常呢? - 不要担心辅助函数抛出的异常,但是你不能对所有异常进行单元测试,因为你不知道公共函数可以抛出哪些异常
- 有一些工具可以(半)自动列出助手等抛出的所有异常。我查看了 Doxygen 的文档,但没有找到执行此操作的方法。
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???
我想使用选项 4,但我还没有找到好的解决方案,也许 Doxygen 可行?或者我只是想记录很多???
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()
'
编辑:也许它不是很清楚,但我正在寻找一种简单的方法来记录所有异常(最好使用 Doxygen)一个函数可能会在不手动检查所有辅助函数的情况下抛出。一种简单的方法包括“不记录所有异常”或“捕获并转换所有异常theFunction()
”
采纳答案by rve
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:
我想出了以下手动解决方案。基本上我只是@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();
};
Then in the Doxyfile
configuration file add *_throws
to EXCLUDE_SYMBOLS
. This makes sure these groups do not show up as modules.
然后在Doxyfile
配置文件中添加*_throws
到EXCLUDE_SYMBOLS
。这可确保这些组不会显示为模块。
Then B::bar()
results in this documentation:
然后B::bar()
导致此文档:
void B::bar()
Do something else.Exceptions:
FooException
Exceptions:
BarException
void B::bar()
做其他事情。异常:
FooException
异常:
BarException
回答by Jason Williams
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 AtomineerUtilsadd-in makes this much easier to achieve, as it keeps the code and doc comments in sync with a minimum of effort.
1)容易的一点。记录在您的方法中直接抛出的异常。您可以手动执行此操作,但它非常费力,而且如果您未能使文档与代码保持同步,文档就会变得具有误导性(可能比根本没有文档更糟糕,因为您只能真正信任您确定的文档100% 准确)。我的AtomineerUtils插件使这一点更容易实现,因为它以最少的努力使代码和文档注释保持同步。
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).
2)不可能的位。记录所有可能“通过”您的方法的异常。这意味着递归遍历您的方法调用的整个方法子树,以查看它们可能会抛出什么。为什么不可能?好吧,在最简单的情况下,您将静态绑定到已知方法,因此可以扫描它们以查看它们抛出的内容 - 相当容易。但是大多数情况最终会调用动态绑定的方法(例如虚拟方法、反射或 COM 接口、dll 中的外部库方法、操作系统 API 等),您无法明确地计算出可能抛出的内容(因为您不会知道)在您实际在最终用户的 PC 上运行该程序之前调用的是什么 - 每台 PC 都不同,并且在(例如)WinXP 和 Win7 上执行的代码可能完全不同。或者想象一下,您调用了一个虚拟方法,然后有人向您的程序添加了一个插件,该插件覆盖了该方法并引发了一种新类型的异常)。可靠地处理这种情况的唯一方法是在您的方法中捕获所有异常,然后重新抛出可以精确记录的特定异常 - 如果您不能这样做,那么异常记录几乎仅限于“通常抛出和通常预期的异常”在您的方法中,留下“异常错误”在很大程度上没有记录,并简单地传递给更高级别的未处理异常捕获块。(正是异常的这种可怕的“未定义”行为常常导致使用 catch(...) 的必要性——在学术上它是“邪恶的”,但如果你想让你的程序防弹,