【问题标题】:std::errc, how to indicate success in retvalstd::errc,如何在 retval 中指示成功
【发布时间】:2016-04-24 15:37:48
【问题描述】:

我想写一个这样的c++函数:

#include <system_error>
std::errc f() { return std::errc::success; }

但我不明白如何使用 std::errc 'enum class' 类型返回成功的值(在本例中为 0)。我看到的一种方法是返回 int:

template <typename E>
constexpr typename std::underlying_type<E>::type to_underlying(E e) {
    return static_cast<typename std::underlying_type<E>::type>(e);
}

int f() { is_succ() ? 0 : to_underlying(err); }

但对我来说它看起来很丑。从 std c++ 0x14 中的函数返回面向 C 的成功/错误代码的标准方法在哪里?

附言。我正在使用 MS VC 2015 补丁 2。

【问题讨论】:

  • 为什么不直接使用intEXIT_SUCCESSEXIT_FAILURE
  • @CoffeeandCode: 1972 调用..
  • 好的。我的主要目标是编写一个 c-api 包装器 c++ 库。我已经声明了一组函数 extern "C" 等等。现在我必须从他们那里返回一个 int 来指示错误状态(或成功)。因此,我必须将异常或内部错误代码转换为对 C++ 代码中的 C 用户有价值的东西。我认为 std::errc 是一个不错的选择。看来这不是个好主意。现在我在原生 int 和 boost::system::errc 类型之间进行选择。 PS。对不起我的英语不好:)
  • 您只需要返回默认构造的 std::error_code 实例。这有什么问题吗?
  • 现在枚举 std::from_chars 中没有成功值的情况看起来是有史以来最糟糕的设计。成功由非枚举值表示,这通常意味着出现了令人难以置信的错误。

标签: c++ c++14


【解决方案1】:

你可以这样做:

#include <system_error>

std::errc f() { return std::errc(); }

int main()
{
    std::errc x = f();
    if (x == std::errc())
    {
        // success
    }
}

std::errc()(0) 是一个有效的枚举值,即使它没有出现在从 1 开始的枚举数列表中。它代表成功。

为了比较,看看std::to_chars,它返回一个

struct to_chars_result {
    char* ptr;
    std::errc ec;
}

【讨论】:

    【解决方案2】:

    您通常不会直接从函数返回 std::errc 值。相反,您返回 std::error_code。例如,你的函数会这样声明:

    std::error_code f();
    

    然后将该函数的结果与 std::errc 值进行比较。例如:

    std::error_code error = f();
    if (error == std::errc::no_such_file_or_directory)
    {
      // deal with the error
    }
    

    std::error_code是上下文可转换的布尔值。测试函数是否成功的方法是在布尔表达式中使用错误代码。例如:

    std::error_code error = f();
    if (error)
    {
      // the function failed...
    }
    

    要从这样的函数返回成功,您返回一个默认初始化的std::error_code。例如:

    std::error_code f()
    {
      // do stuff...
      return std::error_code{}; // success!
    }
    

    当您使用 C 风格的 API 时,这种方法很有效。您的包装器将使用 API 返回的整数值和定义如何将这些错误转换为 std::errc 值的自定义 std::error_category 构造 std::error_code 对象。

    但是,相反的情况不起作用。如果您正在为 C++ 库编写 C 包装器,那么您必须以 C 方式做事。使用您的错误值定义 enum 并从您的 C 入口点返回这些值。

    【讨论】:

    • 理论上是好的设计。但实际上std::error_code 是 128 位变量(int + padding + 指针),在某些情况下,从函数中返回它可能太昂贵了。如果错误不是那么频繁,那么返回错误代码的代价将会太高,恕我直言。
    【解决方案3】:

    来自the documentation

    作用域枚举 std::errc 定义与 POSIX 错误代码相对应的可移植错误条件的值。

    std::errc 常量用于在您检查是否发生某些特定错误情况时进行比较(如该页面上的示例所示)。

    成功不是错误条件。

    返回一个std::error_code,它可能包含这些错误条件之一,或者默认情况下没有错误条件(即成功)。

    【讨论】:

    • std::errc f() { return std::errc::success;我应该在函数体中写什么来返回成功状态?那是一个问题。
    • 是的,我必须这样做。我必须阅读解释而不是答案。没什么私人的,我已经接受了 c++ 不应该返回成功值的事实 :) 就我而言,它看起来不可预测。至少。
    • std::from_chars 在成功时返回一个值初始化的errc,因此它不仅用于错误。
    【解决方案4】:

    人们可能会想出一个快速(而肮脏)的答案来回答您的问题:

    // default category is std::system_error
    inline std::error_code system_error_code_default{};
    

    您可能会创建系统错误的全局默认实例,其内部值确实为 0(零),这是“成功的标志”。然后你用它来“意味着一个好的 retval”

    std::error_code very_complex_operation ()  
    {
      return {};
       // same as
      // return error_code_success ;
    }
    

    然后你可以在返回时检查是否成功

        auto errcde = very_complex_operation();
        auto default_category_name = errcde.category().name();
    
        if (system_error_code_default == errcde) {
          // the sucess 
        }  else {
          // error condition
        }
    

    这确实会起作用,您不必“做任何其他事情”。 但。这一切在逻辑上发生在“系统”错误类别又名域中。

        // "system"
        auto default_category_name = errcde.category().name();
    

    因此,在这里,您处于“系统”错误的范围内。如果这是你想要的。但我对此表示怀疑。在“玩”完上述内容后,您将需要立即定义自己的 std::error_code 系统,以及您自己的枚举、类别、消息和其他内容。仅与您的 API 相关的信号错误。

    要理解为什么在存在多个错误类别的情况下只考虑 std::error_code 使用模式。了解 std 命名空间就是这样做的。那里至少有三个类别。

    您的用户/客户可能正在编写一个 C++ 应用程序,其中可能有十几个或更多不同的库。如果您使用自己的标准 std::error_code 系统开发 API,使用起来会感觉“正常”。用户将能够并且可能愿意将您的 API 集成到他们的程序中。

    我希望这能以更全面的方式回答这个问题。 如果您希望按照自己的错误代码(以及更多)继续,请proceed here

    诚然,基于标准 C++ 错误代码的处理似乎有点过度设计。我可能认为理解它的关键是,如果愿意的话,要站在代码或库的用户的角度。

    【讨论】:

      猜你喜欢
      • 2021-10-27
      • 2019-01-03
      • 2010-09-14
      • 1970-01-01
      • 2020-01-21
      • 2020-11-16
      • 2019-07-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多