2016-07-20 12 views
2

Eines der alten Anti-Patterns ist, dass Leute den Fehlerstatus überprüfen und dann ziemlich nutzlose Nachrichten wie "Operation failed" anstatt "Operation Failed weil ..." zurückgeben. Ich möchte, dass C++ - Datei-E/A-Operationen mit Ausnahme fehlschlagen und die Fehlermeldung erhalten, warum sie fehlgeschlagen ist. Insbesondere möchte ich, dass das Objekt stream eine Exception auslöst, wenn die Dateierstellung fehlschlägt, und eine etwas nützlichere Nachricht wie "Berechtigung verweigert" oder "Keine Datei oder Pfad" erhalten.Wie bekomme ich IO-Fehlermeldungen beim Erstellen einer Datei in C++?

Dies ist trivial in Sprachen wie C# oder Java oder Python, aber irgendwie gibt es keine gut dokumentierte Möglichkeit, dies zu tun C++. Standardmäßig schlagen Iostream-Objekte nur unbemerkt fehl. Es gibt einige globale Fehlercodes, aber ich hätte lieber Ausnahmen. Nach viel Sucherei, las ich, dass Sie Ausnahmen mit folgenden Codezeile aktivieren:

my_file.exceptions(flog.exceptions() | std::ios::failbit | std::ifstream::badbit); 

Das funktioniert aber jetzt ist die Ausnahme, die ausgelöst wird ist std::ios_base::failure und die ex.what() gibt nutzlos Zeichenketten wie „basic_ios: :klar". Gemäß der C++ 11 Spezifikationen std::ios_base::failure sollte von System_error vererbt werden, der .code(). Message() hat, die die Ausnahmebedingungsnachricht geben wird. Lassen Sie uns diese Seltsamkeit hier beiseite und nicht Finger zeigen auf Person, die entschieden, was() sollte nicht tatsächliche Fehlermeldung zurückgeben :). Das Problem ist, dass selbst beim Kompilieren mit C++ 11 und G ++ 4.8.4, ich finde, dass std::ios_base::failure nicht tatsächlich von system_error geerbt wird.

Fragen

  1. Warum std::ios_base::failure nicht von system_error in neueste G ++ 4.8.4 auch beim Kompilieren mit C++ 11-Modus vererbt wird? Ist die Implementierung von C++ 11 von GCC in diesem Bereich unvollständig oder muss ich etwas mehr tun?
  2. Wie erreiche ich mein Ziel, Ausnahmen auszulösen, wenn IO-Operationen in C++ fehlschlagen und Fehlermeldungen angezeigt werden? Gibt es keine Möglichkeit, dies auch in den neuesten C++ 11 oder C++ 14 zu tun? Was sind die Alternativen?

Hier ist der Beispielcode. Sie können compile and run it here.

#include <iostream> 
#include <fstream> 
#include <system_error> 

int main() { 
    try { 
     std::ofstream flog; 
     flog.exceptions(flog.exceptions() | std::ios::failbit | std::ifstream::badbit); 
     flog.open("~/watever/xyz.tsv", std::ios::trunc); 
    } 
    catch (const std::ios_base::failure &ex) {  
     std::cout << "ios_base::failure: " << ex.what(); 
    } 
    catch(const std::system_error& ex) { 
     std::cout << "system_error: " << ex.code().message(); 
    } 
} 

Antwort

3
  1. Nach GCC C++11 status documentation, "Systemfehler Unterstützung" wird voll unterstützt.

    Und Bug 57953 - no C++11 compliant std::ios_base::failure found nach wurde std::ios_base::failure in Revision 217559 änderte sich von system_error in C++ 11 abzuleiten. Wenn Sie in der aktualisierten ios_base.h suchen, wird std::ios_base::failure von system_error abgeleitet, wenn _GLIBCXX_USE_CXX11_ABI definiert ist. Diese Definition wird in der Dokumentation des GCC Using Dual ABI erwähnt.

    Allerdings gibt es eine Regression in Bezug auf ABI Probleme mit std::ios_base::failure, die noch offen ist, aufgrund der Tatsache, dass einige Teile der Standardbibliothek definieren nicht _GLIBCXX_USE_CXX11_ABI:

    Bug 66145 - [5/6/7 Regression] std::ios_base::failure objects thrown from libstdc++.so use old ABI

  2. die kurze Antwort ist, - Sie können es wahrscheinlich nicht, jedenfalls nicht mit der aktuellen Implementierung von GCC. Es sei denn, Sie können alles in der Bibliothek mit _GLIBCXX_USE_CXX11_ABI definiert neu kompilieren.

+0

musste google http://en.cppreference.com/w/cpp/error/system_error zu finden, aber dank – strangeqargo

2

Auf POSIX Systeme ios Ausfälle eingestellt errno so können Sie mit, dass aussagekräftige Fehlermeldungen. Ich oft tun:

std::string getenv_as_string(std::string const& var) 
{ 
    auto ptr = std::getenv(var.c_str()); 
    return ptr ? ptr : ""; 
} 

// ~ doesn't work from C++ 
const std::string HOME = getenv_as_string("HOME"); 

int main() 
{ 
    try 
    { 
     std::ofstream ifs; 

     ifs.open(HOME + "/watever/xyz.tsv", std::ios::trunc); 

     if(!ifs) 
      throw std::runtime_error(std::strerror(errno)); 

     // Do stuff with ifs 
    } 
    catch(std::exception const& e) 
    { 
     std::cerr << e.what() << '\n'; 
    } 
} 

Ausgang:

No such file or directory 
+0

Wäre es sei besser, stattdessen ios_base :: failure exception zu werfen und fail() zu verwenden: 'if (flog.fail()) werfe std :: ios_base :: failure (std :: strerror (errno));' – ShitalShah