代码之家  ›  专栏  ›  技术社区  ›  Valentin H

奇怪的模式:所有函数/方法在C++中使用相同的类型返回错误代码

  •  2
  • Valentin H  · 技术社区  · 15 年前

    在我最近的两个项目中,我看到了奇怪的指导原则,“所有方法/函数都应该使用一些常见的错误代码类型返回错误代码”。在两个项目中,错误代码都是int typedef。

    在C++中有什么好的理由吗?一些米斯拉的要求或类似的东西?

    我只能看到缺点:

    • 如果函数应该返回一个值,则通过参数引用来完成。例如。:

      字符串S; 错误_code err=getstring;

    • 函数的重要性并不明显。一切看起来都一样。错误列表包含数百个错误,从低级错误到某些特定于域的错误。

    您体验过这种编程风格吗?有没有好的论据反对或赞成?

    5 回复  |  直到 15 年前
        1
  •  3
  •   R.. GitHub STOP HELPING ICE    15 年前

    我认为这是一个非常糟糕的风格有几个原因。

    1. 如您所说,它强制您传递指针/引用来存储函数的实际结果。
    2. 正如您所说,统一的错误代码是丑陋的,因为它试图统一来自各种域的各种错误。
    3. 它创建了所有程序模块对错误代码系统的人工依赖关系,使得在其他程序中重用单个模块或模块的小子集变得很困难。
    4. 此外,由于某些错误代码是特定于域的,它实际上引入了不相关的对象类型/模块之间的依赖性,因为它们都依赖于依赖于所有可能错误类型联合的组件。

    我的观点是,任何一个函数/方法的失败方式不只是一个小的可管理的数量,要么过于复杂,要么被很难分解,可能两者都有。

    如果您真的想返回错误代码,我将交换一些内容,并将指向错误代码的指针作为参数传递给函数,并使实际结果成为返回值。然后,我将选择以下两种实现错误代码的方法之一:

    1. 简单的方法:丢弃错误代码的所有抽象,并简单地使用 int 有几个通用的错误类。
    2. 重面向对象的方法:提供一个指向内部“错误对象”的指针,其中基类是非常抽象的,可以在所有组件之间共享,而不引入任何依赖关系,并且每个组件在需要时定义自己的特定于组件的错误对象。

    如果使用C++,更好的方法可能是使用异常…

        2
  •  1
  •   Joshua    15 年前

    我已经看过了。

    内核编程就是这样,除非只有一个错误是可能的。

    这听起来不是一个好主意,但也不是一个坏主意。

        3
  •  1
  •   Steve Townsend    15 年前

    对于团队来说,就返回错误的常见方法达成一致并不罕见,因为这有助于为项目代码创建一个通用的“外观和感觉”,就像任何其他团队范围的编码约定一样。这可以帮助新的团队成员更快地了解整体情况,并使团队内部对其他人代码的维护更加直观。

    不过,我觉得奇怪的是,C++项目是在错误后面而不是在异常之外统一起来的。有人讨论了使用异常与错误代码的优缺点。 here .

    我想支持错误代码处理的一个论点是,如果您使用的是一个C风格的API,它将引导您使用这种方法(咳……Win32…咳嗽)

        4
  •  1
  •   Jakub Wieczorek    15 年前

    这个成语很常见,特别是在C世界。

    尽管我自己不使用它,而且我认为它弊大于利(其他答案中的弊大于利),但我确实发现了它的一个优点:一种向呼叫站点报告意外错误的一致方法。类似errno变量,但更容易使用。

    例如,考虑一组函数:

    int a();
    std::string b();
    double c();
    std::list<long> d();
    

    上面的每个函数都以不同的方式指示失败:a()可以返回-1,b()空字符串,c()a 0.0和d()空列表。这是不一致的,不太直观。现在想象一个函数,它的范围覆盖了它返回类型的整个可能范围。更糟的是。

    一些API还可以:

    int x(bool* ok);
    

    但这也会用一个额外的参数污染每个函数。

    在C中,以一种好的方式做的可能性不大,不幸的是,如果您真的需要设计这样一个API,它将指示不同类型的失败。但是,在C++世界中,可以只使用异常。

        5
  •  1
  •   dennycrane    15 年前

    我已经看到了这样一个论点:当连接到由另一个编译器编译的C++库时,其编译二进制文件时,异常可能不起作用。虽然这种不工作可能是完全正确的,但实际上,即使是连接过程也不需要工作(尽管每个人都可能坚持标准),因此,理论上,这一论点是无效的。但实际上,它 可以 be(我在这里没有经验,抱歉),名称管理冲突很少出现,对齐冲突很少出现,而且,好吧,所有其他特定于实现的东西都得到了广泛的同意,除了例外。

    我看到的第二个论点是运行时性能。在发生异常时展开堆栈 是 代价昂贵,我还没有看到一个公平的基准,将异常与实际数量的返回代码检查进行比较。

    在我的典型C++中,我使用混合。对于那些我真的不希望经常发生的事情,或者那些被测量为很少执行的代码路径,我使用较慢的异常,但是对于那些更可能中断并可能经常调用的事情,我返回代码。 在一个紧密的循环中抛出异常,因为在每个迭代中都有一些有趣的条件是不便宜的(假设循环体处理它)。