2016-05-12 7 views
0

Ich versuche, eine Exception-Klasse zu entwickeln, die es ermöglicht, relevante Datenströme zu sammeln.Kann ich einen Stream werfen?

Nach Custom stream to method in C++? Ich habe meine eigene Klasse erweitert:

class NetworkException : public std::exception, public std::ostream 

die Fehlerdaten aus einem Stream zu holen und dann wieder, was es durch .what() erworben.

Dann habe ich versucht, so etwas wie diese:

try { 
    ssize_t ret = send(sock, headBuffer, headLength, MSG_MORE); 

    if (ret <= 0) throw NetworkException() << "Error sending the header: " << strerror(errno); 

    // much more communication code 

} catch (NetworkException& e) { 
    connectionOK = false; 
    logger.Warn("Communication failed: %s",e.what()); 
} 

Aber der Compiler erzeugt einen Fehler:

HTTPClient.cpp:246:113: error: use of deleted function 'std::basic_ostream<char>::basic_ostream(const std::basic_ostream<char>&)' 

(das ist die Zeile mit den throw.)

Ich weiß, strömt don‘ Ich habe Kopierkonstruktoren, aber ich dachte, es wäre ausreichend, statt des Objekts eine Referenz zu finden. Wie kann ich das überwinden - kann ich ein Objekt werfen, das einen Strom "verschluckt"?

+0

Mögliches Duplikat von [C++: eine Ausnahme Werfen des Kopierkonstruktor aufruft] (http://stackoverflow.com/questions/10855506/c-throwing-an-exception-invokes-the-copy-constructor) –

+0

@MohamadElghawi: Nun, dann wird das "Kann ich irgendwie einen Kopierkonstruktor für ein Stream-Objekt erstellen?" –

Antwort

1

Sie nicht Objekt enthält Stream-Objekt mit Standard Copykonstruktor kopieren können, aber Sie können Ihre eigene Kopie Konstruktor schreiben, die Inhalte von Stream kopiert.

#include <iostream> 
#include <sstream> 

struct StreamableError : public std::exception { 
     template <typename T> 
     StreamableError& operator << (T rhs) { 
      innerStream << rhs; 
      return *this; 
     } 

     StreamableError() = default; 

     StreamableError(StreamableError& rhs) { 
       innerStream << rhs.innerStream.str(); 
     } 

     virtual const char* what() const noexcept { 
      str = innerStream.str(); //this can throw 
      return str.c_str(); 
     } 

    private: 
     std::stringstream innerStream; 
     mutable std::string str; 
}; 


int main() { 
     try { 
       throw StreamableError() << "Formatted " << 1 << " exception."; 
     } catch (std::exception& e) { 
       std::cout << e.what() << std::endl; 
     } 
} 

Lösung oben ist nicht perfekt. Wenn str = innerStream.str() wirft dann wird std::terminate aufgerufen. Wenn Sie what Methode machen wollen trully sein noexcept dann haben Sie zwei Möglichkeiten:

  1. Kopieren von Inhalten von Strom zu str Variable innerhalb Copykonstruktor. Dann können Sie die Methode what nicht aufrufen, um eine Ausnahmebedingungsnachricht zu erhalten, bevor Sie sie auslösen.
  2. Kopieren Sie den Inhalt des Streams auf str Variable innerhalb Kopie Konstruktor und << Operator. In diesem Fall können Sie eine Exception-Nachricht vor dem Wurf erhalten, aber Sie werden die Nachricht jedes Mal kopieren, wenn der Operator aufgerufen wird, was ein Overkill sein kann.
+0

Wenn einfache Kopie einer dummen kurzen Zeichenfolge wirft, dann ist die allgemeine Situation katastrophal genug, dass die Ausnahme erfolgreich abgeschlossen wird nicht helfen. Es ist nicht so, dass das Programm in solch einer Situation den normalen Betrieb wiederherstellen könnte, oder die Ursache der Situation wäre irgendwo in der Nähe dieser Ausnahme. Es bringt einen Lippenstift auf eine Leiche - wenn ich mich nicht auf die Fähigkeit verlassen kann, etwa 50 Zeichen zu kopieren, ist das Beste, was das System tun kann, den Wachhund treten zu lassen. Unter Debugger ist es egal. Ohne Debugger ist es sowieso nicht mehr zu retten. –

1

Ausnahmen werden immer mindestens einmal kopiert. Das Abfangen der Ausnahme durch Referenz vermeidet eine zweite Kopie.

Nur eine Idee: Vielleicht können Sie Ihren Stream in einem Smart-Zeiger wie std::shared_ptr einbetten und dann diesen Smart-Zeiger werfen.

Persönlich verwende ich normalerweise std::runtime_error überall.

+2

Persönlich vermeide ich die Verwendung von '' Ausnahmen wie 'std :: runtime_error', weil sie [während der Konstruktion werfen können] (http://stackoverflow.com/q/36106747/3919155). – jotik

+0

@jotik Wahr. Ausnahmen sind irgendwie in C++ gebrochen. – ZunTzu

1

Zusätzlich zu ZunTzus Antwort: Verwenden Sie stattdessen einen ostringstream.

::std::ostringstream what; 
what << "Error sending the header: " << strerror(errno); 
throw ::std::exception(what.str()); 

Eigentlich :: std :: exception hat keinen Konstruktor char const unter * oder :: std :: string const &, so benötigen Sie entweder eine entsprechende vorhandene subclass oder erstellen Sie Ihre eigenen verwenden.

1

Was Sie tun möchten, wurde zuvor von vielen Leuten versucht. Natürlich ist es möglich, erfordert aber einige Tricks (ähnlich denen, die für Streaming-Logger erforderlich sind).

Es stellt sich auch, weil eine schlechte Idee sein aus:

  1. koppelt sie das Konzept von Streaming auf das Konzept einer Ausnahme.

  2. Es kann mit einer einzigen Vorlage Funktion

In der Tat einfacher durchgeführt werden, sind hier drei sehr einfache Alternativen:

#include <iostream> 
#include <sstream> 
#include <exception> 
#include <stdexcept> 
#include <boost/format.hpp> 

template<class...Args> 
std::string collect(Args&&...args) 
{ 
    std::ostringstream ss; 
    using expand = int[]; 
    void(expand{0, ((ss << args), 0)...}); 
    return ss.str(); 
} 

struct collector : std::ostringstream 
{ 
    operator std::string() const { 
     return str(); 
    } 
}; 

// derive from std::runtime_error because a network exception will always 
// be a runtime problem, not a logic problem 
struct NetworkException : std::runtime_error 
{ 
    using std::runtime_error::runtime_error; 
}; 

int main() 
{ 
    try { 
     throw NetworkException(collect("the", " cat", " sat on ", 3, " mats")); 

    } catch (const std::exception& e) { 
     std::cout << e.what() << std::endl; 
    } 

    try { 
     throw NetworkException(collector() << "the cat sat on " << 3 << " mats"); 

    } catch (const std::exception& e) { 
     std::cout << e.what() << std::endl; 
    } 

    try { 
     throw NetworkException((boost::format("the cat sat on %1% mats") % 3).str()); 

    } catch (const std::exception& e) { 
     std::cout << e.what() << std::endl; 
    } 


    return 0; 
} 

erwartete Ausgabe:

the cat sat on 3 mats 
the cat sat on 3 mats 
the cat sat on 3 mats 

Und schließlich die wahrscheinlich stream-ähnliche Lösung:

template<class Exception> 
struct raise 
{ 
    [[noreturn]] 
    void now() const { 
     throw Exception(_ss.str()); 
    } 

    std::ostream& stream() const { return _ss; } 

    mutable std::ostringstream _ss; 
}; 

template<class Exception, class T> 
const raise<Exception>& operator<<(const raise<Exception>& r, const T& t) 
{ 
    using namespace std; 
    r.stream() << t; 
    return r; 
} 

struct now_type {}; 
static constexpr now_type now {}; 

template<class Exception> 
void operator<<(const raise<Exception>& r, now_type) 
{ 
    r.now(); 
} 

Aufrufort Beispiel:

raise<NetworkException>() << "the cat " << "sat on " << 3 << " mats" << now; 

ich den Sentinel now um verwendet habe irgendeine bösen destructor Schmu zu vermeiden.

+0

Ich überlege ernsthaft, ein wenig auf den 'Kollektor-Klasse'-Ansatz zu betrügen: Überladen Sie den' Operator 'der Ausnahme (Kollektor &) ', ohne ihn zu einem" Stream "zu machen. Auf diese Weise wird es syntaktisch konsistent aussehen, der Collector wird als "throwable" -Typ behandelt und die Exception wird nicht eng mit Alien-Konzepten gekoppelt. –

+0

@SF. hmm, du hast mir eine Idee gegeben ... –

+0

@SF. Siehe vierte Option am Ende. –

1

Can I throw a stream?

Nein, das geht nicht. Wenn eine Ausnahme ausgelöst wird, wird ein temporäres Objekt aus dem Ausnahmebedingungsausdruck erstellt. Da Stream-Objekte nicht aus einem anderen Stream-Objekt erstellt werden können, kann ein Stream-Objekt nicht geworfen werden.

Aus dem Standard-11 C++:

15.1 Throwing an exception

3 A throw-expression initializes a temporary object, called the exception object, the type of which is determined by removing any top-level cv-qualifiers from the static type of the operand of throw and adjusting the type from “array of T” or “function returning T” to “pointer to T” or “pointer to function returning T”, respectively. The temporary is an lvalue and is used to initialize the variable named in the matching handler (15.3).

Verwandte Themen