Подтвердить что ты не робот

Понимание объекта <system_error> в С++ 11

Я пытаюсь использовать средство 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 для всего пространства имен commons и определять только std::error_condition для каждого пространства под-имен, например dynlib. Это хорошая практика? Я бы сказал, да, потому что это позволит избежать дублирования кода. Но есть ли уловка за этим?
  • В настоящий момент я использую единственный std::error_category для каждого пространства под-имен общих прав. Это хорошая практика? Как вы думаете, я должен использовать std::error_category по-другому?
4b9b3361

Ответ 1

Основное отличие состоит в том, что 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).