C++例外设计

C++ exception design

本文关键字:外设 C++      更新时间:2023-10-16

这个问题类似于 c++ 异常类设计,如下所示:

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

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

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

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

鉴于此,我有:

#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的东西。

关于我的评论,取决于文字是否可以接受,我想到了这样的事情:

#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...
  • 它包含"固定"的内容和位置

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

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

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