2014-02-17 14 views
17

Ich versuche, die system_error Einrichtung zu verwenden, um Fehler in einer Bibliothek von mir zu behandeln. Ich werde kurz die Struktur der Bibliothek besprechen, falls Sie es hilfreich finden: Der Namensraum der Bibliothek heißt commons und darunter habe ich einen anderen Namensraum namens dynlib. dynlib enthalten Klassen, die zum Laden von .so/DLL-Dateien verantwortlich sind:Verständnis der <system_error> Einrichtung in C++ 11

namespace commons { 
    namespace dynlib { 
     class DynLibLoader { 
     }; 
    } 
} 

Die Fehler, die in den DynLibLoader auftreten können, sind LibraryFailedToLoad, LibraryFailedToUnload und SymbolNotFound. Also meine Gedanken für die Behandlung der Fehler sind die folgenden: Ich werde einen Namespace error unter dem Namespace dynlib hinzufügen. Dann definiere ich unter diesem Namensraum eine Enum für std::error_codes und eine Enum für std::error_conditions. Von meinem Verständnis der std::error_codes auf den Wert von errno (Linux) oder GetLastError (Win32) und die std::error_conditions auf Werte wie LibraryFailedToLoad, SymbolNotFound usw. Also, hier entsprechen haben, sind meine Fragen:

  • Ist mein Verständnis std::error_code und std::error_condition richtig?
  • Wie soll ich alle möglichen Werte von errno und GetLastError() kennen, um sie unter meiner std::error_codes enum zu definieren? Was ist, wenn Microsoft der API in Zukunft zusätzliche Fehlerwerte hinzufügt? Muss ich zurück zum Quellcode gehen und sie unter der Enum definieren, die ich für die std::error_codes habe?
  • Was ist, wenn wir uns auf einer anderen Plattform befinden und es keine Möglichkeit gibt, den genauen Systemfehlercode herauszufinden, wenn ein Fehler auftritt?
  • Was passiert, wenn ich die gleiche std::error_codes für den gesamten commons Namensraum haben wollen und nur eine andere std::error_condition für jeden Teilnamespace wie dynlib definieren. Ist das eine gute Übung? Ich würde ja sagen, weil dies doppelten Code vermeidet. Aber steckt dahinter ein Haken?
  • Im Moment verwende ich eine einzige std::error_category für jeden Sub-Namespace von Commons. Ist das eine gute Übung? Denkst du, ich sollte die std::error_category anders verwenden?

Antwort

11

Der Hauptunterschied ist, dass std::error_condition tragbar ist (plattformunabhängig), während std::error_code ist plattformabhängig. Typischerweise generiert plattformabhängiger Low-Level-Code error_codes und der Client-Code vergleicht diese error_codes mit plattformunabhängigen error_conditions.

19,5 [SYSERR] definiert eine lange Liste von Standard (und portabel) Fehlerbedingungen (z.B. errc::no_such_file_or_directory), die auf bestimmte Werte von errno (z.B. ENOENT) explizit zugeordnet sind. Daher müssen Sie die vollständige Liste der möglichen Werte errno oder GetLastError(), die auf Ihrem System generiert wurden, nicht kennen. Sie müssen nur die für Ihren Code relevanten Standardwerte kennen.Zum Beispiel könnte die Bibliothek Umsetzung wie folgt aussehen:

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); 
} 

Ihr Client-Code würde dann prüfen, ob die error_code jede spezifische error_condition Spiele:

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 
} 

In Ihrem Fall würden Sie wahrscheinlich Ihre eigene Aufzählung definieren von Fehlern (nur eine Aufzählung) und als error_conditions markieren automatische Konvertierung zu aktivieren:

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 

Sie

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); 
    } 
} 

Ihr Client-Code den Fehlercode zu den möglichen Fehlerbedingungen vergleichen würde, wie oben: (wenn Sie es vorziehen oder error_code) könnten dann die Ergebnisse der verschiedenen plattformabhängigen Vorgänge in den entsprechenden error_condition übersetzen

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 
} 

beachten, dass der ENUM commons::dynlib::errc::LibraryFailedToLoad automatisch in eine error_condition umgewandelt wird (die vorgesehen make_error_condition Methode), da commons::dynlib::errc mit is_error_condition_enum markiert.

Das Mapping von error_category auf den Namensraum ist wahrscheinlich eine persönliche Vorliebe, aber es scheint ein bisschen künstlich. In diesem speziellen Fall ist es zwar sinnvoll, eine Kategorie für den Namensraum dynlib zu haben, aber es wäre leicht, Beispiele zu finden, bei denen es sinnvoll wäre, Kategorien zu haben, die mehrere Namensräume verteilen. In einigen Fällen kann es sinnvoll und praktisch sein, alle Ihre verschiedenen Fehleraufrufe in einem eindeutigen Namespace zu haben (z. B. commons::errors).