【问题标题】:Coding conventions for method returns in C++C++ 中方法返回的编码约定
【发布时间】:2010-09-22 18:18:23
【问题描述】:

我观察到成功完成方法预期功能的一般编码约定是 0。(如在 exit(0) 中)。

这让我很困惑,因为如果我的 if 语句中有方法并且方法返回 0,则“if 条件”为假,从而促使我思考一下该方法是否失败。当然我知道我必须附加一个“!” (如 if(!Method()) ),但这种约定不是自相矛盾吗??

【问题讨论】:

  • 没有通用的编码约定(尽管希望每个 API 至少在内部是一致的)。

标签: c++ c naming-conventions conventions


【解决方案1】:

您需要区分错误代码和错误标志。代码是代表任意数量错误的数字,而标志是表示成功的布尔值。

当谈到错误代码时,想法是:成功只有一种方法,但失败的方法有很多。把 0 当作一个好的单一唯一数字,代表成功,那么你每隔一个数字就是一种表示失败的方式。 (任何其他方式都没有任何意义。)

对于错误标志,想法很简单:True 表示有效,false 表示无效。然后,您通常可以通过其他方式获取错误代码,并采取相应措施。

有些函数使用错误代码,有些使用错误标志。除非您尝试将所有内容都视为标志,否则没有什么令人困惑或倒退的地方。并不是所有的返回值都是一个标志,这只是你必须习惯的东西。

请记住,在 C++ 中,您通常会处理带有异常的错误。无需查找错误代码,您只需从捕获的异常中获取必要的信息。

【讨论】:

    【解决方案2】:

    约定本身并不矛盾,它与您对函数的期望用途相矛盾。

    其中一个必须改变,这不会成为惯例;-)

    我通常会写:

    if (Function() == 0) {
        // code for success
    } else {
        // code for failure
    }
    

    if (Function() != 0) 反之亦然。

    整数可以隐式转换为布尔值,但这并不意味着您总是应该这样做。 0 这里指的是0,并不代表false。如果你真的想,你可以写:

    int error = Function();
    if (!error) {
        // code for success
    } else {
        // code for failure, perhaps using error value
    }
    

    【讨论】:

      【解决方案3】:

      有各种约定,但 C 函数最常见的是在失败时返回 0,在成功时返回正值,因此您可以在 if 语句中使用它(几乎所有 CPU 都有条件跳转,可以测试是否值是否为 0,这就是为什么在 C 中这个“滥用”,0 表示假,其他一切都表示真)。

      另一个约定是在错误时返回 -1,而在成功时返回一些其他值(您尤其会在设置 errno 变量的 POSIX 函数中看到这一点)。这就是0可以解释为“成功”的地方。

      然后是exit。它是不同的,因为返回的值不是由 C 解释的,而是由 shell 解释的。而这里的值0 表示成功,所有其他值都表示错误条件(很多工具会告诉你这个值发生了什么类型的错误)。这是因为在 shell 中,通常只有 0-127 的范围来返回有意义的值(历史原因,它是一个无符号字节,高于 127 的所有内容都意味着被某些信号 IIRC 杀死)。

      【讨论】:

      • “C 函数最常见的是在失败时返回 0,在成功时返回正值”... 你的意思是像 printfread?在这些情况下,结果就是处理的数据量。是否存在结果不是字节数或类似结果但返回值 >0 表示成功的情况?
      • 不,我的意思是大多数返回布尔值或指针的“用户”级函数。在这两种情况下,0 是假/失败,其他一切都是真/成功(获得有效指针对我来说是成功的 ;-)。至少在我参与过的大多数 C 项目中都是这种情况,YMMV。正如我所说,几乎所有 POSIX 函数,如 readprintfstatopen 等,都会在出错时返回 -1。
      【解决方案4】:

      您已将您的问题标记为[c] [c++]。它是哪一个?因为答案会有所不同。

      你说:

      我观察到成功完成方法预期功能的一般编码约定是 0。

      对于 C,这很好。

      对于 C++,它肯定是不是 C++ 有另一种机制来表示失败(即异常)。滥用(数字)返回值通常是糟糕设计的标志。

      如果由于某些原因无法使用异常,还有其他方法可以发出失败信号,而不会阻塞方法的返回类型。替代方法包括返回bool(考虑方法try_insert)或使用无效/保留返回值来表示失败,例如string::npos,当在字符串中未找到出现时,string::find 方法使用。

      【讨论】:

      • @Goz:这就是我写的。但是使用数字错误代码简直是疯了。类型安全,有人吗?你如何处理真正的返回值(如果有的话)?使用参考参数?不不不。糟糕的设计。在设计审查中,我会无条件拒绝数字错误代码。
      • 此外,在 C++ 中,如果您只想返回“成功”与“失败”(例如 empty()),则返回 bool。如果你想返回一个结果代码,其中有一个“成功”和几个“失败”的情况,但你不想像异常那样中断控制流(例如,你只是要转发结果在其他地方编写代码,例如记录日志),返回int(结果代码),其中== 0 是成功,!= 0 是失败。如果您有“错误”情况(即无法按预期工作但可以以降级方式工作),则返回int,其中== 0 是成功,>0 是错误,<0 是失败。
      • @Konrad:实际上,使用参考参数输出并返回结果代码(或类似的东西)是标准 C 设计的 C++ 风格。请参阅 POSIX strtol() 函数。另外,我不确定您所指的类型安全问题与结果代码有关。我认为您的意思是命名空间问题(即两个不同的错误可以具有相同的代码)。
      • @Mike:两件事。首先,没有人可以声称strtol 是一个C++ 风格的函数。这不是设计好的 C++ API 的方式。这是传统 C API 的经典示例,偶然也用于 C++。其次,类型安全问题。 C++ 有一个可扩展的类型系统。您创建自定义类来引用自定义实体。使用整数来指代错误是一种设计错误。如果要表示错误,请为其编写一个类。 enum 可能适用于非常受限的解决方案,但通常它们也不太适合。这一切都是真正的 API 设计 101。
      • @Konrad:Mike 是正确的,返回错误代码(使用枚举是我的偏好)并在 c++ 方法的引用中返回值没有问题。这是常见的做法和良好的风格。
      【解决方案5】:

      exit(0) 是一个非常特殊的情况,因为它是一个哨兵值,要求编译器告诉操作系统返回该操作系统上真正的成功值。它完全有可能不是数字 0。

      正如您所说,许多函数返回 0 表示成功,但它们主要是“遗留”C 库操作系统接口函数,并遵循 C 最初开发和部署的操作系统的接口样式。

      在 C++ 中,因此在包装此类 C 遗留接口时,0 可能是一个成功值。您可能会考虑使用 0 来表示成功的另一种情况是,当您有效地返回错误代码时,所有错误都是非零值,而 0 作为非错误值是有意义的。因此,不要将返回值视为布尔值(即使 C++ 会将其隐式转换为 1),而是将其视为错误代码,其中 0 表示“无错误”。 (实际上,使用枚举通常是最好的)。

      不过,您通常应该从某种形式的谓词函数(例如 is_empty()、has_dependencies()、can_fit() 等)返回布尔值,并且通常会在错误时抛出异常。或者,根据 libc 的 errno 为错误代码使用子系统(可能还有线程)特定的值,或者接受要加载错误代码的变量的单独引用/指针参数。

      【讨论】:

      • 在 C 中,NULL 完全有可能不会被定义为 ((void*)0),但我还没有看到没有将其定义为 0 的操作系统。IIRC,C++ 定义了一个 null指针为0 而不是NULL
      • @Mike:C 标准(在本例中为 K&R)定义了对于指针,整数值 0 for pointers 很神奇,意味着指针无效。请参阅 C 常见问题解答,question 5.2question 5.4
      • @Mike:C++ 不这样做。它将NULL定义为零(不一定是0,类型不一定是int)0和NULL都不是空指针,但都是空指针constants。跨度>
      【解决方案6】:
      int retCode = SaveWork();
      
      if(retCode == 0) {
        //Success !
      } else if(retCode == ERR_PERMISSIONS) {
        //User doesn't have permissions, inform him
        //and let him chose another place
      } else if(retCode == ERR_NO_SPACE) {
        //No space left to save the work. Figure out something.
      } else {
          //I give up, user is screwd.
      }
      

      因此,如果返回 0/false 表示失败,则您无法区分错误的原因。 对于 C++,您可以使用异常来区分不同的错误。你也可以使用 一个全局变量,类似于errno,您可以在发生故障时对其进行检查。当不需要异常或全局变量时,通常使用返回错误代码。

      【讨论】:

        【解决方案7】:

        正如其他答案已经说过的那样,使用 0 表示成功会使所有非零都表示失败。通常(尽管不总是)这意味着使用单个非零值来指示故障类型。如前所述,在这种情况下,您必须将其视为错误代码而不是成功/失败标志。

        在这种情况下,我绝对讨厌看到像if(!Method()) 这样的声明,这实际上是对成功的测试。对我自己来说,我也发现它可能会让人怀疑这句话是在测试成功还是失败。在我看来,因为它不是一个简单的布尔返回,所以不应该写成这样。

        理想情况下,由于返回值被用作错误代码,因此该函数的作者应该提供一个枚举(或至少一组定义),可以用来代替原始数字。如果是这样,那么我总是更喜欢将测试重写为if(Method() == METHOD_SUCCESS) 之类的东西。如果作者没有提供,但您有关于错误代码值的文档,请考虑制作自己的枚举或定义并使用它。

        如果没有别的,我会把它写成if(Method() == 0),因为这样至少读者应该仍然清楚 Method 不会返回简单的布尔值并且零具有特定的含义。


        虽然经常使用该约定,但它肯定不是唯一的。我已经看到通过使用正值和负值来区分成功和失败的约定。当函数在成功时返回计数但需要在失败时返回不是值计数(通常为 -1)时,尤其会发生这种情况。我还看到了使用无符号数字的变化,其中(unsigned int)-1(又名 0xffffffff)表示错误。可能还有一些我想不出来的。

        由于没有唯一正确的方法,不同时期的不同作者发明了不同的方案。

        当然,这一切都没有提到异常提供(除其他外)在函数出现错误时提供信息的完全不同的方式。

        【讨论】:

          【解决方案8】:

          我在我的代码中使用这个约定:

          int r = Count();
          
          if(r >= 0) {
             // Function successful, r contains a useful non-negative value
          }
          else {
             // A negative r represents an error code
          }
          

          我倾向于避免使用 bool 返回值,因为它们没有任何优势(甚至就其大小而言,四舍五入为一个字节)并且限制了函数返回值的可能扩展。

          在使用异常之前,请考虑它们带来的性能和内存问题。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-07-02
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-04-21
            • 1970-01-01
            相关资源
            最近更新 更多