Я пытаюсь использовать system_error
средство для обработки ошибок в моей библиотеке. Я собираюсь кратко обсудить структуру библиотеки на случай, если она окажется вам полезной: пространство имен библиотеки называется commons
и под этим у меня есть еще одно пространство имен dynlib
, dynlib
содержит классы, отвечающие за загрузку файлов .so / .dll:
namespace commons {
namespace dynlib {
class DynLibLoader {
};
}
}
Ошибки, которые могут возникнуть в DynLibLoader: LibraryFailedToLoad
, LibraryFailedToUnload
а также SymbolNotFound
, Поэтому мои мысли по поводу обработки ошибок следующие: я добавлю пространство имен error
под пространством имен dynlib
, Затем в этом пространстве имен я определю одно перечисление для std::error_codes
и одно перечисление для std::error_conditions
, Из моего понимания std::error_codes
должны соответствовать значению errno
(Linux) или GetLastError
(Win32), а std::error_conditions
к таким ценностям, как LibraryFailedToLoad
, SymbolNotFound
и т.д. Итак, вот мои вопросы:
std::error_code
а также std::error_condition
правильный?errno
а также GetLastError()
для того, чтобы определить их под моим std::error_codes
перечисление? Что если Microsoft добавит дополнительные значения ошибок в API в будущем? Должен ли я вернуться к исходному коду и определить его в соответствии с перечислением, которое у меня есть для std::error_codes
?std::error_codes
для всего пространства имен общего и только определить другой std::error_condition
для каждого под-пространства имен, как dynlib
, Это хорошая практика? Я бы сказал, да, потому что это позволит избежать дублирования кода. Но есть ли подвох за этим?std::error_category
для каждого подпространства имен общего достояния. Это хорошая практика? Как вы думаете, я должен использовать std::error_category
по-другому?Основное различие заключается в том, что std::error_condition
является портативным (независимым от платформы), в то время как std::error_code
зависит от платформы. Как правило, низкоуровневый зависимый от платформы код генерирует error_codes
и код клиента сравнивает эти error_codes
независимым от платформы error_conditions
,
19.5 [syserr] определяет длинный список стандартных (и переносимых) состояний ошибки (например, errc::no_such_file_or_directory
), которые явно связаны с конкретными значениями errno
(например. ENOENT
). В результате вам не нужно знать полный список возможных значений errno
или же GetLastError()
генерируется в вашей системе. Вам нужно знать только стандартные значения, относящиеся к вашему коду. Например, ваша реализация библиотеки может выглядеть так:
void MyLibraryClass::foo(std::error_code &ec)
{
// whatever platform dependent operation that might set errno
// possibly with alternative platform-dependent implementations
ec = make_error_code(errno);
}
Ваш код клиента будет затем проверять, если error_code
соответствует любому конкретному error_condition
:
error_code ec;
myLibraryInstance.foo(ec);
if (!ec)
{
// success
}
else if (errc::no_such_file_or_directory == ec)
{
// no_such_file_or_directory
}
else
{
// unknown or unexpected error
}
В вашем случае вы, вероятно, определите собственное перечисление ошибок (только одно перечисление) и отметите его как error_conditions
включить автоматическое преобразование:
namespace commons
{
namespace dynlib
{
enum class errc {LibraryFailedToLoad=1, LibraryFailedToUnload, SymbolNotFound};
}
}
namespace std
{
template<> struct is_error_condition_enum<commons::dynlib::errc> : true_type {};
}
// TODO: implement make_error_code and make_error_condition
Затем вы можете перевести результаты различных зависящих от платформы операций в соответствующие error_condition
(или же error_code
Если вы предпочитаете):
void DynLibLoader::open(std::error_code &ec)
{
// possibly implement the windows version here as well
if (NULL == dlopen(filename, flag))
{
ec = make_error_code(errc::LibraryFailedToLoad);
}
}
Ваш код клиента будет сравнивать код ошибки с возможными условиями ошибки, как указано выше:
error_code ec;
dynLibLoader.open(ec);
if (!ec)
{
// success
}
else if (commons::dynlib::errc::LibraryFailedToLoad == ec)
{
// Library Failed To Load
}
else
{
// unknown or unexpected error
}
Обратите внимание, что перечисление commons::dynlib::errc::LibraryFailedToLoad
автоматически преобразуется в error_condition
(используя предоставленную make_error_condition
метод), потому что commons::dynlib::errc
помечен is_error_condition_enum
,
Отображение error_category
пространство имен, вероятно, является личным предпочтением, однако это выглядит несколько искусственно. В данном конкретном случае верно, что имеет смысл иметь категорию для dynlib
пространства имен, но было бы легко найти примеры, в которых было бы целесообразно иметь категории, распространяющиеся на несколько пространств имен. В некоторых случаях может быть целесообразным и практичным иметь все различные перечисления ошибок в уникальном пространстве имен (например, commons::errors
).
Других решений пока нет …