c++ multithreading c++11 exception-handling raii

c++ - Programa de terminación apropiada. Usando excepciones



multithreading c++11 (5)

De la documentación:

[[noreturn]] void exit (estado int); Terminar proceso de llamada Termina el proceso normalmente, realizando la limpieza regular para terminar programas.

La terminación normal del programa realiza lo siguiente (en el mismo orden): los objetos asociados con el hilo actual con la duración del almacenamiento del hilo se destruyen (solo C ++ 11). Los objetos con duración de almacenamiento estática se destruyen (C ++) y se registran las funciones registradas con atexit. Todos los flujos de C (abiertos con funciones en) se cierran (y se vacían, si se almacenan en búfer) y se eliminan todos los archivos creados con tmpfile. El control se devuelve al entorno de host.

Tenga en cuenta que los objetos con almacenamiento automático no se destruyen llamando a exit (C ++).

Si el estado es cero o EXIT_SUCCESS, se devuelve un estado de finalización exitoso al entorno de host. Si el estado es EXIT_FAILURE, se devuelve un estado de finalización sin éxito al entorno de host. De lo contrario, el estado devuelto depende del sistema y la implementación de la biblioteca.

Para una función similar que no realiza la limpieza descrita anteriormente, consulte quick_exit.

Pregunta: ¿Usar excepciones es la manera correcta de finalizar mi programa si todo lo que quiero es mostrar un mensaje de error y cerrar (teniendo en cuenta que puedo estar metido en el programa)? ¿Puedo simplemente llamar explícitamente algo como exit ()?

Lo que estoy haciendo actualmente:

Estoy trabajando en un proyecto de juego y estoy tratando de encontrar la mejor manera de terminar el programa en el caso de un error que requiera dicha acción. Por ejemplo, en el caso de que las texturas no se puedan cargar, visualizo un mensaje de error y termino el programa.

Actualmente estoy haciendo esto con excepciones como estas:

int main() { Game game; try { game.run(); } catch (BadResolutionException & e) { Notification::showErrorMessage(e.what(), "ERROR: Resolution"); return 1; } catch (BadAssetException & e) { Notification::showErrorMessage(e.what(), "ERROR: Assets"); return 1; } catch (std::bad_alloc & e) { Notification::showErrorMessage(e.what(), "ERROR: Memory"); return 1; } return 0; }

Todos menos bad_alloc son mis propias excepciones definidas derivadas de runtime_error.

No necesito ninguna limpieza manual de recursos y estoy usando std :: unique_ptr para cualquier asignación dinámica. Solo necesito mostrar el mensaje de error y cerrar el programa.

Investigación / Alternativas a Excepciones:

He buscado un montón de publicaciones en SO y otros lugares y he visto a otros decir algo, no utilices excepciones, para usar excepciones, pero que las uses incorrectamente. También busqué explícitamente algo como exit ().

El uso de exit () suena bien, pero leo que no volverá a la pila de llamadas para limpiar todo (si puedo encontrar esto de nuevo, publicaré el enlace). Además, de acuerdo con http://www.cplusplus.com/reference/cstdlib/exit/, esto no debería usarse si hay múltiples hilos activos. Espero crear un segundo hilo durante un tiempo breve al menos una vez, y podría producirse un error en ese hilo.

No se mencionó la excepción en algunas respuestas aquí en relación con los juegos https://gamedev.stackexchange.com/questions/103285/how-industy-games-handle-the-code-errors-and-exceptions

Se discutió excepciones de uso aquí: http://www.quora.com/Why-do-some-people-recommend-not-using-exception-handling-in-C++

Hay varias otras fuentes que he leído, pero esas fueron las más recientes que vi.

Conclusión personal:

Debido a mi experiencia limitada de trabajar con el manejo de errores y el uso de excepciones, no estoy seguro de si estoy en el camino correcto. Elegí la ruta de usar excepciones basadas en el código que publiqué anteriormente. Si acepta que debería abordar esos casos con excepciones, ¿lo estoy usando correctamente?


No hay nada de malo en detectar errores irrecuperables y cerrar su programa de esta manera. De hecho, es cómo deberían usarse las excepciones. Sin embargo, tenga cuidado de no cruzar la línea de usar excepciones para controlar el flujo de su programa en circunstancias normales. Siempre deben representar un error que no puede manejarse con gracia en el nivel en que ocurrió el error.

La llamada a exit() no desenrollaría la pila desde donde la llamó. Si quieres salir limpiamente, lo que ya estás haciendo es ideal.


Ya has aceptado una respuesta, pero quería agregar algo sobre esto:

¿Puedo simplemente llamar explícitamente algo como exit ()?

Puede llamar a exit, pero (probablemente) no debería.

std::exit debe reservarse para situaciones en las que desee expresar "exit now now!", no simplemente "la aplicación no tiene nada más que hacer".

Por ejemplo, si escribiera un controlador para un láser usado en tratamientos contra el cáncer, su primera prioridad en caso de que algo std::exit mal sería apagar el láser y llamar a std::exit - o posiblemente a std::terminate (para asegurar los efectos secundarios de una aplicación colgante, lenta o accidentada no matan al paciente).

Similar a cómo las excepciones no se deben usar para controlar el flujo de aplicaciones, la exit no se debe usar para detener la aplicación en condiciones normales.


¿Usar excepciones es la manera correcta de finalizar mi programa si todo lo que quiero es mostrar un mensaje de error y cerrar (teniendo en cuenta que puedo estar metido en el programa)?

Sí. Esta es la razón para usar excepciones. Se produjo un error en el fondo del código y algo en un nivel superior lo manejará. En tu caso, al más alto nivel.

Hay argumentos a favor / en contra de excepciones y códigos de error, y esta es una buena lectura:

Excepciones o códigos de error

¿Puedo simplemente llamar explícitamente algo como exit ()?

Puede, pero puede terminar duplicando su código de registro. Además, si en el futuro decide que desea manejar una excepción de manera diferente, deberá cambiar todas sus llamadas de salida. Imagine que desea un mensaje diferente o recurrir a un proceso alternativo.

Otra pregunta similar:

Corregir el uso de exit () en c ++?

También tiene un error en su enfoque ya que no maneja todas las excepciones (C ++). Quieres algo como esto:

int main() { Game game; try { game.run(); } catch (BadResolutionException & e) { Notification::showErrorMessage(e.what(), "ERROR: Resolution"); return 1; } catch (BadAssetException & e) { Notification::showErrorMessage(e.what(), "ERROR: Assets"); return 1; } catch (std::bad_alloc & e) { Notification::showErrorMessage(e.what(), "ERROR: Memory"); return 1; } catch (...) { // overload? Notification::showErrorMessage("ERROR: Unhandled"); return 1; } return 0; }

Si no manejas todas * las excepciones, puedes hacer que el juego finalice sin decirte nada útil.

No puedes manejar TODAS las excepciones. Ver este enlace:

C ++ detecta todas las excepciones


En general, se considera buena práctica permitir que todas las excepciones se propaguen a main . Esto se debe principalmente a que puede estar seguro de que la pila se desenrolla correctamente y se invocan todos los destructores (consulte esta respuesta ). También creo que está más organizado para hacer las cosas de esta manera; usted siempre sabe dónde terminará su programa (a menos que el programa falle ). También facilita informes de errores más consistentes (un punto a menudo descuidado en el manejo de excepciones; si no puede manejar la excepción, debe asegurarse de que su usuario sepa exactamente por qué). Si siempre comienzas con este diseño básico

int main(int argc, const char **argv) { try { // do stuff return EXIT_SUCCESS; } catch (...) { std::cerr << "Error: unknown exception" << std::endl; return EXIT_FAILURE; } }

entonces no te equivocarás. Puede (y debe) agregar declaraciones de catch específicas para un mejor informe de errores.

Excepciones cuando multihilo

Hay dos formas básicas de ejecutar el código de forma asíncrona en C ++ 11 utilizando las características estándar de la biblioteca: std::async y std::thread .

Primero el simple. std::async devolverá un std::future que capturará y almacenará cualquier excepción no detectada lanzada en la función dada. Llamar a std::future::get on the future hará que las excepciones se propaguen en el hilo de llamada.

auto fut = std::async(std::launch::async, [] () { throw std::runtime_error {"oh dear"}; }); fut.get(); // fine, throws exception

Por otro lado, si no se detecta una excepción en un objeto std::thread se llamará a std::terminate :

try { std::thread t {[] () { throw std::runtime_error {"oh dear"};}}; t.join(); } catch(...) { // only get here if std::thread constructor throws }

Una solución para esto podría ser pasar un std::exception_ptr al objeto std::thread que puede pasar la excepción a:

void foo(std::exception_ptr& eptr) { try { throw std::runtime_error {"oh dear"}; } catch (...) { eptr = std::current_exception(); } } void bar() { std::exception_ptr eptr {}; std::thread t {foo, std::ref(eptr)}; try { // do stuff } catch(...) { t.join(); // t may also have thrown throw; } t.join(); if (eptr) { std::rethrow_exception(eptr); } }

Aunque una mejor manera es usar std::package_task :

void foo() { throw std::runtime_error {"oh dear"}; } void bar() { std::packaged_task<void()> task {foo}; auto fut = task.get_future(); std::thread t {std::move(task)}; t.join(); auto result = fut.get(); // throws here }

Pero a menos que tenga una buena razón para usar std::thread , prefiera std::async .