我正在编写一个希望具有可移植性的库。因此,它不应依赖于glibc或Microsoft扩展或其他任何不在标准中的内容。我有一个很好的从std :: exception派生出来的类层次结构,用于处理逻辑和输入错误。知道在特定文件和行号抛出了特定类型的异常是有用的,但知道如何执行到那里可能会更有价值,因此我一直在寻找获取堆栈跟踪的方法。
我知道当使用execinfo.h中的函数(请参见问题76822)构建针对glibc时,可以使用此数据,并通过Microsoft的C ++实现中的StackWalk接口(请参见问题126450)。不过,我非常想避免使用任何不具备可移植性的东西。
我正在考虑以以下形式自己实现此功能:
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块是否需要在每次函数调用时进行额外的设置和撤消,还是在编译时以某种方式处理?或者还有其他问题我没有考虑吗?