【问题标题】:C++ exception designC++异常设计
【发布时间】:2016-01-14 17:08:39
【问题描述】:

这个问题与c++ Exception Class Design类似,如下:

我想为我的应用程序设计异常类层次结构,这里是我使用的设计点:

  1. 异常应派生自标准异常类(即std::exceptionstd::logic_errorstd::runtime_error)。

  2. 异常类应该能够获取错误描述(即所谓的what)和错误发生的位置(const std::string &file, int line

  3. Exception 不应在构造期间或从任何其他成员引发任何异常。

鉴于此,我有:

#define throw_line(TException, what) throw TException((what), __FILE__, __LINE__)

class AnException : public std::exception {
public:
    AnException(const std::string &what, const std::string &file, int line) noexcept {
        try {
            what_ = what;
            file_ = file;
            line_ = line;
        } catch (std::exception &e) {
            was_exception_ = true;
        }
    }
    virtual ~AnException() noexcept {}

    virtual const char *what() const noexcept override {
        if (was_exception_) {
            return "Exception occurred while construct this exception. No further information is available."
        } else {
            try {
                std::string message = what_ + " at " + file_ + ":" + std::to_string(line);
                return message.c_str();
            } catch (std::exception &e) {
                return "Exception occurred while construct this exception. No further information is available."
            }
        }
    }
};

class ParticularException : public AnException {
    ...
}

如您所见,构造这样的类似乎有些复杂,因为我们绝对不应该在构造函数(否则将调用 std::terminate())或 what() 成员中出现异常。

问题:这个例子是一个好的设计还是我应该删除一些限制(比如,有文件/行信息)来简化它?有没有更好的办法?

我可以随意使用 C++11/C++14,但尽量不要使用 C++17,因为它尚未完成,编译器可能无法完全实现它。

注意:我希望这段代码是跨平台的。

提前致谢。

编辑 1: 后续问题:我如何停用文件/行信息,但将其保留在日志中?可能打印堆栈跟踪是比我现在拥有的更好的解决方案?我的意思是离开仅包含错误消息 (what) 的异常类,并在异常处理链的上层调用 print_backtrace 之类的东西。

【问题讨论】:

  • FILE 通常扩展为文字。这意味着您应该能够使用... template constructor(const char (&array)[N])... ...这可以防止异常。您可以以类似的方式强制使用文字。
  • 请参阅stackoverflow.com/a/24520735/576911 了解如何使您的异常复制构造函数为 noexcept。

标签: c++ c++11 exception exception-handling c++14


【解决方案1】:

关于我的评论,取决于文字是否可以接受,我有这样的想法:

#include <array>
template <int N> constexpr std::size_t arraySize(const char (&)[N]){return N;}

template <class ExceptT, std::size_t whatN, std::size_t fileN>
  class MyExcept : public ExceptT
{
  static_assert(std::is_base_of<std::exception, ExceptT>::value, "bad not base");

  public:
    MyExcept(
      const char (&what)[whatN],
      const char (&file)[fileN],
      int line) noexcept
    : ExceptT(""), //Using our own what
      what_(), file_(), line_(line)
    {
      std::copy(std::begin(what), std::end(what), begin(what_));
      std::copy(std::begin(file), std::end(file), begin(file_));
    }

    virtual const char *what() const noexcept override
    {
      //....
    }

  private:
    std::array<char,whatN> what_;
    std::array<char,fileN> file_;
    int line_;
};

#define throw_line(TException, what) throw MyExcept<TException,arraySize(what),arraySize(__FILE__)>(what,__FILE__, __LINE__)

void driver()
{
  throw_line(std::runtime_error, "Hoo hah");
}

我添加了一些允许从 std::exception 类型派生的代码(类型需要带有单个文字参数的构造函数(可以检查这也是 noexcept)。我传递给它的是一个空字符串文字,所以 std: :exception 类至少不应该抛出。我正在使用 static_assert 来检查这一点。

  • 它不能投入建设......
  • 它派生自 std::exception...
  • 它包含“固定”的内容和位置

【讨论】:

    【解决方案2】:

    这通常只发生在你想在另一个 catch 块中而不是最外面的 catch 块中捕获某些错误时。如果我看到需求,我只会开始这样做。我记得我的 system-call-read() 包装器会为 EOF 抛出一个不同的异常——因为有时需要捕获它。 然后我有一个系统错误异常和一个正常的消息异常,因为 std::exception 不使用某些编译器存储任何消息。

    单独的异常类被过度使用,就像过度使用抛出规范一样。

    请记住,如果您想在最外层捕获块之外的其他地方捕获某个错误,您需要能够对错误执行一些操作,而不是重新抛出它。通常从 what() 返回的字符串应该已经足以告诉你发生了什么样的错误。因此,出于打印原因,无需重载异常类型。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-07-02
      • 2021-08-13
      • 1970-01-01
      • 1970-01-01
      • 2013-02-02
      • 2023-03-05
      • 2012-09-22
      相关资源
      最近更新 更多