¿Hay casos del mundo real para C ++ sin excepciones? [cerrado]

40

¿En Cuándo usar C sobre C ++ y C ++ sobre C? hay una declaración wrt. para codificar excepciones de tamaño / C ++:

Jerry responde (entre otros puntos):

(...) tiende a ser más difícil producir ejecutables realmente pequeños con C ++. Para sistemas realmente pequeños, rara vez escribes mucho código de todos modos, y el extra (...)

a lo que pregunté por qué sería eso, a lo que Jerry respondió:

Lo principal es que C ++ incluye manejo de excepciones, que (al menos generalmente) agrega un mínimo al tamaño ejecutable. La mayoría de los compiladores le permitirán deshabilitar el manejo de excepciones, pero cuando lo haga, el resultado ya no será C ++. (...)

que realmente no dudo a nivel técnico del mundo real.


Por lo tanto, estoy interesado (por pura curiosidad) en escuchar ejemplos del mundo real en los que un proyecto eligió C ++ como lenguaje y luego eligió desactivar las excepciones. (No solo las excepciones de "no usar" en el código de usuario, sino deshabilitarlas en el compilador, para que no pueda lanzar o atrapar excepciones). ¿Por qué un proyecto elige hacerlo? (Todavía usando C ++ y no C, pero excepciones): ¿cuáles son / fueron los motivos (técnicos)?


Anexo: Para aquellos que deseen elaborar sus respuestas, sería bueno detallar cómo se manejan las implicaciones de las no excepciones:

  • Las colecciones STL ( vector, ...) no funcionan correctamente (no se puede informar la falla de asignación)
  • new no puede tirar
  • Los constructores no pueden fallar
Martin Ba
fuente
1
JSF C ++. Los jets y las excepciones no se mezclan.
Codificador
1
Información esclarecedora sobre problemas con excepciones (y C ++ en general) 250bpm.com/blog:4
Ambroz Bizjak
1
@AmbrozBizjak - Citaré DeadMG de un comentario a la publicación a la que enlazas: cita: "Me parece que no entiendes las excepciones". Estoy de acuerdo. El autor claramente hizo un desastre de manejo de excepciones, mirando los ejemplos que da en esa publicación.
Martin Ba

Respuestas:

35

Casi todos los juegos de consola están en C ++ con la excepción desactivada, incluso hoy. De hecho, es la configuración predeterminada para los compiladores de C ++ destinados a esas consolas. A veces, no se garantiza que algunas funciones de C ++ funcionen correctamente en esos compiladores, como la herencia múltiple (por ejemplo, estoy pensando en un compilador predeterminado de consola muy conocido).


Además, otro ejemplo es el SDK de hardware Arduino que usa gcc sin excepción activada en C ++, y otras cosas como que no se proporciona STL .


Hay algunas razones técnicas, buenas o malas, lo que sea, no es mi consejo, sino razones que he escuchado:

  1. La mayoría de las consolas son sistemas realmente integrados con memoria y tiempo de procesamiento limitados. Tal vez no sea tan cierto en las futuras consolas, pero las actuales siguen siendo bastante restrictivas en comparación con una PC. Algunas consolas portátiles son mucho más difíciles de programar que cualquier teléfono inteligente, por ejemplo, el NDS. La función de excepción agrega memoria y un poco de costo de velocidad, incluso si no la usa. Puede comprobarlo usted mismo, es cierto incluso en la PC.
  2. Los videojuegos en la consola no pueden fallar. Tienen que ser probados de manera que eviten cualquier choque o cualquier callejón sin salida, cualquier espectáculo. Es por eso que los fabricantes de consolas solicitan que se revisen los juegos antes de su publicación. Eso también significa que la gestión de excepciones agrega un costo que no es realmente útil en el caso de juegos de consola. Es mejor en teléfonos inteligentes, por ejemplo, porque puede haber algunas formas de recuperación o agregar algún código para enviarle el problema por correo electrónico. Las plataformas cerradas como la mayoría de las consolas no permiten esto. Entonces, un sistema de excepción no es realmente necesario después de todo. "Solo tienes que hacer que funcione correctamente". ;)
  3. La administración de excepciones, cuando no permite errores y fallas, significa que debe implementar una estrategia de administración de errores. Tal sistema puede ser lo suficientemente complejo como para hacer que alguien trabaje mucho tiempo para que sea útil. Los desarrolladores de juegos no tienen lujo para trabajar en funciones que se consideran útiles en los bloqueos ...
  4. El compilador no lo permite (todavía). Si, pasa.

Creo que las excepciones pueden ser útiles incluso en los juegos, pero eso es cierto que en los juegos de consola no es realmente útil.


Actualizar:

Estoy agregando otro ejemplo sorprendente aquí: LLVM / CLang no usa excepción ni RTTI por las siguientes razones :

En un esfuerzo por reducir el código y el tamaño del ejecutable, LLVM no utiliza RTTI (por ejemplo, dynamic_cast <>) o excepciones. Estas dos características del lenguaje violan el principio general de C ++ de "solo paga por lo que usa", lo que provoca una hinchazón ejecutable incluso si nunca se usan excepciones en la base del código, o si RTTI nunca se usa para una clase. Debido a esto, los desactivamos globalmente en el código.

Dicho esto, LLVM hace un uso extensivo de una forma de RTTI enrollada a mano que usa plantillas como isa <>, cast <> y dyn_cast <>. Esta forma de RTTI es opcional y se puede agregar a cualquier clase. También es sustancialmente más eficiente que dynamic_cast <>.

CLang es bien conocido por su velocidad de compilación y errores explícitos, pero también es un compilador único que tiene un código realmente fácil de seguir.

Klaim
fuente
9
(3): necesita una estrategia de gestión de errores, pase lo que pase, si va a aceptar (2). Surgirán problemas y su software de consola debe fallar de manera suave y elegante. No es obvio para mí que evitar excepciones por completo lo haga más fácil.
David Thornley
66
Todos los excelentes ejemplos de entornos de tiempo de ejecución estrictamente controlados donde los errores se manejan correctamente y no hay eventos "excepcionales".
Patrick Hughes
1
@DavidThornley Porque supones que el sistema de la consola manejará algún tipo de error, pero no lo hará. Bueno, tal vez lo haga en PS3 o XBox, pero eso no permitirá pasar las pruebas del constructor de consolas. De todos modos, el firmware de la mayoría de las consolas avanzadas no es tan flexible como podría pensar. Un juego de consola debe tener acceso a casi todo el hardware de la consola, por lo que puede pensar en un juego que se ejecuta en una consola casi como si fuera también el "SO" de la consola ... cuanto más tengamos consolas potentes, cuanto menos es cierto. Así que estoy de acuerdo contigo en que parece extraño en algunos casos.
Klaim
2
Esta es una buena respuesta, me gustaría agregar que incluso si una excepción apareciera y presentara al usuario un error, ¿qué se supone que debe hacer al respecto con un D-pad? La única solución real para el usuario final es un reinicio.
anon
2
Eliminé el ejemplo de locura porque alguien en su equipo dijo que impulsar la NO EXCEPCIÓN no significa "sin uso de excepción" sino "sin excepción a las reglas". Ver permalink.gmane.org/gmane.comp.lib.boost.devel/231377
Klaim
9

Jerry dijo: ... el resultado ya no es C ++ , mientras que mi metáfora es que es claramente C ++, solo un dialecto ligeramente diferente porque los programas utilizan otras formas, convenciones y estilos escritos.

Aquí están mis razones principales para deshabilitarlas:

Compatibilidad binaria

Cruzar los límites del idioma y la traducción no está universalmente bien definido o indefinido. Si desea garantizar que su programa funcione dentro del dominio de comportamiento definido, deberá poner en cuarentena las excepciones en los puntos de salida del módulo.

Tamaño ejecutable

Aquí están los tamaños binarios de un programa libre de excepciones que escribí, creado sin y con excepciones habilitadas:

Sin excepciones:

  • ejecutable + dependencias: 330
  • ejecutable despojado final (versión de lanzamiento): 37

Con excepciones:

  • ejecutable + dependencias: 380
  • ejecutable despojado final (compilación de lanzamiento): 44

Recordatorio: es una colección de bibliotecas y programas que contienen cero lanzamientos / capturas. La bandera compilador hace permitir excepciones en la biblioteca estándar de C ++. Por lo tanto, el costo en el mundo real es más del 19% visto en este ejemplo.

Compilador: apple gcc4.2 + llvm. Tamaños en MB.

Velocidad

A pesar del término "excepciones de costo cero", todavía agregan algunos gastos generales incluso cuando nada arroja. En el caso anterior, es un programa crítico de rendimiento (procesamiento de señales, generación, presentación, conversiones, con grandes conjuntos de datos / señales, etc.). Las excepciones no son una característica necesaria en este diseño, mientras que el rendimiento es muy importante.

Corrección del programa

Parece una razón extraña ... Si lanzar no es una opción, debe escribir programas relativamente estrictos, correctos y bien probados para garantizar que su programa se ejecute correctamente, y que los clientes usen las interfaces correctamente (si me da un mal argumento o no lo hace) No verifique un código de error, entonces se merece UB). ¿El resultado? La calidad de la implementación mejora enormemente y los problemas se solucionan rápidamente.

Sencillez

Las implementaciones de manejo de excepciones a menudo no se mantienen actualizadas. También agregan mucha complejidad porque una implementación puede tener muchas, muchas, muchas secuencias de salida. Es más sencillo leer y mantener programas altamente complejos cuando utilizan un pequeño conjunto de estrategias de salida bien definidas, tipadas, que surgen y son manejadas por el cliente. En otros casos, las implementaciones pueden con el tiempo implementar más lanzamientos o sus dependencias pueden introducirlos. Los clientes no pueden defenderse fácil o adecuadamente contra todas estas salidas. Escribo y actualizo muchas bibliotecas, hay una evolución y una mejora frecuentes. Intentar mantener todo sincronizado con las secuencias de salida de excepción (en una base de código grande) no sería un buen uso del tiempo y probablemente agregaría mucho ruido y ruido. Debido a la mayor corrección del programa y más pruebas,

Historia / Código existente

En algunos casos, nunca se introdujeron por razones históricas. Una base de código existente no los usaba, cambiar los programas podría llevar muchos años y hacer que sea realmente feo de mantener debido a la superposición de convenciones e implementaciones.

Desventajas

Por supuesto, hay inconvenientes, los más importantes son: incompatibilidad (incluido binario) con otras bibliotecas, y el hecho de que tendrá que implementar una buena cantidad de programas para adaptarse a este modelo.

justin
fuente
+1 excelente información! (aunque no estoy de acuerdo con el párrafo Simplicity)
Martin Ba
Sería interesante si solo tiene constructores sin fallas y cómo maneja la asignación (- falla).
Martin Ba
@ Martin 1) No estar de acuerdo está muy bien. Entiendo que la mayoría de los desarrolladores no están de acuerdo con inhabilitar excepciones por múltiples razones. Por simplicidad, va junto con la corrección del programa. Los problemas / estados inválidos simplemente no pueden viajar lejos. Eso significa que verifican fallas y salen con gracia. La publicación de jheriko hace eco de esto.
justin
1
@ Martin 2b) la asignación es un poco más compleja. Primero, el número de asignaciones de almacenamiento dinámico se reduce en número y aumenta de tamaño; para empezar, hay relativamente pocas asignaciones de almacenamiento dinámico. En segundo lugar, las asignaciones pasan por asignadores personalizados. Si no se proporciona inicialmente una asignación para el asignador (por ejemplo, mallocdevoluciones 0), el asignador ingresa a uno while (1)que tiene un cambio de contexto, seguido de otro intento de asignación. En esta base de código, solía ser que nuevo a través del asignador podría devolver 0, pero la implementación más reciente ha estado funcionando bien. (cont)
justin
2
sí, hay un contenedor compatible con stl sobre las interfaces del asignador personalizado. técnicamente, el programa no abortará en el lanzamiento; introducirá cambios de contexto (por ejemplo, permitirá que funcione otro subproceso que, con suerte, liberará algo de memoria), volverá a intentarlo y registrará si eso falla, para siempre. Las pruebas unitarias pueden ejecutarse durante días sin problemas. La única amenaza real son las grandes asignaciones, muchas de las cuales se capturarán antes de solicitarlas. nuevamente, las tasas de falla del sistema también están en el radar en este punto; existe la posibilidad de que fallen antes de la implementación central en tal escenario.
justin
7

Google no aprueba las excepciones en su Guía de estilo de C ++ , principalmente por razones históricas:

A primera vista, los beneficios del uso de excepciones superan los costos, especialmente en proyectos nuevos. Sin embargo, para el código existente, la introducción de excepciones tiene implicaciones en todo el código dependiente. Si se pueden propagar excepciones más allá de un nuevo proyecto, también se vuelve problemático integrar el nuevo proyecto en el código existente sin excepciones. Debido a que la mayoría del código C ++ existente en Google no está preparado para hacer frente a las excepciones, es relativamente difícil adoptar un nuevo código que genere excepciones.

Dado que el código existente de Google no tolera las excepciones, los costos de usar excepciones son algo mayores que los costos en un nuevo proyecto. El proceso de conversión sería lento y propenso a errores. No creemos que las alternativas disponibles a las excepciones, como los códigos de error y las afirmaciones, introduzcan una carga significativa.

Nuestro consejo contra el uso de excepciones no se basa en razones filosóficas o morales, sino prácticas. Debido a que nos gustaría usar nuestros proyectos de código abierto en Google y es difícil hacerlo si esos proyectos usan excepciones, también debemos desaconsejar las excepciones en los proyectos de código abierto de Google. Las cosas probablemente serían diferentes si tuviéramos que volver a hacerlo desde cero.

Hay una excepción a esta regla (sin juego de palabras) para el código de Windows.

(Énfasis del editor)

Súper eléctrico
fuente
5

Qt casi nunca usa excepciones. Los errores en Qt se indican con códigos y señales de error. La razón declarada oficialmente es:

Cuando se inició Qt, las excepciones no estaban disponibles para todos los compiladores que debían ser compatibles con Qt. Hoy estamos tratando de mantener las API consistentes, por lo que los módulos que tienen un historial de no usar excepciones generalmente no obtendrán código nuevo usando excepciones agregadas.

¿Por qué QT usa tan pocas excepciones?

Hoy, una crítica común de Qt es que su seguridad de excepción no está completa.

usuario16764
fuente
1
Gracias. Buena información, aunque me pregunto si la mayoría de las bases de códigos QT probablemente tienen excepciones habilitadas en el compilador ...
Martin Ba
4

Symbian C ++ (usado en algunos teléfonos móviles Nokia) no usa excepciones, al menos no directamente, porque los compiladores de C ++ no las implementaron de manera confiable cuando Symbian se desarrolló por primera vez.

Keith Thompson
fuente
1
Esto no es válido para las versiones modernas de Symbian. Las trampas y hojas de Symbian se implementan como excepciones reales de C ++ , pero EPOC32 y versiones anteriores de Symbian se basaron en otros medios (setjmp / longjmp si recuerdo correctamente).
otto
1
@ OttoHarju: Eso es lo que quise decir con "al menos no directamente".
Keith Thompson
3

Yo / nunca / uso excepciones. Hay varias razones para esto, pero las dos razones principales son que nunca las he necesitado para producir código robusto y que reducen el rendimiento en tiempo de ejecución.

He trabajado en el código de producción que usa y deshabilita las excepciones: el código que permitía las excepciones era uniformemente peor. En algunos lugares, se utilizaron excepciones para el control de flujo genuino en lugar del manejo de errores, que es muy pesado, anti-rendimiento y difícil de depurar. En general, los problemas de depuración en el código lleno de excepciones fueron más difíciles que en el código libre de excepciones, en parte debido a la pila y las dificultades intrínsecas del mecanismo de excepción, pero mucho más que eso fue debido al código perezoso que se fomentó como resultado de tener disponible un manejo de excepciones.

No hay nada realmente malo con las excepciones en sí mismas / si no le importa el rendimiento / y no tiene tiempo para hacer algo correctamente: son una característica del lenguaje para el manejo de errores y un buen sustituto para un mecanismo adecuado de manejo de errores. Sin embargo, casi siempre hay / mejores / formas de manejar los errores, como con su propia lógica (es difícil de explicar, es casi de sentido común). Si no es su error, pero proviene de una biblioteca (por ejemplo, la biblioteca estándar), entonces debe respetar la elección de excepciones o tener algún bloqueo, pero siempre cuestionaría esa elección. Nunca he visto una situación en la que las excepciones fueran realmente la mejor solución.

Las afirmaciones son más depurables, los códigos de error son menos pesados ​​... entre los dos, si se usan correctamente, es más fácil leer, depurar y mantener el código, que es más rápido. Sus victorias en todo ...

jheriko
fuente
1
No Es más fácil hacer un mal uso de las excepciones, pero aparte de eso, las excepciones funcionan bien. Es al menos tan fácil de razonar, siempre y cuando todas sus funciones sean seguras (y eso generalmente es solo una cuestión de usar RTTI para todos los recursos, lo que debería hacer de todos modos). Hacer los códigos de error correctamente puede ser extremadamente tedioso y puede resultar en enterrar su programa en una pila de códigos de manejo de errores; en ese caso, las excepciones son mejores. Recuerde, que yo haga las cosas de manera diferente a usted no es evidencia concluyente de que no me importa hacer las cosas bien.
David Thornley
... en cuanto a por qué RTTI es malo, esa es otra historia: el RTTI incorporado en cada compilador que he visto es trivialmente superable, incluso si necesita RTTI. Sin embargo, todo se trata de contexto: estas cosas como RTTI y excepciones pueden ser excelentes para RAD y aplicaciones empresariales, no tienen lugar en el mundo de la informática de alto rendimiento (por ejemplo, juegos). Nunca he visto un motor de juego, ya sea en la producción utilizando cualquier objetivo en el que podrían estar apagados ...
jheriko
2
Lo siento, RAII es lo que quise decir. Si no está usando eso, entonces no solo las excepciones se arruinarán. (Sin embargo, los moldes dinámicos funcionan muy bien en mi trabajo, y tengo que preocuparme por el rendimiento).
David Thornley
Tema interesante: creo que las excepciones permiten una codificación de manejo de errores más concisa, pero los códigos de error son más robustos porque permiten manejar los errores localmente (puede verificar el código de retorno inmediatamente en la persona que llama, mientras que la excepción puede llenar la pila de llamadas antes de que sean atrapados y a veces no son atrapados, lo que resulta en un bloqueo. A menos que use catch (...)).
Giorgio
3

En el estándar de codificación Joint Strike Fighter C ++ de Bjarne et. al., las excepciones están prohibidas debido a los duros requisitos en tiempo real de los aviones de combate.

JSF ++ es para aplicaciones en tiempo real y críticas para la seguridad (software de control de vuelo). Si un cálculo lleva demasiado tiempo, alguien puede morir. Por esa razón, tenemos que garantizar tiempos de respuesta, y no podemos, con el nivel actual de soporte de herramientas, hacerlo por excepciones. En ese contexto, ¡incluso la asignación gratuita de tiendas está prohibida! En realidad, las recomendaciones de JSF ++ para el manejo de errores simulan el uso de excepciones antes del día en que tenemos las herramientas para hacer las cosas bien, es decir, usar excepciones.

Citado de las preguntas frecuentes de C ++ de Bjarne .

Recuerde, C ++ probablemente ejecuta la más amplia variedad de software de todos los lenguajes ...

Macke
fuente
JSF ++ también prohíbe el uso de "nuevo" (que no sea ubicación nueva) y "eliminar". Que es una respuesta al "¿cómo manejar la nueva resistencia, sin excepciones" poco de la cuestión ...
armb
@armb: Sí Consulte "En ese contexto, ¡incluso la asignación gratuita de tiendas está prohibida!" encima.
Macke
2

Es algo importante en el diseño de la biblioteca C ++. Muchas veces con una interfaz C es un poco desagradable lanzar una excepción de su biblioteca de terceros al cliente. Señale que si su biblioteca arroja, está eliminando un conjunto de clientes que lo habrían usado, dado que tenía una garantía de no tirar (por cualquier razón que el cliente tenga como restricción de excepciones).

Personalmente, he visto que se abusan de las excepciones cuando se le dice al equipo que "agregue una excepción" cuando sucede algo no tan terrible. Por supuesto, verá el error aquí: la excepción se arrojó en el código antes de que alguien descubriera qué hacer con él. Ese proyecto tuvo algunos accidentes, ya que estos lanzamientos profundos se formaban ocasionalmente.

luego
fuente
1

Quizás a este respecto, vale la pena mencionar Embedded C ++ . Embedded C ++ es una variante de C ++ diseñada (obviamente suficiente) para sistemas embebidos. Básicamente es un subconjunto adecuado de C ++, con (entre otras cosas) plantillas, espacios de nombres y manejo de excepciones eliminado.

Debo agregar que si bien EC ++ causó un pequeño revuelo cuando era nuevo, parece que en su mayoría se han quedado bastante silenciosos. No estoy seguro de si las personas han perdido interés, o si su primer intento fue simplemente tan perfecto que nadie ha visto ninguna razón para meterse con eso durante una década más o menos.<closed captioning for the humor impaired>Yeah, right!</closed captioning>

Jerry Coffin
fuente
Buscando un Q&A - caravan.net/ec2plus/question.html - Diría que está casi muerto.
Martin Ba
1

Un buen ejemplo es la programación en modo kernel.

Puede usar C ++ allí para obtener un código más limpio, pero sin excepciones. No hay una biblioteca de tiempo de ejecución para ellos y el manejo de excepciones utiliza demasiada memoria de pila que es muy limitada en el núcleo (Experimenté con excepciones de C ++ en el núcleo de Windows NT y el lanzamiento y desenrollamiento de excepciones consume la mitad de la pila disponible, muy fácil de obtener desbordamiento de pila y bloquear todo el sistema).

Por lo general, define sus propios newy deleteoperadores. También placement newencontré referencias de valor de a y c ++ 11 bastante útiles . No se pueden utilizar STL u otras bibliotecas de modo de usuario de C ++ que se basan en excepciones.

Sergio
fuente
0

Cuando intenta mantener baja la memoria ejecutable. Normalmente se realiza en sistemas integrados (o móviles). Para las aplicaciones de escritorio nunca es necesario, sin embargo, en los servidores, se puede considerar si desea la mayor cantidad de memoria RAM posible para el servidor web o para sql.


fuente