如何在c++中编写自定义异常类时重载throw函数

How is it possible to overload the throw function while writing a custom exception class in C++?

本文关键字:重载 throw 函数 自定义异常 c++      更新时间:2023-10-16

我写了一个例程来存储回溯、行号和文件名等。这样做的目的是存储这些数据,以便在抛出异常时使用。然而,我面临的问题是,我的例程将从catch块调用,并且它最终将存储回溯到catch块。这可不太好。我只需要在抛出异常的地方添加回溯。我不能(显然在try块中调用它,因为在这种情况下,即使在没有抛出异常的情况下,我最终也会存储回溯)。我还可以总是将backtrace存储到try块的末尾,并在catch块中访问它;但是没有办法知道在try块的哪一行会抛出异常。因此,throw函数似乎是添加例程调用的好地方。但我不知道该怎么做。请帮帮我。

如果我的策略似乎是错误的,请随时给我一个更好的解决方案。如果问题本身不清楚,请留下评论。

注:我编写了自定义异常类来继承std::runtime_error。

c++中没有可以覆盖的'throw函数'。抛出是由c++实现来处理的,没有标准的方法来为每个throw插入任何类型的代码。

相反,你可以做的是让你的异常类型在构造时存储当前的回溯。

std::string get_backtrace() {
    return "this is the backtrace...";
}
struct backtrace_exception : public std::exception {
    std::string b;
    backtrace_exception() : b(get_backtrace()) {}
};
int main() {
    try {
        throw backtrace_exception();
    } catch(backtrace_exception &e) {
        std::cout << e.b;
    }
}

不能重载抛出操作符。更常见的解决方案是定义一个宏,用回溯记录打包异常。例如:

#include <string>
#include <iostream>
#include <sstream>
#include <exception>
#include <stdexcept>
#include <type_traits>
template <typename BaseException>
class backtraced_exception : public BaseException {
  private:
    std::string backtrace;
  public:
    template <typename... Args>
    backtraced_exception(const char* aFilename, int aLineNum, Args&&... args) :
      BaseException(std::forward<Args>(args)...) {
      std::stringstream ss;
      ss << "From '" << aFilename << "' at line " << aLineNum << ":n" 
         << BaseException::what();
      backtrace = ss.str();
    };
    backtraced_exception(const std::exception& e, const char* aFilename, int aLineNum) :
      BaseException(static_cast<const BaseException&>(e)) {
      std::stringstream ss;
      ss << "From '" << aFilename << "' at line " << aLineNum << ":n" 
         << e.what();
      backtrace = ss.str();
    };
    virtual ~backtraced_exception() noexcept { };
    virtual const char* what() const noexcept {
      return backtrace.c_str();
    };
};
#define THROW_WITH_BACKTRACE(EXCEPTION, ARG1) throw backtraced_exception< EXCEPTION >(__FILE__, __LINE__, ARG1)
// ... and you can create more macros for more arguments...
#define CATCH_WITH_BACKTRACE(EXCEPTION, EXCEPT_NAME) catch(backtraced_exception< EXCEPTION >& EXCEPT_NAME)
#define RETHROW_WITH_BACKTRACE(EXCEPT_NAME) throw backtraced_exception< std::decay< decltype(EXCEPT_NAME) >::type >(EXCEPT_NAME, __FILE__, __LINE__)

像这样使用:

int main() {
  try {
    try {
      try {
        THROW_WITH_BACKTRACE(std::runtime_error, "This is an example!");
      } CATCH_WITH_BACKTRACE(std::runtime_error, e) {
        std::cout << "First caught this exception:n" << e.what() << std::endl;
        RETHROW_WITH_BACKTRACE(e);
      };
    } catch(std::runtime_error& e) {  // can also catch normally.
      std::cout << "Got this exception:n"
                << e.what() << std::endl;
      // and even rethrow again, with backtrace:
      RETHROW_WITH_BACKTRACE(e);
    };
  } catch(std::runtime_error& e) {
    std::cout << "Finally, got this exception:n"
              << e.what() << std::endl;
  };
};

输出如下:

First caught this exception:
From 'logged_except.cpp' at line 50:
This is an example!
Got this exception:
From 'logged_except.cpp' at line 53:
From 'logged_except.cpp' at line 50:
This is an example!
Finally, got this exception:
From 'logged_except.cpp' at line 59:
From 'logged_except.cpp' at line 53:
From 'logged_except.cpp' at line 50:
This is an example!

这个解决方案的另一个好处是,你可以通过简单地有条件地定义宏来禁用回溯,这取决于你是否想要回溯(例如调试或发布构建)。

上面的例子需要c++ 11的特性,但是你可能可以想出一个不需要这些特性(即可变模板、decltype、类型特征等)的等效解决方案。

您还可以使用c++ 11异常指针使该方案更加方便。

是的,你可以通过重写这个函数来重写GCC下的'throw':

外来的"C"Void __cxa_throw (Void *thrown_exception, Void *pvtinfo, Void (*dest)(Void *))

在完成你的工作之后,你应该调用真正的'throw',因为你应该在Unix下使用'dlsym()'来获得它的地址,或者通过将- wl,-wrap,symbol传递给链接器来使用'mingw'下的函数包装,就像在这里GNU gcc/ld中一样-在同一个对象文件中定义调用者和被调用者来包装对symbol的调用,我使用这两种方法