关于c ++:异常与错误代码与断言

Exception vs. error-code vs. assert

我正在开发一个生成设备报告的库。generate_report (const std::string& no)成员函数可能由于各种原因而失败:

  • 报告编号无效。
  • 无效状态(report_generator为fsm)
  • 没有设备处于活动状态
  • 生成报告时出错
  • 哪种错误处理机制最适合这些错误?

    • 只需返回truefalse
    • 返回错误代码
    • 断言日志
    • 引发异常
    • 以上任意组合

    一些上下文信息:正常的工作流如下。用户激活设备,从列表中选择报告并单击"生成"。

    编辑:感谢您的回复!对于我来说,现在很清楚何时使用断言以及何时进行错误处理。在错误处理方面,错误代码和异常都有优缺点。我认为我会选择异常(并为上述错误创建四个类),但我还没有真正信服。我总是想到"意外情况"的例外。一个无效的报告"否"并不出乎意料。有什么建议吗?:)


    其中任何一个都有不同的用途:

    • 错误代码版本。异常:异常和错误代码表示如何处理结果代码的不同习惯用法。异常更为健壮-结果代码可以被忽略或丢失。库通常应该强烈区分抛出异常的位置/内容,以及何时使用错误代码。充其量只能使用两者中的一个。

    • 返回truefalse:错误代码的专门化。通常是最坏的想法——只有当没有好的或坏的报告时才是好的(即malloc返回好的或坏的(=NULL)。

    • 断言和日志:这些是调试技术,不应用作向用户/客户机报告的机制。断言只是说"发生了我无法处理的事情-我退出了"。


    断言不是正确的选择。当您有一个不变量时使用断言;这是不应该发生的事情。不要做像assert()这样的事情:如果参数是错误条件而不是不变量,那么它永远不会为空。

    如果是我,我会在接口中使用异常,如果必须这样做,我会使用内部使用的函数来翻译错误代码,如果它们不使用异常的话。只是要保持一致(不要对这些东西使用断言)。


    与真/假和错误代码相比,异常具有以下几个重要优势:

    • 不能忽略异常。如果代码引发异常,调用方必须捕获它以避免获得未处理的异常。
    • 异常可以在比直接调用程序更高的级别处理。如果您使用错误代码,那么可能会在应用程序的所有层都必须检查错误并将其传递回调用者的情况下结束。

    断言用于表示代码中的前提条件之类的东西,并希望在开发期间发现任何错误。但是,您不应该依赖发布代码中的断言,并且出于性能原因,通常从发布代码中删除断言。


    我建议阅读Boost社区指南[boost.org]了解异常和错误处理。


    我将违背常规,同时建议错误代码和异常,但仅仅是因为您正在创建一个库。既然你说你正在创建一个库,我猜这个库将被那些你无法控制的人编写的代码所使用。因此,让您的代码对不同的编译器,甚至可能是语言友好是一件好事。

    因此,我将编写C++异常库,并提供详细说明异常类的头文件。我还将编写一个C接口来处理用户的异常。现在,用户可以链接任何界面:

    1
    2
    3
    4
    5
    6
    7
    #ifdef __cplusplus__
    void generate_report(const std::string& rep_number, ostream& output);

    extern"C"
    #endif
    int generate_report(const char* rep_number, const char* outputfilename,
                        int* error_code, char* error_text, int max_error_text_len);

    C实现调用C++实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    extern"C"
    int generate_report(const char* rep_number, const char* outputfilename,
                        int* error_code, char* error_text, int max_error_text_len)
    {
        ofstream os;
        try {
            os.open(outputfilename, IOS_WRITE);
            generate_report(rep_number, os);
            os.close();
            return TRUE;
        } catch (base_exception& e) {
            os.close();
            if (error_code) *error_code = e.error_code();
            if (error_text) strncpy(error_text, e.str(), max_error_text_len);
            return FALSE;
        }
    }

    您报告的设备的可靠性如何?

    我这样问是因为对于一大类设备来说,没有连接、没有打开、没有电池、忙于做其他事情等都是相当正常的状态。

    如果是这种情况,如果设备不可用,我希望返回状态代码(注意不是错误代码)。

    另一方面,如果您认为这些设备超级可靠,而且它们没有响应真的是异常的,那么异常处理可能就是解决方法。

    这并不重要,因为"异常"实际上只是一种编码"if(x!"的奇特方式。=0)转到错误例程,但是,我个人更喜欢异常处理来处理异常情况,而不是处理诸如文件结尾之类的常规事件。


    选择什么样的策略往往是个问题。我说去挑选最能与图书馆客户融为一体的东西。如果他们采用异常策略,则使用异常。如果他们习惯了错误代码,就坚持使用它。


    • 如果您无法访问终端来生成/读取错误报告,则应使用日志记录。
    • 返回"真/假"应与错误代码结合使用。示例:函数成功时返回true,错误时返回false,并使用适当的错误代码/说明设置变量(全局或参数,由您选择)。
    • 例外:在我看来,将它们与日志记录和优雅的错误恢复结合起来是很好的。如果这是不可能的,您也可以求助于错误代码,作为例外,然后没有提供额外的好处。
    • assert():正如其他人指出的那样,它在发布版本上编译,所以可以随意触发。

    2C


    第一,要始终如一!

    第二:

    • 仅仅是对/错是不够的。它必须与错误代码结合在一起(例如,false+getlasterror)。
    • 错误代码很快,但要构建一些基础结构,以便轻松地将它们转换为字符串。
    • 断言/日志:不,您希望应用程序能够对错误作出响应
    • 异常比错误代码慢,但更容易用困难的控制流编程。
    • 组合:只有真/假+错误码组合,其余的一致,即:不要组合。