【问题标题】:Compound error_codes?复合错误代码?
【发布时间】:2020-04-24 19:24:33
【问题描述】:

假设我有一个函数(为简单起见,请忽略它不是异步的)

http_response http_get(string url, std::error_code& ec);

当这个函数失败时,它通过ec返回一个错误。到目前为止一切顺利。

现在,假设我有另一个函数多次使用http_get

int calculate_meaning_of_life(std::error_code& ec) {
    auto r1 = http_get("foo.com", ec);
    if (ec) return 0;
    auto r2 = http_get("bar.com", ec);
    if (ec) return 0;
    return combine(r1, r2);
}

但是现在我有一个问题,当calculate_meaning_of_life 函数失败时,我丢失了关于它是对http_get 的哪个调用的信息。

另一种方法可能是我定义自定义错误代码,并像这样使用它们:

int calculate_meaning_of_life(std::error_code& ec) {
    std::error_code internal_ec;
    auto r1 = http_get("foo.com", internal_ec);
    if (internal_ec) {
        ec = custom_error::failed_at_foo_com;
        return 0;
    }
    auto r2 = http_get("bar.com", internal_ec);
    if (internal_ec) {
        ec = custom_error::failed_at_bar_com;
        return 0;
    }
    return combine(r1, r2);
}

但现在我丢失了有关 http_get 内部发生的事情的信息。理想情况下,我想保留所有信息,如下所示:

int calculate_meaning_of_life(std::error_code& ec) {
    std::error_code internal_ec;
    auto r1 = http_get("foo.com", internal_ec);
    if (internal_ec) {
        ec = meaning_error(custom_error::failed_at_foo_com, internal_ec);
        return 0;
    }
    auto r2 = http_get("bar.com", internal_ec);
    if (internal_ec) {
        ec = meaning_error(custom_error::failed_at_bar_com, internal_ec);
        return 0;
    }
    return combine(r1, r2);
}

因此,当我运行它但它失败时,我可以获得所有可用信息。例如:

std::cerr << ec << "\n"; // Would print: "Failed to retrieve foo.com: operation aborted"

据我了解,std::error_code 只是一个int 和一个指向类别结构的指针,所以我认为没有一种非骇客的方法可以使std::error_code 中的int 实际上代表一个结构。

我现在能想到的制作这种复合错误代码的唯一“hackish”方法是滥用int 来实际表示多个值。例如。通过为自定义错误代码“保留”前 K 位并为“内部”错误代码保留,或者使用 Cantor's pairing function 或类似的东西。

所以最后的问题是:有没有推荐的方法来构造这样的复合错误代码?和/或在 WWW 的某个地方是否有关于此类复合错误代码的讨论?或者也许有其他选择?

【问题讨论】:

  • 如果错误是“异常的”,那么是否会抛出异常?
  • 谢谢,尽管它们通常不是“异常”,这是我目前感兴趣的情况。在我们的代码库中,我们不使用异常也是这种情况,因为与Boost.Asio 和协程他们搞砸了地址清理器(至少在 g++ 上)。
  • @PeterJankuliak 通常错误代码不被认为是很好的解决方案 AFAIK。处理异常情况有两种很好但不同的策略 - 异常和“结果类型”,它是返回类型和错误消息/错误对象的变体。两者都支持您的需求。如果这些情况不是例外,我建议只检查和ifs,这意味着这是预期的行为
  • @PeterJankuliak 如果我理解正确,您想在编写错误时尽可能多地保留有关发生错误的上下文和错误本身的信息吗?
  • @GauravDhiman 是的,没错

标签: c++ error-code


【解决方案1】:

std::error_code 不被认为是一个很好的解决方案,我认为这是一个糟糕的界面。你真的应该使用一些对消息进行编码的东西,例如。改为:

int calculate_meaning_of_life(std::system_error& ec) {

std::system_error 编码一个std::error_code 加上一条消息。 thrower(在这种情况下由参数返回)负责编码足够的信息以使异常有用。

【讨论】:

    【解决方案2】:

    如果您对动态分配本身没有问题,那么以下应该是一种做您想做的事情的方法(即使我对此不太满意)

    struct my_error {
      std::error_code ec;
      std::string description; // Or whatever is appropriate
    }
    
    class my_traceback {
      std::vector<my_error> errors;
      bool unhandled_error = false; // you can let this be, but I think it really adds a bit of safety
    public:
      void add_error(my_error&& e) {
        assert(!unhandled_error);
        errors.push_back(std::move(e));
        unhandled_error = true;
      }
    
      explicit operator bool() const {
        return unhandled_error;
      }
    
      bool handle_error() {
        return std::exchange(unhandled_error, false);
      }
    
      my_error& last_error() { // So we can add information to the last error
        return errors.back();
      }
    
      // Add possibilities to output errors etc.
    };
    

    您的代码中的用法如下:

    http_response http_get(string url, my_traceback& tr); 
    
    int calculate_meaning_of_life(my_traceback& tr) {
        auto r1 = http_get("foo.com", tr);
        if (tr.handle_error()) {
          std::error_code ec = tr.last_error().ec; // Probably you want to pass that up the chain? If that is always done, than you can set the ec in my_traceback and remove it from my_error
          tr.add_error({ec, "Failed in Foo"});
          return 0;
        }
        auto r2 = http_get("bar.com", ec);
        if (tr.handle_error()) {
          std::error_code ec = tr.last_error().ec; 
          tr.add_error({ec, "Failed in Bar"});
          return 0;
        }
        return combine(r1, r2);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-24
      • 1970-01-01
      • 1970-01-01
      • 2016-08-06
      • 2015-03-28
      • 2011-03-16
      相关资源
      最近更新 更多