Составные коды ошибок? - PullRequest
3 голосов
/ 07 января 2020

Скажем, у меня есть функция (пожалуйста, игнорируйте, что это не asyn c для простоты)

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 и указатель на структуру категории, поэтому я думаю, что нет нехакерского sh способа сделать int в std::error_code на самом деле представляет собой структуру.

Единственный способ "hacki sh", который я теперь могу придумать, чтобы создать такой составной код ошибки, состоит в том, чтобы злоупотребить тем, что int фактически представляет больше чем одно значение. Например, «резервируя» первые K битов для пользовательского кода ошибки и оставляя для «внутреннего», или используя функцию связывания Кантора или что-то подобное.

Итак, наконец, вопрос : Есть ли рекомендуемый способ создания таких составных кодов ошибок? И / или где-нибудь обсуждается WWW about такие составные коды ошибок? Или, может быть, есть альтернатива?

Ответы [ 2 ]

0 голосов
/ 07 января 2020

Если у вас нет проблем с динамическим c распределением как таковым, следующее должно быть способом сделать то, что вы хотите (даже если я не слишком доволен этим)

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);
}
0 голосов
/ 07 января 2020

std::error_code не считаются отличным решением, и я считаю, что плохой интерфейс. Вы действительно должны использовать то, что кодирует сообщение, например. изменить на:

int calculate_meaning_of_life(std::system_error& ec) {

std::system_error кодирует std::error_code плюс сообщение. Ответственный за thrower (в данном случае возвращаемый параметром) отвечает за кодирование достаточного количества информации, чтобы сделать исключение полезным.

...