2014-02-17 5 views
17

Я пытаюсь использовать средство system_error для обработки ошибок в моей библиотеке. Я кратко расскажу о структуре библиотеки, если вы сочтете ее полезной: пространство имен библиотеки называется commons, и под этим я имею другое пространство имен, которое называется dynlib. dynlib содержит классы, которые отвечают за загрузку .so/.dll файлов:Понимание объекта <system_error> в C++ 11

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 по-другому?

ответ

11

Главным отличием является то, что 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 

You то может перевести исход различных операций зависит от платформы в соответствующий 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).