Как бросить std :: исключения с переменными сообщениями?

Это пример того, что я часто делаю, когда хочу добавить некоторую информацию в исключение:

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

Есть ли лучший способ сделать это?

88

Решение

Вот мое решение:

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
31

Другие решения

Стандартные исключения могут быть построены из 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 Можно не быть построенным таким образом; Вы должны использовать один из конкретных, производных классов.

142

Есть разные исключения, такие как 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);
24

Следующий класс может пригодиться:

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());
15

Используйте оператор строкового литерала, если 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 std::exception("Could not load config file '"_s + configfile + "'"_s);

который выглядит красиво и чисто.

11

Более хорошим способом было бы создание класса (или классов) для исключений.

Что-то вроде:

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

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

Причина в том, что исключения гораздо больше, чем просто передача строки. Предоставляя различные классы для ошибок, вы даете разработчикам возможность обрабатывать определенную ошибку соответствующим образом (не просто отображать сообщение об ошибке). Люди, перехватывающие ваше исключение, могут быть настолько конкретными, насколько им нужно, если вы используете иерархию.

а) Может понадобиться узнать конкретную причину

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

а) другой не хочет знать подробности

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

Вы можете найти вдохновение на эту тему в https://books.google.ru/books?id=6tjfmnKhT24C Глава 9

Кроме того, вы также можете предоставить свое сообщение, но будьте осторожны — составлять сообщение с std::string или же std::stringstream или любым другим способом, который может вызвать исключение.

Как правило, нет никакой разницы, выделяете ли вы память (работаете со строками в стиле C ++) в конструкторе исключения или непосредственно перед броском — std::bad_alloc Исключение может быть брошено перед тем, который вы действительно хотите.

Таким образом, буфер, размещенный в стеке (как в ответе Максима), является более безопасным способом.

Это очень хорошо объясняется в http://www.boost.org/community/error_handling.html

Таким образом, более удачный способ — это особый тип исключения и избегание составления форматированной строки (по крайней мере, при выдаче).

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector