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

时间:2012-09-04 10:48:35

标签: c++ exception exception-handling

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

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

有更好的方法吗?

8 个答案:

答案 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_errorrange_erroroverflow_errorlogic_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::stringstd::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值临时变量,因此在此行结束后超出范围。您的异常对象的构造函数必须使用复制或(更好)移动语义获取输入字符串。

其他:我不一定认为这种方法是“最佳实践”,但确实有效,可以在紧要关头使用。最大的问题之一是此方法需要堆分配,因此运算符<<可以抛出。您可能不希望发生这种情况;但是,如果进入这种状态,您可能还有更多的问题要担心!