C++ 如何记录函数可能引发的所有异常?

C++ 如何记录函数可能引发的所有异常?,c++,exception,documentation,doxygen,C++,Exception,Documentation,Doxygen,如果您有一个公共函数,它可能会抛出一个异常,该异常使用其他(私有或公共)辅助函数,这些辅助函数也可以抛出异常,我认为您应该记录公共函数可以抛出哪些异常,这包括辅助函数抛出的异常 类似这样的情况(使用强氧剂): 和helperWhichMayThrowException()还调用可能引发异常的其他函数 为此,您可以: 递归地跟踪所有函数theFunction()调用,并查找该函数所包含的异常。这是一项大量的工作,当您向助手添加异常时,您可能会忘记在某处记录异常 在theFunction()中捕获帮

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

类似这样的情况(使用强氧剂):

helperWhichMayThrowException()
还调用可能引发异常的其他函数

为此,您可以:

  • 递归地跟踪所有函数
    theFunction()
    调用,并查找该函数所包含的异常。这是一项大量的工作,当您向助手添加异常时,您可能会忘记在某处记录异常
  • theFunction()
    中捕获帮助程序引发的所有异常,并对其进行转换,以便确保只引发指定的异常。但是为什么要使用异常呢
  • 不要担心帮助函数引发的异常,但是您不能对所有异常进行单元测试,因为您不知道公共函数可以引发哪些异常
  • 有一些工具可以(半)自动列出助手等抛出的所有异常。我查阅了Doxygen的文档,但没有找到一种方法来做到这一点
  • 我想使用选项4,但我还没有找到一个好的解决方案,也许使用强氧是可行的?或者也许我只是想记录更多


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

    基本上,您所要求的在几乎所有实际情况下都是不可能的

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

    1) 简单的一点。记录方法中直接抛出的异常。您可以手动完成这项工作,但这相当费劲,如果您无法使文档与代码保持同步,文档就会产生误导(可能比根本没有文档更糟糕,因为您只能真正信任您确信100%准确的文档)。我的外接程序使这更容易实现,因为它以最少的工作量保持代码和文档注释的同步


    2) 不可能的一点。记录可能“通过”您的方法的所有异常。这意味着递归方法调用的方法的整个子树,以查看它们可能抛出什么。为什么不可能?嗯,在最简单的情况下,您将静态绑定到已知方法,因此可以扫描它们以查看它们抛出了什么-相当简单。但大多数情况下最终都会调用动态绑定的方法(例如,虚拟方法、反射或COM接口、DLL中的外部库方法、操作系统API等),您无法确定会抛出什么(因为在最终用户的电脑上实际运行程序之前,您不知道调用了什么-每台电脑都是不同的,在(例如)WinXP和Win7上执行的代码可能完全不同。或者想象一下,您调用一个虚拟方法,然后有人向您的程序添加一个插件,该插件覆盖该方法并引发新类型的异常)。可靠地处理这种情况的唯一方法是捕获方法中的所有异常,然后重新抛出可以精确记录的特定异常-如果不能做到这一点,则异常的记录几乎仅限于方法中的“通常抛出和通常预期的异常”,留下“异常错误”基本上没有文档记录,只是传递到更高级别的未处理异常捕获块。(正是异常的这种可怕的“未定义”行为常常导致使用catch的必要性(…)-学术上是“邪恶的”,但如果您希望您的程序是防弹的,您有时必须使用“捕获所有”来确保意外情况不会刺杀您的应用程序).

    我想出了以下手动解决方案。基本上,我只是从我打电话给的成员那里复制
    @throw
    文档。如果Doxygen有一个类似于
    @copydoc
    @copydrows
    ,那就太好了,但下面的方法会起作用:

    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
    配置文件中添加
    *\u抛出
    排除\u符号
    。这确保这些组不会显示为模块

    然后
    B::bar()
    将生成以下文档:

    无效B::bar()
    做点别的

    例外情况:
    FooException
    例外情况:
    毫无例外


    假设一个特定的异常可以被抛出,但假设它不会被抛出,这是有意义的。例如,考虑<代码> STD::BADYOLLC/<代码>。您应该总是假定它可以被许多操作抛出,例如动态分配或容器操作,并且您应该使用RAII。Howe来防御代码。当然,这并不意味着你需要到处放置处理程序,因为在大多数应用程序中,你不太可能看到它,而且当它确实发生时,你不太可能不费吹灰之力就从中恢复过来。@James McNellis:好的,对于一些例外情况来说,这是有意义的,但是除了某些帮助程序抛出的异常?为什么要记录异常?这会使所有异常的想法都变得无用。请阅读为什么异常规范无用。您应该定义函数在输入方面的行为,如果这意味着必须记录某些帮助程序函数异常,则可以。请注意,异常这是因为函数中的一些不变量被破坏而抛出的,可能不需要记录——事实上,最好是
    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();
    };