1

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

如果我的策略似乎本质上是错误的,请随时为我指出一个更好的解决方案。如果问题本身不清楚,请发表评论。

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

4

3 回答 3

4

C++ 没有定义可以覆盖的“抛出函数”。抛出由 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;
    }
}
于 2013-06-05T18:03:16.750 回答
1

您不能重载 throw 运算符。更常见的解决方案是定义一个将异常与回溯记录打包的宏。例如这个:

#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 异常指针使这个方案更加方便。

于 2013-06-05T19:12:21.150 回答
1

是的,您可以通过覆盖此函数来覆盖 GCC 下的“抛出”:

extern "C" void __cxa_throw (void *thrown_exception, void *pvtinfo, void (*dest)(void *))

在完成你的工作之后,你应该调用真正的“throw”,因为你应该在 Unix 下使用“dlsym()”来获取它的地址,或者通过将 -WL,-wrap,symbole 传递给链接器来使用 'mingw' 下的函数包装,比如在这里GNU gcc/ld - 使用在同一个目标文件中定义的调用者和被调用者包装对符号的调用 ,我使用了这两种方法

于 2017-10-30T08:49:58.273 回答