专栏首页C++核心准则原文翻译C++核心准则E.27:如果无法抛出异常,系统化运用错误处理代码

C++核心准则E.27:如果无法抛出异常,系统化运用错误处理代码

E.27: If you can't throw exceptions, use error codes systematically

E.27:如果无法抛出异常,系统化运用错误处理代码

Reason(原因)

Systematic use of any error-handling strategy minimizes the chance of forgetting to handle an error.

系统化运用任何错误处理策略都可以减少忘记处理某个错误的可能性。

See also: Simulating RAII

参见:模仿RAII方式进行资源管理

Note(注意)

There are several issues to be addressed:

有多个课题需要说明:

  • How do you transmit an error indicator from out of a function?
  • 如何在函数外部发送错误指示器?
  • How do you release all resources from a function before doing an error exit?
  • 如何在执行发生错误从函数退出之前释放所有资源?
  • What do you use as an error indicator?
  • 使用什么作为错误指示器?

In general, returning an error indicator implies returning two values: The result and an error indicator. The error indicator can be part of the object, e.g. an object can have a valid() indicator or a pair of values can be returned.

一般情况下,返回错误指示器包含包含两个值:结果和错误指示器。错误指示器可以是对象的一部分,例如对象可以包含一个valid()检查函数或者一对可以返回的值。

Example(示例)

Gadget make_gadget(int n)
{
    // ...
}

void user()
{
    Gadget g = make_gadget(17);
    if (!g.valid()) {
            // error handling
    }
    // ...
}

This approach fits with simulated RAII resource management.

这个做法符合模拟RAII的资源管理。

The valid() function could return an error_indicator (e.g. a member of an error_indicator enumeration).

valid()函数可以返回error_indicator(例如,枚举类型error_indicator的某个成员。

Example(示例)

What if we cannot or do not want to modify the Gadget type? In that case, we must return a pair of values. For example:

如果我们不能或不想修改Gadget类型时该怎么办呢?这种情况,我们必须返回值对。例如:

std::pair<Gadget, error_indicator> make_gadget(int n)
{
    // ...
}

void user()
{
    auto r = make_gadget(17);
    if (!r.second) {
            // error handling
    }
    Gadget& g = r.first;
    // ...
}

As shown, std::pair is a possible return type. Some people prefer a specific type. For example:

如代码所示,std::pair是可能的返回值类型。有些人更愿意使用特殊类型。例如:

Gval make_gadget(int n)
{
    // ...
}

void user()
{
    auto r = make_gadget(17);
    if (!r.err) {
            // error handling
    }
    Gadget& g = r.val;
    // ...
}

One reason to prefer a specific return type is to have names for its members, rather than the somewhat cryptic first and second and to avoid confusion with other uses of std::pair.

更愿意使用特殊返回值类型的一个原因是可以为成员命名,而不是有些难以理解的first和second,另外的好处就是可以和使用std::pair的其他代码混淆。

Example(示例)

In general, you must clean up before an error exit. This can be messy:

一般情况下,你必须在错误退出之前执行清理动作。这可能产生凌乱的代码:

std::pair<int, error_indicator> user()
{
    Gadget g1 = make_gadget(17);
    if (!g1.valid()) {
        return {0, g1_error};
    }

    Gadget g2 = make_gadget(31);
    if (!g2.valid()) {
        cleanup(g1);
        return {0, g2_error};
    }

    // ...

    if (all_foobar(g1, g2)) {
        cleanup(g2);
        cleanup(g1);
        return {0, foobar_error};
    }

    // ...

    cleanup(g2);
    cleanup(g1);
    return {res, 0};
}

Simulating RAII can be non-trivial, especially in functions with multiple resources and multiple possible errors. A not uncommon technique is to gather cleanup at the end of the function to avoid repetition (note the extra scope around g2 is undesirable but necessary to make the goto version compile):

模拟RAII可能需要特别处理,特别是包含多个资源和多个错误的时候。一个并不罕见的技术是将清除动作集中在函数末尾以避免重复(注意包含g2的额外作用域本来是不需要的,只是为了让goto版本代码通过编译)

std::pair<int, error_indicator> user()
{
    error_indicator err = 0;
    int res = 0;

    Gadget g1 = make_gadget(17);
    if (!g1.valid()) {
        err = g1_error;
        goto g1_exit;
    }

    {
        Gadget g2 = make_gadget(31);
        if (!g2.valid()) {
            err = g2_error;
            goto g2_exit;
        }

        if (all_foobar(g1, g2)) {
            err = foobar_error;
            goto g2_exit;
        }

        // ...

    g2_exit:
        if (g2.valid()) cleanup(g2);
    }

g1_exit:
    if (g1.valid()) cleanup(g1);
    return {res, err};
}

The larger the function, the more tempting this technique becomes. finally can ease the pain a bit. Also, the larger the program becomes the harder it is to apply an error-indicator-based error-handling strategy systematically.

函数越大,使用这类技术的诱惑越大。finally可以稍微减轻痛苦。同时,问题越大,基于错误指示器的系统化错误处理策略就越难运用。

We prefer exception-based error handling and recommend keeping functions short.

我们比较喜欢基于异常的错误处理并且推荐保持函数短小。

See also: Discussion

参见:问题讨论

See also: Returning multiple values

参见:如果需要返回多个输出值,最好返回结构体或者tuple

Enforcement(实施建议)

Awkward.

不容易。

原文链接

https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#e26-if-you-cant-throw-exceptions-consider-failing-fast

本文分享自微信公众号 - 面向对象思考(OOThinkingDalian),作者:面向对象思考

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-08-09

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • C++核心准则CP.1: 设想你的代码​会成为多线程程序的一部分

    It's hard to be certain that concurrency isn't used now or won't be used sometim...

    面向对象思考
  • C++核心准则E.14:使用根据目的设计的用户定制类型异常(非内置类型)

    E.14: Use purpose-designed user-defined types as exceptions (not built-in types)

    面向对象思考
  • C++核心准则ES.34:不要定义C风格的可变参数函数

    Not type safe. Requires messy cast-and-macro-laden code to get working right.

    面向对象思考
  • Android网络请求框架之Retrofit实践

    网络访问框架经过了从使用最原始的AsyncTask构建简单的网络访问框架(甚至不能称为框架),后来使用开源的android-async-http库,再到使用go...

    xiangzhihong
  • Mac环境变量设置(以ADB为例)

    按回车输入密码后用vi打开用户目录下的bash_profile文件。一定要用sudo,否则没权限保存文件。

    用户7744319
  • Python比特币公链技术架构介绍

    作者:csunny,具有多年开发经验,有前后端开发经验,熟悉python、go、nodejs等多种语言,目前在国内某一线互联网大厂工作,主要从事devops以及...

    Python中文社区
  • Linux系统(Ubantu16.04)安装Pytorch

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.n...

    TeeyoHuang
  • 为什么说二叉树遍历用递归的方法不如非递归方法?

    作者:find goo 链接:https://www.zhihu.com/question/24976589/answer/128338947 来源:知乎 著作...

    九州暮云
  • 傲腾这么厉害?QLC闪存笑了!

    傲腾(Optane)是Intel在存储器方面的重量级产品。其采用3D Xpoint存储非易失介质来存储数据。3D Xpoint的一大特点就是延迟更加接近SDRA...

    冬瓜哥
  • 腾讯围棋 AI 技术 PhoenixGo 正式开源,源码、模型全公开

    AI 研习社消息,5 月 11 日,腾讯微信团队研发的 PhoenixGo 正式开源,这也是国内第一个开源的围棋 AI 项目。

    AI研习社

扫码关注云+社区

领取腾讯云代金券