这是我的异常类:
class Win32Failure : public std::exception
{
public:
Win32Failure( char const* win32_function_name, LONG error_code );
char const* win32_function_name() const { return win32_function_name_; }
LONG error_code() const { return error_code_; }
virtual char const* what() const;
private:
std::string GetFormattedMessage() const;
char const* win32_function_name_;
LONG error_code_;
std::string error_text_;
};
Win32Failure::Win32Failure( char const* win32_function_name, LONG error_code )
: error_code_(error_code)
, win32_function_name_(win32_function_name)
{
std::stringstream error_msg;
error_msg << win32_function_name << " failed with code: "
<< error_code << " (" << GetFormattedMessage() << ")"
;
error_text_ = error_msg.str();
}
std::string Win32Failure::GetFormattedMessage() const
{
TCHAR message_buffer[1000];
FormatMessage(
//FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
error_code_,
0, // Default language
reinterpret_cast<LPTSTR>(&message_buffer),
sizeof(message_buffer) / sizeof(TCHAR),
NULL
);
return std::string(message_buffer);
}
char const* Win32Failure::what() const
{
return error_text_.c_str();
}
boost exception guidelines建议不要将任何分配内存的对象放置为我的异常类的成员。在这种情况下,std::string
的使用违反了这一点。我尊重这个规则,但是我想不出一种方法来实现what()
覆盖而不使用std :: string来管理内存(而不是要求调用者为我管理它)。
我可以使用固定大小的缓冲区作为成员并使用C库函数(如snprintf()
)来完成这项工作,但这不是C ++的惯用语,因此不是理想的解决方案。
这是一个合适的异常类实现吗?如果没有,可以做出哪些改进?
答案 0 :(得分:6)
对于它的价值,<stdexcept>
中定义的所有异常类型都将std::string
作为参数。图书馆设计师可以解释这是“好的”#34;我认为反对这一点的主要论点是,如果您处于内存受限的环境中,您可能无法分配内存来抛出异常。
答案 1 :(得分:2)
例外情况应防止在施工期间可能造成的资源枯竭。在异常中使用动态数组并不好(除非,它是通过指针完成的,如果分配失败,则会有一个回退计划)。使用std::string
向用户传达一些信息是双重的:首先,它是动态数组,其次是大多数没用 - 抛出异常的函数不知道情况,试图合理地解释它失败的原因。
异常应该做有用的事情,比如堆栈跟踪+函数参数恢复(如果可能)+ ID到外部消息(例如动态库资源),以防最终用户需要格式化的低级消息。如果异常对最终用户产生,它应该以来自外部字符串表模板的相干格式化错误消息的形式(可能添加一些运行时参数)。如果异常用于帮助调试并将其发送给开发人员,那么堆栈跟踪,函数参数,机器状态是有用的,一些硬编码的通用字符串不是。
编辑:看起来你正试图围绕Windows API调用制作C ++异常包装器,我是否正确?如果是这样,你应该考虑几件事:dbghelp.dll
的{{3}},StackWalk64()和类似的函数来生成人类可读的堆栈跟踪(只有地址在异常构造函数中用于调试异地); 答案 2 :(得分:0)
作为类私有数据的一部分有一个静态字符缓冲区(如果是多线程则考虑使用TLS)
what()只返回一个指向此缓冲区的const指针。
在GetFormattedMessage()中填写缓冲区(就像你现在一样) (如果需要,可能会从unicode / wchars转换,因为你正在处理TCHAR)作为静态类成员,在发生任何异常之前,缓冲区在堆栈上预先分配。
您不需要管理在堆栈上分配的内存。 但是你会嚼掉1000个字节的堆栈空间(通常不是什么大不了的事)
但是只有一个缓冲区来保存错误文本,但在任何给定时间只应存在一个自定义异常(我认为?)