当我想向异常添加一些信息时,这是我经常做的一个例子:
std::stringstream errMsg;
errMsg << "Could not load config file '" << configfile << "'";
throw std::exception(errMsg.str().c_str());
有更好的方法吗?
答案 0 :(得分:147)
可以从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
可以不构造;你必须使用一个具体的派生类。
答案 1 :(得分:35)
这是我的解决方案:
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
答案 2 :(得分:24)
有runtime_error
,range_error
,overflow_error
,logic_error
等不同的例外情况。您需要将字符串传递给其构造函数,并且可以连接无论你想要什么,你的消息。那只是一个字符串操作。
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);
答案 3 :(得分:15)
以下课程可能非常方便:
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());
答案 4 :(得分:11)
如果是C ++ 14(operator ""s
)
using namespace std::string_literals;
throw std::exception("Could not load config file '"s + configfile + "'"s);
或在C ++ 11中定义自己的if。例如
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);
看起来很干净。
答案 5 :(得分:0)
一种非常好的方法是为异常创建一个类(或类)。
类似的东西:
class ConfigurationError : public std::exception {
public:
ConfigurationError();
};
class ConfigurationLoadError : public ConfigurationError {
public:
ConfigurationLoadError(std::string & filename);
};
原因是异常不仅仅是转移字符串。为错误提供不同的类,您可以让开发人员有机会以相应的方式处理特定错误(而不仅仅是显示错误消息)。捕获异常的人可以根据需要使用层次结构。
a)可能需要知道具体原因
} catch (const ConfigurationLoadError & ex) {
// ...
} catch (const ConfigurationError & ex) {
a)另一个人不想了解细节
} catch (const std::exception & ex) {
您可以在https://books.google.ru/books?id=6tjfmnKhT24C第9章
中找到有关此主题的一些灵感此外,您也可以提供自定义消息,但要小心 - 使用std::string
或std::stringstream
或任何其他可能导致异常的方式撰写邮件是不安全的即可。
通常,无论是在异常的构造函数中还是在抛出之前分配内存(以C ++方式使用字符串)都没有区别 - std::bad_alloc
异常可以在您真正想要的异常之前抛出。 / p>
因此,在堆栈上分配的缓冲区(如Maxim的答案中)是一种更安全的方式。
http://www.boost.org/community/error_handling.html
解释得很清楚因此,更好的方法是特定类型的异常,并避免编写格式化的字符串(至少在投掷时)。
答案 6 :(得分:0)
遇到了类似的问题,即为我的自定义异常创建自定义错误消息会产生难看的代码。这是我的解决方案:
class MyRunTimeException: public std::runtime_error
{
public:
MyRunTimeException(const std::string &filename):std::runtime_error(GetMessage(filename)) {}
private:
static std::string GetMessage(const std::string &filename)
{
// Do your message formatting here.
// The benefit of returning std::string, is that the compiler will make sure the buffer is good for the length of the constructor call
// You can use a local std::ostringstream here, and return os.str()
// Without worrying that the memory is out of scope. It'll get copied
// You also can create multiple GetMessage functions that take all sorts of objects and add multiple constructors for your exception
}
}
这将分隔创建消息的逻辑。我最初曾考虑过重写what(),但是随后您必须将消息捕获到某个地方。 std :: runtime_error已经有一个内部缓冲区。
答案 7 :(得分:0)
也许是吗?
throw std::runtime_error(
(std::ostringstream()
<< "Could not load config file '"
<< configfile
<< "'"
).str()
);
它创建一个临时的ostringstream,必要时调用<<操作符,然后将其包装在圆括号中,然后对评估结果(为ostringstream)调用.str()函数以传递临时的std :: string到runtime_error的构造函数。
注意:ostringstream和字符串是r值临时变量,因此在此行结束后超出范围。您的异常对象的构造函数必须使用复制或(更好)移动语义获取输入字符串。
其他:我不一定认为这种方法是“最佳实践”,但确实有效,可以在紧要关头使用。最大的问题之一是此方法需要堆分配,因此运算符<<可以抛出。您可能不希望发生这种情况;但是,如果进入这种状态,您可能还有更多的问题要担心!