我正在编写一个我希望可移植的库。因此,它不应该依赖于 glibc 或 Microsoft 扩展或任何其他不在标准中的东西。我有一个很好的从 std::exception 派生的类层次结构,我用它来处理逻辑和输入中的错误。知道在特定文件和行号处引发了特定类型的异常是有用的,但是知道执行是如何到达那里的可能更有价值,所以我一直在寻找获取堆栈跟踪的方法。
我知道在使用 execinfo.h 中的函数(参见问题 76822)和通过 Microsoft C++ 实现中的 StackWalk 接口(参见问题126450)针对 glibc 构建时,这些数据是可用的,但我非常想避免任何不便携。
我正在考虑以这种形式自己实现此功能:
class myException : public std::exception
{
public:
...
void AddCall( std::string s )
{ m_vCallStack.push_back( s ); }
std::string ToStr() const
{
std::string l_sRet = "";
...
l_sRet += "Call stack:\n";
for( int i = 0; i < m_vCallStack.size(); i++ )
l_sRet += " " + m_vCallStack[i] + "\n";
...
return l_sRet;
}
private:
...
std::vector< std::string > m_vCallStack;
};
ret_type some_function( param_1, param_2, param_3 )
{
try
{
...
}
catch( myException e )
{
e.AddCall( "some_function( " + param_1 + ", " + param_2 + ", " + param_3 + " )" );
throw e;
}
}
int main( int argc, char * argv[] )
{
try
{
...
}
catch ( myException e )
{
std::cerr << "Caught exception: \n" << e.ToStr();
return 1;
}
return 0;
}
这是一个可怕的想法吗?这意味着为每个函数添加 try/catch 块需要做很多工作,但我可以接受。当异常的原因是内存损坏或内存不足时,它不会起作用,但在那一点上你几乎已经搞砸了。如果堆栈中的某些函数没有捕获异常、将自身添加到列表中并重新抛出,它可能会提供误导性信息,但我至少可以保证我的所有库函数都这样做。与“真正的”堆栈跟踪不同,我不会在调用函数时获得行号,但至少我会有一些东西。
我主要担心的是,即使实际上没有抛出异常,这也会导致速度变慢。所有这些 try/catch 块是否需要对每个函数调用进行额外的设置和拆卸,或者在编译时以某种方式处理?还是有其他我没有考虑过的问题?