C++ 如何使用可变消息抛出 std::exceptions?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12261915/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 16:05:38  来源:igfitidea点击:

How to throw std::exceptions with variable messages?

c++exceptionexception-handling

提问by Ben

This is an example of what I often do when I want to add some information to an exception:

这是我想向异常添加一些信息时经常做的一个例子:

std::stringstream errMsg;
errMsg << "Could not load config file '" << configfile << "'";
throw std::exception(errMsg.str().c_str());

Is there a nicer way to do it?

有没有更好的方法来做到这一点?

采纳答案by Torsten

Here is my solution:

这是我的解决方案:

#include <stdexcept>
#include <sstream>

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 &);
};

Example:

例子:

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

回答by Kerrek SB

The standard exceptions can be constructed from a std::string:

标准异常可以从 a 构造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);

Note that the base class std::exceptioncan notbe constructed thus; you have to use one of the concrete, derived classes.

需要注意的是基类std::exception可以被这样构成; 您必须使用具体的派生类之一。

回答by Neel Basu

There are different exceptions such as runtime_error, range_error, overflow_error, logic_error, etc.. You need to pass the string into its constructor, and you can concatenate whatever you want to your message. That's just a string operation.

有不同的例外,例如runtime_errorrange_erroroverflow_errorlogic_error,等等。你需要将字符串传递到它的构造,并且您可以连接任何你想你的消息。这只是一个字符串操作。

std::string errorMessage = std::string("Error: on file ")+fileName;
throw std::runtime_error(errorMessage);

You can also use boost::formatlike this:

你也可以boost::format这样使用:

throw std::runtime_error(boost::format("Error processing file %1") % fileName);

回答by Maxim Egorushkin

The following class might come quite handy:

下面的类可能会派上用场:

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; }
};

Usage example:

用法示例:

throw Error("Could not load config file '%s'", configfile.c_str());

回答by Shreevardhan

Use string literal operator if C++14 (operator ""s)

如果 C++14 ( operator ""s)使用字符串文字运算符

using namespace std::string_literals;

throw std::exception("Could not load config file '"s + configfile + "'"s);

or define your own if in C++11. For instance

或者在 C++11 中定义你自己的 if。例如

std::string operator ""_s(const char * str, std::size_t len) {
    return std::string(str, str + len);
}

Your throw statement will then look like this

您的 throw 语句将如下所示

throw std::exception("Could not load config file '"_s + configfile + "'"_s);

which looks nice and clean.

看起来漂亮干净。

回答by Arthur P. Golubev

A really nicer way would be creating a class (or classes) for the exceptions.

一个更好的方法是为异常创建一个(或多个)类。

Something like:

就像是:

class ConfigurationError : public std::exception {
public:
    ConfigurationError();
};

class ConfigurationLoadError : public ConfigurationError {
public:
    ConfigurationLoadError(std::string & filename);
};

The reason is that exceptions are much more than just transferring a string. Providing different classes for the errors, you give developers a chance to handle a particular error in a corresponded way (not just display an error message). People catching your exception can be as specific as they need if you use a hierarchy.

原因是异常不仅仅是传输字符串。为错误提供不同的类,您可以让开发人员有机会以相应的方式处理特定错误(不仅仅是显示错误消息)。如果您使用层次结构,捕获您的异常的人员可以根据需要具体化。

a) One may need to know the specific reason

a) 可能需要知道具体原因

} catch (const ConfigurationLoadError & ex) {
// ...
} catch (const ConfigurationError & ex) {

a) another does not want to know details

a) 另一个不想知道细节

} catch (const std::exception & ex) {

You can find some inspiration on this topic in https://books.google.ru/books?id=6tjfmnKhT24CChapter 9

您可以在https://books.google.ru/books?id=6tjfmnKhT24C第 9 章中找到有关此主题的一些灵感

Also, you can provide a custom message too, but be careful - it is not safe to compose a message with either std::stringor std::stringstreamor any other way which can cause an exception.

此外,您也可以提供自定义消息,但要小心 -使用std::stringstd::stringstream或任何其他可能导致异常的方式编写消息是不安全的

Generally, there is no difference whether you allocate memory (work with strings in C++ manner) in the constructor of the exception or just before throwing - std::bad_allocexception can be thrown before the one which you really want.

通常,在异常的构造函数中或在std::bad_alloc抛出之前分配内存(以 C++ 方式处理字符串)没有区别 -异常可以在您真正想要的异常之前抛出。

So, a buffer allocated on the stack (like in Maxim's answer) is a safer way.

因此,在堆栈上分配缓冲区(如 Maxim 的回答)是一种更安全的方法。

It is explained very well at http://www.boost.org/community/error_handling.html

它在http://www.boost.org/community/error_handling.html 中有很好的解释

So, the nicer way would be a specific type of the exception and be avoiding composing the formatted string (at least when throwing).

因此,更好的方法是使用特定类型的异常并避免组合格式化字符串(至少在抛出时)。