Я использую класс, унаследованный от std::system_error
для обработки ошибок, и я хотел бы контролировать, что возвращается, когда what()
называется. Причина: стандарт (как C ++ 11, так и проект C ++ 1y CD — N3690, ссылки на него приведены ниже) не указывает, как именно строка, возвращаемая what()
должен выглядеть так: просто укажите в §19.5.6.2 (14):
Примечание. Возвращенный NTBS может быть содержимым
what_arg + ": " +
, — конец примечания
code.message()
так что это должно считаться зависимым от реализации. (Кстати, не должно быть code().message()
вместо code.message()
?)
Итак, вопрос: как я могу точно определить строку, которая what()
возвращается, если я хочу быть стандартным и не полагаться на реализацию (т.е. хочу быть переносимым)?
Для тех, кто предпочитает код:
class my_class : public std::system_error {
public:
my_class(std::error_code ec, std::string const & what_arg)
: system_error(ec, /* this string is not required to be equal to what is returned by what() */)
{
// ok, try it here
// but what is the name of the member storing the string?
}
const char * what() const noexcept
{
// ok, try it here
// but how to access what_arg in its unaltered form?
}
};
Хорошо, тривиальное решение, которое мне не нравится, может быть следующим:
class my_class : public std::system_error {
std::string my_what;
public:
my_class(std::error_code ec, std::string const & what_arg)
: system_error(ec, what_arg),
my_what( /* construct my what string */ )
{ }
const char * what() const noexcept
{ return my_what.c_str(); }
};
поскольку std::exception::what()
Виртуальный, он будет работать, но есть ли более элегантный способ без использования каких-либо деталей реализации? Мне не нравится идея хранить две строки: одну в std::system_error
а другой в my_what
,
Корень проблемы: std :: runtime_error — который является родительским классом std :: system_error — имеет точное требование в §1.9.2.6 (3), постусловии конструктора:
strcmp(what(), what_arg.c_str()) == 0
Который, в случае std::system_error
становится следующим в §19.5.6.2 (2):
string(what()).find(what_arg) != string::npos
Кто-нибудь знает, почему стандарт так старается включить code().message()
в what()
? Обратите внимание, что code()
возвращает объект кода ошибки, так что любой может включить code().message()
в строке в любое время (даже в то время, когда ловится исключение этого класса).
Если требование std::system_error
был такой же, как std::runtime_error
Я мог бы просто написать:
class my_class : public std::system_error {
public:
my_class(std::error_code ec, std::string const & what_arg)
: system_error(ec, /* build my what string here */ )
{ }
};
Есть ли элегантное и портативное решение?
ОБНОВИТЬ:
Многие комментарии ниже утверждают, что сообщения об ошибках определяются реализацией. Я понимаю это, я просто хочу формат строка, возвращаемая what()
Я не хочу, чтобы он был побайтовым эквивалентом во всех системах. Подумайте только о том, что я хочу записать его или передать третьему лицу, и оно должно соответствовать некоторому фиксированному формату (что не соответствует стандарту).
UPDATE2:
Я считаю, что std :: system_error не только для ошибок ОС или STL. Я могу (и предполагаю) извлечь из него свои собственные классы и использовать их для сообщений об ошибках. Что если я пишу API низкого уровня? Кстати, почему запрещено использовать его в API высокого уровня?
Если я передам все аргументы его конструктору в части обработки ошибок моего API, есть нет задействованы определенные (то есть неизвестные) строки ошибок, но я все еще не могу отформатировать их без дублирования данных.
Я не знаю «элегантного и портативного» решения, но я не думаю, что есть что-то не так с решением, которое вы предлагаете в вопросе, а именно: сделать свою собственную копию what_arg
,
Объект исключения не будет длиться очень долго: как правило, только достаточно долго, чтобы разматывать стек. Кроме того, строка в what_arg
вряд ли будет очень длинным, учитывая, что мегабайт what
не будет очень читабельным. И, наконец, объект исключения будет создаваться только в исключительных обстоятельствах, поэтому «ненужное» дублирование небольшой строки не окажет заметного влияния на производительность вашей программы.
По сути, дизайнеры класса решили сделать состояние класса непрозрачным, и, прямо скажем, вы не доверяете им в получении полезного результата (читаемого сообщения) из этого состояния. В этом случае вам придется дублировать состояние. Это не редкое последствие инкапсуляции состояний, и общим решением почти всегда является дублирование состояния. В этом случае стоимость незначительна, поэтому, если контролировать стоимость what()
Это важно для вас, вы должны просто принять стоимость и двигаться дальше.
Я хочу сделать более длинный комментарий, поэтому я опубликую это как ответ сообщества вики.
Стандарт (C ++ 14 черновик N3690) описывает system_error
в [syserr.syserr.overview] / 1:
Класс
system_error
описывает объект исключения, используемый для сообщения об ошибках, связанных с кодом ошибки. Такие ошибки обычно возникают из-за операционной системы или других низкоуровневых интерфейсов прикладных программ.
Ошибки и сообщения об ошибках от операционная система или другие низкоуровневые API обязательно непереносимой.
Кроме того, если вы производите от system_error
кто-то, кто перехватывает исключение по ref, может ожидать коды ошибок от OS / API низкого уровня, как указано в реализации C ++; по крайней мере, этот пользователь знает результат what()
Стандарт не гарантируется. Если этот пользователь ловит исключение вашего производного типа, вы можете также предоставить другое what2()
функция, которая точно возвращает вашу строку.
Я бы предложил решение не извлечь из system_error
, Насколько я вижу, ваш анализ гарантий (и опечатка code().message()
), то есть вы не можете точно указать возврат what()
за system_error
,
Если вы хотите иметь код ошибки в вашем исключении, вы можете использовать error_code
сооружения; все же, как и с system_error
Стандарт указывает в [syserr] / 1
В этом подпункте описаны компоненты, которые стандартные библиотеки и программы на C ++ могут использовать для сообщения об ошибках, возникающих в операционной системе или других низкоуровневых интерфейсах прикладных программ.
Поэтому может быть лучше использовать другой тип кода ошибки.
Насколько я вижу, исходя из system_error
полезно, если вы хотите улучшить сообщение об ошибке от system_error
s, происходящие из OS / API низкого уровня. В этом случае вы можете добавить описание к сообщению (what()
) и вот что system_error
гарантии на сообщение, передаваемое в ctor.
Еще один «комментарий», чтобы ответить, как вы могли бы извлечь из system_error
и точно указать возврат what()
:
class my_class : public std::system_error
{
std::string my_what;
public:
my_class(std::error_code ec, std::string const& what_arg)
: system_error(ec)
// ^^^^ note: NOT passing the string
, my_what( /* construct my what string */ )
{ }
const char* what() const noexcept
{ return my_what.c_str(); }
};
Да, есть дублирование данных в том смысле, что есть (возможно) два строковых объекта, но нет дублирования данных содержимого строки.
Если вам не нужно передавать строку в ctor исключения (например, тип исключения и код ошибки достаточно описательны), вы можете даже пропустить объект string; что-то вроде
class my_class : public std::system_error
{
public:
my_class(std::error_code ec)
: system_error(ec)
// ^^^^ note: NOT passing the string
{ }
const char* what() const noexcept
{ return "description of error"; }
};
Хотя, вероятно, лучше включить код ошибки в описание, возвращаемое what()
(требуется какое-то хранилище в my_class
).
Возможно, стоит упомянуть, что конструкция струны по запросу, по требованию (то есть когда what()
называется) может быть благоприятным.