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());
¿Existe una forma mejor de hacerlo?
std∷exception
no tienen un constructor conchar*
arg.std::string
tiene un constructor implícito que toma unconst char*
...std::exception
las clases secundarias de MS , y es utilizado por sus versiones destd::runtime_error
ystd::logic_error
. Aparte de los definidos por el estándar, la versión de MSVS<exception>
también incluye dos constructores más, uno tomando(const char * const &)
y el otro tomando(const char * const &, int)
. Se utilizan para establecer una variable privadaconst char * _Mywhat
; si_Mywhat != nullptr
, porwhat()
defecto lo devuelve. El código que se basa en él probablemente no sea portátil.Respuestas:
Aquí está mi solución:
Ejemplo:
fuente
Las excepciones estándar se pueden construir a partir de
std::string
:Tenga en cuenta que la clase base
std::exception
puede no estar construido de este modo; tienes que usar una de las clases derivadas concretas.fuente
Hay diferentes excepciones como
runtime_error
,range_error
,overflow_error
,logic_error
, etc .. Es necesario para pasar la cadena en su constructor, y puede concatenar lo que quiera a su mensaje. Eso es solo una operación de cadena.También puedes usar
boost::format
así:fuente
La siguiente clase puede resultar muy útil:
Ejemplo de uso:
fuente
throw std::runtime_error(sprintf("Could not load config file '%s'", configfile.c_str()))
throw std::runtime_error("Could not load config file " + configfile);
(convirtiendo uno u otro argumento astd::string
si es necesario).printf
y los amigos son inminentes en C ++ 11. El búfer de tamaño fijo es tanto una bendición como una maldición: no falla en situaciones de bajos recursos, pero puede truncar el mensaje. Considero que truncar un mensaje de error es una mejor opción que fallar. Además, la conveniencia de las cadenas de formato ha sido probada en muchos idiomas diferentes. Pero tienes razón, es en gran parte una cuestión de gustos.Utilice un operador literal de cadena si C ++ 14 (
operator ""s
)o defina el suyo si está en C ++ 11. Por ejemplo
Tu declaración de lanzamiento se verá así
que se ve bonito y limpio.
fuente
Una forma realmente mejor sería crear una clase (o clases) para las excepciones.
Algo como:
La razón es que las excepciones son mucho más preferibles que simplemente transferir una cadena. Al proporcionar diferentes clases para los errores, les da a los desarrolladores la oportunidad de manejar un error en particular de la manera correspondiente (no solo mostrar un mensaje de error). Las personas que detectan tu excepción pueden ser tan específicas como necesiten si usas una jerarquía.
a) Es posible que deba saber la razón específica
a) otro no quiere saber detalles
Puede encontrar algo de inspiración sobre este tema en https://books.google.ru/books?id=6tjfmnKhT24C Capítulo 9
Además, también puede proporcionar un mensaje personalizado, pero tenga cuidado, no es seguro redactar un mensaje de una
std::string
ustd::stringstream
otra forma que pueda causar una excepción .En general, no hay diferencia si asigna memoria (trabaja con cadenas en C ++) en el constructor de la excepción o justo antes de lanzarla; la
std::bad_alloc
excepción se puede lanzar antes de la que realmente desea.Entonces, un búfer 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 forma más agradable sería un tipo específico de excepción y evitar componer la cadena formateada (al menos al lanzar).
fuente
Me encontré con un problema similar, ya que la creación de mensajes de error personalizados para mis excepciones personalizadas crea un código desagradable. Esta fue mi solución:
Esto separa la lógica para crear los mensajes. Originalmente había pensado en anular qué (), pero luego tienes que capturar tu mensaje en alguna parte. std :: runtime_error ya tiene un búfer interno.
fuente
¿Tal vez esto?
Crea un flujo de avestruz temporal, llama a los operadores << según sea necesario y luego lo envuelve entre paréntesis y llama a la función .str () en el resultado evaluado (que es un flujo de avestruz) para pasar un std :: cadena temporal al constructor de runtime_error.
Nota: ostringstream y la cadena son temporales de valor r y, por lo tanto, quedan fuera del alcance después de que finaliza esta línea. El constructor de su objeto de excepción DEBE tomar la cadena de entrada usando la semántica de copiar o (mejor) mover.
Adicional: No considero necesariamente este enfoque como una "mejor práctica", pero funciona y se puede utilizar en un apuro. Uno de los mayores problemas es que este método requiere asignaciones de montón y, por lo tanto, el operador << puede lanzar. Probablemente no quieras que eso suceda; sin embargo, si llega a ese estado, probablemente tenga muchos más problemas de los que preocuparse.
fuente