¿Cuáles son las ventajas y desventajas de utilizar Excepciones en C ++ en relación con el desarrollo del juego?
La guía de estilo de Google dice que no usan Excepciones por una variedad de razones. ¿Son las mismas razones pertinentes para el desarrollo del juego?
No utilizamos excepciones de C ++ ... - google-styleguide.googlecode.com
Algunas cuestiones para pensar.
- Cómo se relaciona con el desarrollo de bibliotecas utilizadas a través de múltiples proyectos.
- ¿Cómo afecta a las pruebas unitarias, las pruebas de integración, etc.?
- ¿Qué hace al usar bibliotecas de terceros?
c++
software-engineering
David Young
fuente
fuente
Respuestas:
También hay algunas consecuencias desagradables por usar excepciones en C ++ si no conoce todos los detalles de cómo funcionan. Dado que muchos juegos están escritos en C ++, esto puede ser un factor.
Por ejemplo, en C ++, lanzar una excepción en un destructor puede tener serias consecuencias. Puede causar todo tipo de comportamientos indefinidos y accidentes, ya que puede quedar atrapado en una situación en la que tiene más de una excepción activa en vuelo. (Consulte el Artículo # 8 de C ++ efectivo para obtener más información al respecto).
fuente
Joel sobre las opiniones de Software sobre Excepciones.
Vista interesante que actualmente no está en ninguna de las respuestas,
http://www.joelonsoftware.com/items/2003/10/13.html
fuente
Las excepciones no son compatibles y, por lo tanto, no se recomiendan en al menos un entorno de desarrollo de consola moderno.
La mayoría de los desarrolladores de consolas que conozco prefieren no usarlos de todos modos debido a la sobrecarga adicional en el ejecutable y la filosofía de que simplemente no son necesarios. RTTI se ve de la misma manera.
fuente
De todos los proyectos en los que trabajé en juegos, ninguno de ellos utilizó excepciones. La sobrecarga de llamadas de función es la razón principal. Como se mencionó anteriormente, como RTTI, en la mayoría de los estudios, no es un tema de discusión. No porque la mayoría de los programadores provengan de un entorno académico / Java, porque francamente, la mayoría no.
En realidad, no afecta a las pruebas unitarias, ya que solo afirma las condiciones. Para las bibliotecas, es mejor sin excepciones, ya que lo impondrá a todos los que lo usen.
Si bien hace que algunas situaciones sean un poco más difíciles de manejar, también (imo) te obliga a tener una configuración más controlada, ya que las excepciones pueden conducir al abuso. La tolerancia a errores es buena, pero no si conduce a una codificación descuidada.
Eso sí, esto proviene de alguien que nunca usó el manejo de excepciones (bueno, está bien, una o dos veces en herramientas, no lo hizo por mí, supongo que es con lo que creciste).
fuente
function();
que no sabe instantáneamente si se ignora o no un código de error. Presumiblemente, esta es la razón por la cual los lenguajes que le permiten ignorar un valor de retorno intentan forzarlo a preferir excepciones.Error
clase que es liviana y, si se ignora, se genera una aserción. Por lo tanto, no se puede ignorar más de lo que se puede ignorar una excepción.Lo más importante para mí como desarrollador de juegos profesional es que las excepciones deben ser escritas por personas que entiendan cómo funcionan las excepciones (que hay pocas en la industria de los juegos) depuradas por ellos también, y el código subyacente que las ejecuta (que es un desorden ramificado de todos modos y matará a su procesador en orden) tuvo que ser escrito por personas que proporcionan su compilador / enlazador. El problema con eso es que solo tienen una cantidad finita de recursos, por lo que se concentran en escribir mejores optimizaciones y correcciones para el código que los desarrolladores de juegos usan ... lo que generalmente no es una excepción. Si tienes un error de excepción en el hardware moderno con nuevos compiladores, ¿a quién vas a llamar? su experto en excepciones que cree que todo está bien con el código, o el proveedor que dice, sí, pronto, nosotros '
No hay nada inherentemente malo en las excepciones, solo que no son buenas porque la realidad se interpone en el camino de la teoría.
fuente
Para mí, el manejo de excepciones puede ser excelente si todo se ajusta a RAII y está reservando excepciones para rutas verdaderamente excepcionales (por ejemplo, un archivo corrupto que se está leyendo) y nunca necesita cruzar los límites del módulo y todo su equipo está a bordo con ellos. ......
EH de costo cero
La mayoría de los compiladores en la actualidad implementan un manejo de excepciones de costo cero que puede hacerlo incluso más barato que la ramificación manual en condiciones de error en las rutas de ejecución regulares, aunque a cambio hace que las rutas excepcionales sean enormemente caras (también hay algo de código hinchado, aunque probablemente no más) que si manejas a fondo todos los errores posibles manualmente Sin embargo, el hecho de que el lanzamiento sea enormemente costoso no debería importar si las excepciones se usan de la manera en que están destinadas en C ++ (para circunstancias verdaderamente excepcionales que no deberían ocurrir en condiciones normales).
Inversión de efectos secundarios
Dicho esto, hacer que todo se ajuste a RAII es mucho más fácil decirlo que hacerlo. Es fácil para los recursos locales almacenados en una función envolverlos en objetos C ++ con destructores que se limpian solos, pero no es tan fácil escribir la lógica de deshacer / deshacer para cada efecto secundario que podría ocurrir en todo el software. Solo considere lo difícil que es escribir un contenedor como el
std::vector
que es la secuencia de acceso aleatorio más simple para que sea perfectamente seguro para las excepciones. Ahora multiplique esa dificultad en todas las estructuras de datos de un software completo a gran escala.Como ejemplo básico, considere una excepción encontrada en el proceso de insertar elementos en un gráfico de escena. Para recuperarse adecuadamente de esa excepción, es posible que deba deshacer esos cambios en el gráfico de escena y restaurar el sistema a un estado como si la operación nunca hubiera ocurrido. Eso significa eliminar automáticamente a los niños insertados en el gráfico de la escena al encontrar una excepción, tal vez desde un guardia de alcance.
Hacer esto a fondo en un software que causa muchos efectos secundarios y se ocupa de una gran cantidad de estado persistente es mucho más fácil decirlo que hacerlo. A menudo creo que la solución pragmática es no molestarse con el interés de enviar las cosas. Sin embargo, con el tipo correcto de base de código que tiene algún tipo de sistema central de deshacer y quizás estructuras de datos persistentes y el número mínimo de funciones que causan efectos secundarios, es posible que pueda lograr una seguridad de excepción en todos los ámbitos ... pero es mucho lo que necesita si todo lo que va a hacer es tratar de hacer que su código sea seguro en todas partes.
Hay algo prácticamente mal allí porque conceptualmente la reversión de los efectos secundarios es un problema difícil, pero se hace más difícil en C ++. En realidad, a veces es más fácil revertir los efectos secundarios en C en respuesta a un código de error que hacerlo en respuesta a una excepción en C ++. Una de las razones es porque cualquier punto dado de cualquier función podría potencialmente
throw
en C ++. Si está tratando de escribir un contenedor genérico que funcione con tipoT
, ni siquiera puede anticipar dónde están esos puntos de salida, ya que cualquier cosa que impliqueT
podría arrojarse. Incluso comparar unoT
con otro por la igualdad podría arrojar. Su código tiene que manejar todas las posibilidades , y el número de posibilidades se multiplica exponencialmente en C ++, mientras que en C, son mucho menos numerosas.Falta de estándares
Otro problema de manejo de excepciones es la falta de estándares centrales. Hay algunos que, con suerte, todos están de acuerdo en que los destructores nunca deberían arrojar, ya que los retrocesos nunca deberían fallar, pero eso solo cubre un caso patológico que generalmente bloquearía el software si viola la regla.
Debería haber algunas normas más sensatas como nunca lanzar desde un operador de comparación (todas las funciones que son lógicamente inmutables nunca deberían lanzar), nunca lanzar desde un ctor de movimiento, etc. Tales garantías facilitarían mucho más la escritura de códigos seguros para excepciones, pero no tenemos tales garantías. Tenemos que seguir la regla de que todo podría arrojar, si no ahora, posiblemente en el futuro.
Peor aún, las personas de diferentes orígenes lingüísticos ven las excepciones de manera muy diferente. ¡En Python, en realidad tienen esta filosofía de "salto antes de mirar" que implica desencadenar excepciones en las rutas de ejecución regulares! Cuando obtenga que dichos desarrolladores escriban código C ++, podría estar viendo excepciones lanzadas de izquierda a derecha para cosas que no son realmente excepcionales, y manejarlo todo puede ser una pesadilla si está tratando de escribir código genérico, por ejemplo
Manejo de errores
El manejo de errores tiene la desventaja de que debe verificar cada posible error que pueda ocurrir. Pero tiene una ventaja: a veces, en retrospectiva, puede verificar los errores un poco tarde, como
glGetError
, para reducir significativamente los puntos de salida en una función y hacerlos explícitos. A veces puede seguir ejecutando el código un poco más antes de verificar, propagarse y recuperarse de un error, y a veces eso puede ser realmente más fácil que el manejo de excepciones (especialmente con la reversión de efectos secundarios). Pero es posible que ni siquiera te molestes tanto con los errores en un juego, tal vez solo apagues el juego con un mensaje si encuentras cosas como archivos corruptos, errores de falta de memoria, etc.Una parte desagradable de las excepciones en contextos DLL es que no puede lanzar excepciones de un módulo a otro de manera segura a menos que pueda garantizar que estén compiladas por el mismo compilador, use la misma configuración, etc. Entonces, si está escribiendo como una arquitectura de complemento destinado a personas de todo tipo de compiladores y posiblemente incluso de diferentes lenguajes como Lua, C, C #, Java, etc., a menudo las excepciones comienzan a convertirse en una molestia importante ya que hay que tragar todas las excepciones y traducirlas en error códigos de todos modos en todo el lugar.
Si son dylibs, entonces no pueden lanzar excepciones de manera segura por las razones mencionadas anteriormente. Tendrían que usar códigos de error, lo que también significa que, usando la biblioteca, tendría que verificar constantemente los códigos de error. Podrían envolver su dylib con una biblioteca de envoltura C ++ estáticamente vinculada que usted mismo construye, que traduce los códigos de error del dylib en excepciones lanzadas (básicamente arrojando desde su binario a su binario). En general, creo que la mayoría de las librerías de terceros ni siquiera deberían molestarse y limitarse a los códigos de error si usan dylibs / libs compartidas.
Por lo general, no encuentro equipos que prueben tanto las rutas de error / excepcionales de su código. Solía hacerlo y en realidad tenía un código que podía recuperarse correctamente
bad_alloc
porque quería hacer mi código ultra robusto, pero realmente no ayuda cuando soy el único que lo hace en un equipo. Al final dejé de molestarme. Me imagino un software de misión crítica que equipos completos podrían verificar para asegurarse de que su código se recupere de manera sólida de excepciones y errores en todos los casos posibles, pero eso es mucho tiempo para invertir. El tiempo tiene sentido en el software de misión crítica, pero quizás no en un juego.Amor / odio
Las excepciones también son mi tipo de característica de amor / odio de C ++, una de las características más impactantes del lenguaje que hace que RAII sea más un requisito que una conveniencia, ya que cambia la forma en que tiene que escribir el código al revés, por ejemplo , C para manejar el hecho de que cualquier línea de código dada podría ser un punto de salida implícito para una función. A veces casi deseo que el idioma no tenga excepciones. Pero hay momentos en que encuentro excepciones realmente útiles, pero son un factor demasiado dominante para mí si elijo escribir un fragmento de código en C o C ++.
Serían exponencialmente más útiles para mí si el comité estándar realmente se concentrara en establecer estándares ABI que permitieran que las excepciones se lanzaran de forma segura a través de los módulos, al menos desde el código C ++ a C ++. Sería increíble si pudieras lanzar idiomas de manera segura, aunque eso es una especie de sueño imposible. La otra cosa que haría las excepciones exponencialmente más útiles para mí es si las
noexcept
funciones realmente causaron un error del compilador si intentaron invocar cualquier funcionalidad que pudiera generar en lugar de simplemente invocarstd::terminate
una excepción. Eso es casi inútil (también podría llamarse así enicantexcept
lugar denoexcept
). Sería mucho más fácil asegurarse de que todos los destructores estén a salvo de excepciones, por ejemplo, sinoexcept
en realidad causó un error de compilación si el destructor hizo algo que podría arrojar. Si eso es demasiado costoso para determinar a fondo en tiempo de compilación, simplemente haga que lasnoexcept
las funciones (que todos los destructores deberían ser implícitamente) solo pueden llamar anoexcept
funciones / operadores, y convertirlo en un error del compilador desde cualquiera de ellos.fuente
Por lo que veo, hay dos razones principales por las que no se usa tradicionalmente en el desarrollo de juegos:
fuente