Pregunta Cómo lanzar std :: excepciones con mensajes variables?


Este es un ejemplo de lo que hago a menudo cuando quiero agregar información a una excepción:

std::stringstream errMsg;
errMsg << "Could not load config file '" << configfile << "'";
throw std::exception(errMsg.str().c_str());

¿Hay alguna manera más agradable de hacerlo?


75
2017-09-04 10:48


origen


Respuestas:


Aquí está mi solución:

class Formatter
{
public:
    Formatter() {}
    ~Formatter() {}

    template <typename Type>
    Formatter & operator << (const Type & value)
    {
        stream_ << value;
        return *this;
    }

    std::string str() const         { return stream_.str(); }
    operator std::string () const   { return stream_.str(); }

    enum ConvertToString 
    {
        to_str
    };
    std::string operator >> (ConvertToString) { return stream_.str(); }

private:
    std::stringstream stream_;

    Formatter(const Formatter &);
    Formatter & operator = (Formatter &);
};

Ejemplo:

throw std::runtime_error(Formatter() << foo << 13 << ", bar" << myData);   // implicitly cast to std::string
throw std::runtime_error(Formatter() << foo << 13 << ", bar" << myData >> Formatter::to_str);    // explicitly cast to std::string

28
2017-09-04 11:32



Las excepciones estándar se pueden construir a partir de std::string:

#include <stdexcept>

char const * configfile = "hardcode.cfg";
std::string const anotherfile = get_file();

throw std::runtime_error(std::string("Failed: ") + configfile);
throw std::runtime_error("Error: " + anotherfile);

Tenga en cuenta que la clase base std::exception poder no ser construido así; tienes que usar una de las clases derivadas concretas.


129
2017-09-04 10:50



Hay diferentes excepciones como runtime_error, range_error, overflow_error, logic_error, etc. Necesitas pasar la cadena a su constructor, y puedes concatenar lo que quieras a tu mensaje. Eso es solo una operación de cuerda.

std::string errorMessage = std::string("Error: on file ")+fileName;
throw std::runtime_error(errorMessage);

También puedes usar boost::format Me gusta esto:

throw std::runtime_error(boost::format("Error processing file %1") % fileName);

21
2017-09-04 10:53



La siguiente clase es muy útil:

struct Error : std::exception
{
    char text[1000];

    Error(char const* fmt, ...) __attribute__((format(printf,2,3))) {
        va_list ap;
        va_start(ap, fmt);
        vsnprintf(text, sizeof text, fmt, ap);
        va_end(ap);
    }

    char const* what() const throw() { return text; }
};

Ejemplo de uso:

throw Error("Could not load config file '%s'", configfile.c_str());

13
2017-09-04 10:53



Utilice el operador literal de cadena si C ++ 14 (operator ""s)

using namespace std::string_literals;

throw std::exception("Could not load config file '"s + configfile + "'"s);

o define tu propio si está en C ++ 11. Por ejemplo

std::string operator ""_s(const char * str, std::size_t len) {
    return std::string(str, str + len);
}

Tu declaración de lanzamiento se verá así:

throw std::exception("Could not load config file '"_s + configfile + "'"_s);

que se ve bien y limpio.


9
2017-09-09 09:05



Una forma realmente más agradable sería crear una clase (o clases) para las excepciones.

Algo como:

class ConfigurationError : public std::exception {
public:
    ConfigurationError();
};

class ConfigurationLoadError : public ConfigurationError {
public:
    ConfigurationLoadError(std::string & filename);
};

La razón es que las excepciones son mucho más que simplemente transferir una cadena. Proporcionando diferentes clases para los errores, le das a los desarrolladores la oportunidad de manejar un error particular de una manera correspondiente (no solo mostrar un mensaje de error). Las personas que capten su excepción pueden ser tan específicas como necesiten si usa una jerarquía.

a) Uno puede necesitar saber el motivo específico

} catch (const ConfigurationLoadError & ex) {
// ...
} catch (const ConfigurationError & ex) {

a) otro no quiere saber detalles

} catch (const std::exception & ex) {

Puedes encontrar algo de inspiración sobre este tema en https://books.google.ru/books?id=6tjfmnKhT24C Capítulo 9

Además, puede proporcionar un mensaje personalizado también, pero tenga cuidado, no es seguro componer un mensaje con cualquiera std::string o std::stringstream o de cualquier otra manera que pueda causar una excepción.

En general, no hay diferencia si asigna memoria (trabajo con cadenas en modo C ++) en el constructor de la excepción o justo antes de tirar - std::bad_alloc se puede lanzar una excepción antes que la que realmente deseas.

Entonces, un buffer asignado en la pila (como en la respuesta de Maxim) es una forma más segura.

Se explica muy bien en http://www.boost.org/community/error_handling.html

Entonces, la mejor manera sería un tipo específico de la excepción y evitando componer la cadena formateada (al menos cuando se lanza).


1
2017-09-08 08:50