我有下面的例子。(我的实际项目是一个多线程项目,我为所有项目设置了终止处理程序。我这里有几个问题。
-
我的终止处理程序没有做任何花哨的事情。它只是说发生了错误并退出。我读到添加处理程序是很好的做法。为什么会这样,在这种情况下我真的需要吗?
-
如果我没有处理程序,我会得到引发的异常类型。
terminate called after throwing an instance of 'char const*'
但是当我使用处理程序时,我无法获得它。即使我使用current_exception,我也无法获得异常类型。(这里显然是字符*,但就我而言,它可能是任何东西,所以我无法正确捕捉。即使我使用 catch{...},消息和类型也会丢失(。无论如何都可以得到消息。如果没有消息,至少我可以得到抛出的异常类型吗?
// set_terminate example
#include <iostream>
#include <exception>
#include <cstdlib>
using namespace std;
void myterminate () {
cerr << "terminate handler calledn";
abort(); // forces abnormal termination
}
int main (void) {
//set_terminate (myterminate);
throw "TEST"; // unhandled exception: calls terminate handler
return 0;
如果您可以忍受<cxxabi.h>
1所暗示的可移植性限制, 那么您可能可以使用下面的backstop()
终止处理程序:
主.cpp
#include <iostream>
#include <exception>
#include <stdexcept>
#include <cstdlib>
#include <cxxabi.h>
void backstop()
{
auto const ep = std::current_exception();
if (ep) {
try {
int status;
auto const etype = abi::__cxa_demangle(abi::__cxa_current_exception_type()->name(), 0, 0, &status);
std::cerr << "Terminating with uncaught exception of type `" << etype << "`";
std::rethrow_exception(ep);
} catch(const std::exception& e) {
std::cerr << " with `what()` = "" << e.what() << """;
} catch(...) {}
std::cerr << std::endl;
}
std::abort();
}
int main(int argc, char *argv[])
{
std::set_terminate(backstop);
if (argc > 1) {
throw argv[1];
} else {
throw std::runtime_error("I am too tired to carry on");
}
return 0;
}
这将始终报告未捕获异常的类型,以及该类型是否派生自std::exception
,它还将报告该异常的what()
。例如
$ g++ --version
g++ (Ubuntu 9.3.0-10ubuntu2) 9.3.0
...
$ g++ -Wall -Wextra -pedantic main.cpp; ./a.out Whoops!
Terminating with uncaught exception of type `char*`
Aborted (core dumped)
$ clang++ --version
clang version 10.0.0-4ubuntu1
...
$ clang++ -Wall -Wextra -pedantic main.cpp; ./a.out
Terminating with uncaught exception of type `std::runtime_error` with `what()` = "I am too tired to carry on"
Aborted (core dumped)
请注意,您可能会避免调用set_terminate(backstop)
- 可以想象这可能是 在一个大型复杂程序中的其他地方反制 - 并确保任何 转义main
主体的异常被捕获在函数 try-block 中,即 将main
替换为:
int main(int argc, char *argv[]) try
{
if (argc > 1) {
throw argv[1];
} else {
throw std::runtime_error("I am too tired to carry on");
}
return 0;
}
catch(...) {
backstop();
}
该程序的行为将像以前一样。
[1] 你至少会有 g++、clang++、icc;你不会有 MS C++
正如你所设想的,如果你想让你的终止做一些花哨的事情,你可以写一个终止处理程序函数并将其作为std::set_terminate
的参数调用。我认为在此函数中添加回溯以提供更多信息可能很有用。虽然函数的名称被编译器篡改了,但你仍然可以看到这个错误发生的位置。
如果你想显示异常,你可以在终止处理程序函数中添加一个try-catch块。
顺便说一句,我认为可能有必要使用signal
函数来处理分段默认错误,通过这样做,您的程序在异常句柄中更加完整。
在 utils.h 文件中:
#include <stdexcept>
#include <execinfo.h>
#include <exception>
#include <stdexcept>
#include <cstdlib>
#include <csignal>
// This function is used for handle segmental fault
inline void segfaultHandler(int signal __attribute__((unused)))
{
void *stackArray[20];
size_t size = backtrace(stackArray, 10);
std::cerr << "Segmentation fault! backtrace: ";
char** backtrace = backtrace_symbols(stackArray, size);
for (size_t i = 0; i < size; i++)
{
std::cerr << "t" << backtrace[i]
}
abort();
}
// This is terminate handle function
inline void exceptionHandler()
{
static bool triedThrow = false;
try
{
if(!triedThrow)
{
triedThrow = true;
throw;
}
}
catch( const std::exception &e)
{
std::cerr << "Caught unhandled exception: " << e.what();
}
catch(...){}
void *stackArray[20];
size_t size = backtrace(stackArray, 10);
std::cerr << "Segmentation fault! backtrace: ";
char** backtrace = backtrace_symbols(stackArray, size);
for (size_t i = 0; i < size; i++)
{
std::cerr << "t" << backtrace[i]
}
abort();
}
主要而言,以使用 Mike Kinghan 的代码为例
#include <iostream>
#include "utils.h"
int main(int argc, char *argv[])
{
signal(SIGSEGV, segfaultHandler);
std::set_terminate(exceptionHandler);
if (argc > 1) {
throw argv[1];
} else {
throw std::runtime_error("I am too tired to carry on");
}
return 0;
}