如何用variables消息抛出std :: exceptions?
这是我经常做的一个例子,当我想添加一些信息到exception:
std::stringstream errMsg; errMsg << "Could not load config file '" << configfile << "'"; throw std::exception(errMsg.str().c_str());
有没有更好的方法来做到这一点?
这是我的解决scheme:
class Formatter { public: Formatter() {} ~Formatter() {} template <typename Type> Formatter & operator << (const Type & value) { stream_ << value; return *this; } std::string str() const { return stream_.str(); } operator std::string () const { return stream_.str(); } enum ConvertToString { to_str }; std::string operator >> (ConvertToString) { return stream_.str(); } private: std::stringstream stream_; Formatter(const Formatter &); Formatter & operator = (Formatter &); };
例:
throw std::runtime_error(Formatter() << foo << 13 << ", bar" << myData); // implicitly cast to std::string throw std::runtime_error(Formatter() << foo << 13 << ", bar" << myData >> Formatter::to_str); // explicitly cast to std::string
标准exception可以从std::string
构造:
#include <stdexcept> char const * configfile = "hardcode.cfg"; std::string const anotherfile = get_file(); throw std::runtime_error(std::string("Failed: ") + configfile); throw std::runtime_error("Error: " + anotherfile);
请注意,基类std::exception
不能这样构造; 你必须使用具体派生类之一。
有不同的例外,例如runtime_error
, range_error
, overflow_error
, logic_error
等等。你需要将string传递给它的构造函数,你可以连接任何你想要的消息。 这只是一个string操作。
std::string errorMessage = std::string("Error: on file ")+fileName; throw std::runtime_error(errorMessage);
你也可以像这样使用boost::format
:
throw std::runtime_error(boost::format("Error processing file %1") % fileName);
以下class级我来得挺方便:
struct Error : std::exception { char text[1000]; Error(char const* fmt, ...) __attribute__((format(printf,2,3))) { va_list ap; va_start(ap, fmt); vsnprintf(text, sizeof text, fmt, ap); va_end(ap); } char const* what() const throw() { return text; } };
用法示例:
throw Error("Could not load config file '%s'", configfile.c_str());
使用string文字运算符,如果C ++ 14( operator ""s
)
using namespace std::string_literals; throw std::exception("Could not load config file '"s + configfile + "'"s);
或者在C ++ 11中定义你自己的。 例如
std::string operator ""_s(const char * str, std::size_t len) { return std::string(str, str + len); }
你的throw语句将会像这样
throw std::exception("Could not load config file '"_s + configfile + "'"_s);
这看起来不错,干净。
一个更好的方法是创build一个类(或类)的例外。
就像是:
class ConfigurationError : public std::exception { public: ConfigurationError(); }; class ConfigurationLoadError : public ConfigurationError { public: ConfigurationLoadError(std::string & filename); };
原因是exception不仅仅是传输string。 为错误提供不同的类别,您可以让开发人员以相应的方式处理特定错误(而不仅仅是显示错误消息)。 如果您使用层次结构,捕捉exception的人员可以根据自己的需要进行特定处理。
a)可能需要知道具体原因
} catch (const ConfigurationLoadError & ex) { // ... } catch (const ConfigurationError & ex) {
a)另一个不想知道细节
} catch (const std::exception & ex) {
你可以在https://books.google.ru/books?id=6tjfmnKhT24C Chapter 9
另外,您也可以提供自定义消息,但要小心 – 使用std::string
或std::stringstream
或其他任何可能导致exception的方式std::stringstream
消息是不安全的 。
通常,在exception的构造函数中分配内存(使用C ++方式处理string)还是在抛出之前, std::bad_alloc
exception可以抛在你真正想要的exception之前。
所以,在堆栈上分配一个缓冲区(比如在Maxim的答案中)是一个更安全的方法。
在http://www.boost.org/community/error_handling.html中有很好的解释;
所以,更好的方法将是一个特定types的exception,并避免组成格式化的string(至less在投掷时)。