【问题标题】:C++ Handling error/exception that comes in multiple formsC++ 处理多种形式的错误/异常
【发布时间】:2021-04-13 14:56:10
【问题描述】:

有没有更好的方法来做到这一点?我觉得这将是可怕的维护。

const unsigned int set_max {32};
std::vector<unsigned int> base;
base.reserve(argc-1);
for (int i = 1; i < argc; ++i) {
  unsigned long tmp;
  try { tmp = std::stoul(argv[i], nullptr, 10); }
  catch (std::invalid_argument) { std::cerr << "Invalid base! Conversion failed.\n"; return -2; }
  catch (std::out_of_range) { std::cerr << "Invalid base! Too large.\n"; return -2; }
  if (tmp > std::numeric_limits<unsigned int>::max()) { std::cerr << "Invalid base! Too large.\n"; return -2; }
  if (tmp < 2) { std::cerr << "Invalid base! Must at least be 2.\n"; return -2; }
  if (tmp > set_max) { std::cerr << "Invalid base! Must be within " << set_max << ".\n"; return -2; }
  base.push_back(static_cast<unsigned int>(tmp));
}

编辑:转发here

【问题讨论】:

  • 为什么不尝试将其发布到codereview.stackexchange.com
  • 如果你改用std::strtoul(),你至少可以摆脱异常处理。无论您使用std::strtoul() 还是std::stoul(),我都会将整个逻辑移到一个单独的函数中,该函数接受一个字符串作为输入并返回一个整数和一个布尔值作为输出,并且只有当布尔值报告为真时才将整数推入向量中.这将使代码更易于维护。
  • 为什么是return -2;?为什么不抛出异常?然后,您可以简单地让调用者处理所有异常,包括从stoul 抛出的异常。
  • @JoaoAlmeida-Domingues 谢谢,我不知道这个论坛存在。
  • @WernerHenze 因为这是来自主函数。它直接与用户交互。

标签: c++ c++11 error-handling


【解决方案1】:

我建议将逻辑拆分为实用函数,以使每个操作更易于管理,例如:

enum class eConvFailure {NoError, ConvFailed, OutOfRange, TooSmall, TooLarge};

std::pair<unsigned int, eConvFailure> my_stoui(const std::string &s)
{
  unsigned long tmp;

  try
  {
    tmp = std::stoul(s, nullptr, 10);
    if (tmp > std::numeric_limits<unsigned int>::max()) {
      throw std::out_of_range("");
    }
  }
  catch (const std::invalid_argument &) {
    return std::make_pair(0, eConvFailure::ConvFailed);
  }
  catch (const std::out_of_range &) {
    return std::make_pair(0, eConvFailure::OutOfRange);
  }

  /* alternatively:
  errno = 0;
  tmp = std::stroul(s.c_str(), nullptr, 10);
  if (errno != 0) {
    if (tmp == ULONG_MAX && errno == ERANGE)
      return std::make_pair(0, eConvFailure::OutOfRange);
    else
      return std::make_pair(0, eConvFailure::ConvFailed);
  }
  if (tmp > std::numeric_limits<unsigned int>::max()) {
    return std::make_pair(0, eConvFailure::TooLarge);
  }
  */

  return std::make_pair(static_cast<unsigned int>(tmp), eConvFailure::NoError);
}

std::pair<unsigned int, eConvFailure> my_stoui(const std::string &s, unsigned int max_value)
{  
  auto res = my_stoui(s);
  if (res.second != eConvFailure::NoError) {
    return res;
  }
  if (res.first < 2) {
    return std::make_pair(0, eConvFailure::TooSmall);
  }
  if (res.first > max_value) {
    return std::make_pair(0, eConvFailure::TooLarge);
  }
  return res;
}
const unsigned int set_max {32};

std::string reason(eConvFailure err) {
  switch (err) {
    case eConvFailure::ConvFailure:  return "Conversion Failed";
    case eConvFailure::OutOfRange:   return "Too Large";
    case eConvFailure::TooSmall:     return "Must at least be 2";
    case eConvFailure::TooLarge:     return "Must be within " + std::to_string(set_max);
    default:                         return "Error " + std::to_string(static_cast<int>(eConvFailure));
  }
}

std::vector<unsigned int> base;
base.reserve(argc-1);
for (int i = 1; i < argc; ++i) {
  auto res = my_stoui(argv[i], set_max);
  if (res.second != eConvFailure::NoError) {
    std::cerr << "Invalid base! " << reason(res.second) << ".\n";
    return -2;
  }
  base.push_back(res.first);
}

或者,您可以使用异常处理来代替,例如:

unsigned int my_stoui(const std::string &s)
{
  unsigned long tmp = std::stoul(s, nullptr, 10);
  if (tmp > std::numeric_limits<unsigned int>::max()) {
    throw std::out_of_range("");
  }
  return static_cast<unsigned int>(tmp);
}

unsigned int my_stoui(const std::string &s, unsigned int max_value)
{  
  unsigned int tmp = my_stoui(s);
  if (tmp < 2) {
    throw std::runtime_error("Must at least be 2");
  }
  if (tmp > max_value) {
    return std::runtime_error("Must be within " + std::to_string(max_value));
  }
  return tmp;
}
const unsigned int set_max {32};

std::vector<unsigned int> base;
base.reserve(argc-1);

try
{
  for (int i = 1; i < argc; ++i) {
    base.push_back(my_stoui(argv[i], set_max));
  }
}
catch (const std::exception &ex)
{
  std::cerr << "Invalid base! ";
  if (dynamic_cast<const std::invalid_argument*>(&ex)) {
    std::cerr << "Conversion failed";
  }
  else if (dynamic_cast<const std::out_of_range*>(&ex)) {
    std::cerr << "Too large";
  }
  else {
    std::cerr << ex.what();
  }
  std::cerr << ".\n";
  return -2;
}

【讨论】:

  • 谢谢!毕竟,将逻辑排除在循环之外似乎是更好的方法。
  • 我喜欢后者,使用异常处理,因为它更简单。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-01-08
  • 2017-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-12
  • 2018-08-09
相关资源
最近更新 更多