Derivando streambuf o basic_ostringstream?
-
20-09-2019 - |
Domanda
Voglio derivare una stringstream in modo che possa utilizzare l'operatore << per costruire un messaggio che verrà poi gettato. L'API sarà simile:
error("some text") << " more text " << 42 << std::endl;
Questo dovrebbe fare un
throw "some text more text 42"
Quindi quello che ho fatto è fare un errorbuf (che eredita dalla streambuf) che sovraccarica il metodo di 'overflow' e quindi creare un ostream (e errorbuf). Mi chiedo se non dovrei, invece ereditare da basic_ostringstream o qualcosa del genere ...
Soluzione
Io tirar fuori il mio macro preferito di nuovo qui:
#define ATHROW( msg ) \
{ \
std::ostringstream os; \
os << msg; \
throw ALib::Exception( os.str(), __LINE__, __FILE__ ); \
} \
In uso:
ATHROW( "Invalid value: " << x << " should be " << 42 );
il tipo di eccezione è dalla mia biblioteca, ma penso che si ottiene l'idea. Questo è molto più semplice di derivare la propria classe torrente, ed evita un sacco di complicazioni brutto con op << ().
Altri suggerimenti
Si potrebbe forse rendere più facile facendo qualcosa di simile:
class error_builder
{
public:
error_builder(const std::string& pMsg = "")
{
mMsg << pMsg;
}
~error_builder(void)
{
throw std::runtime_error(mMsg.str());
}
template <typename T>
error_builder& operator<<(const T& pX)
{
mMsg << pX;
return *this;
}
private:
std::stringstream mMsg;
};
error_builder("some text") << " more text " << 42 << std::endl;
Si noti che non si dovrebbe gettare le stringhe come si sta, quindi ho usato std::runtime_error
. Tutte le eccezioni dovrebbero derivare da std::exception
, che runtime_error
fa, in questo modo tutte le eccezioni significative possono essere catturati con std::exception&
const.
Questo funziona perché la vita temporanea fino alla fine della piena espressione.
Alcuni operatori non sono presenti soluzione Gman.
class error {
public:
explicit error(const std::string& m = "") :
msg(m, std::ios_base::out | std::ios_base::ate)
{}
~error() {
if(!std::uncaught_exception()) {
throw std::runtime_error(msg.str());
}
}
template<typename T>
error& operator<<(const T& t) {
msg << t;
return *this;
}
error& operator<<(std::ostream& (*t)(std::ostream&)) {
msg << t;
return *this;
}
error& operator<<(std::ios& (*t)(std::ios&)) {
msg << t;
return *this;
}
error& operator<<(std::ios_base& (*t)(std::ios_base&)) {
msg << t;
return *this;
}
private:
std::ostringstream msg;
};
Io di solito basta creare il mio classi di eccezioni. Basta ignorare what()
e in grado di fornire il maggior numero di costruttori come ti piace. Per costruire il messaggio di errore, basta usare vasprintf (se disponibile) o std :: ostringstream come sopra.
Ecco un esempio:
class CustomException : public std::exception {
private:
const std::string message;
public:
CustomException(const std::string &format, ...) {
va_list args;
va_start(args, format);
char *formatted = 0;
int len = vasprintf(&formatted, format.c_str(), args);
if (len != -1) {
message = std::string(formatted);
free(formatted);
} else {
message = format;
}
va_end(args);
}
const char *what() const {
return message.c_str();
}
};
Se non si dispone di vasprintf, è anche possibile utilizzare vsnprintf con un buffer sullo stack ...