¿Cómo se implementa el tiempo de ejecución de control de excepciones de C ++?

Estoy intrigado por cómo funciona el mecanismo de manejo de excepciones de C ++. Específicamente, ¿dónde está almacenado el objeto de excepción y cómo se propaga a través de varios ámbitos hasta que se detecta? ¿Está almacenado en algún área global?

Dado que esto podría ser específico del comstackdor, ¿alguien podría explicar esto en el contexto del conjunto de comstackdores de g ++?

Las implementaciones pueden diferir, pero hay algunas ideas básicas que se derivan de los requisitos.

El objeto de excepción en sí mismo es un objeto creado en una función, destruido en una persona que llama. Por lo tanto, normalmente no es factible crear el objeto en la stack. Por otro lado, muchos objetos de excepción no son muy grandes. Ergo, uno puede crear, por ejemplo, un búfer de 32 bytes y desbordamiento para acumular si realmente se necesita un objeto de excepción más grande.

En cuanto a la transferencia real de control, existen dos estrategias. Una es registrar suficiente información en la stack para desenrollar la stack. Esto es básicamente una lista de destructores para ejecutar y manejadores de excepciones que pueden atrapar la excepción. Cuando ocurre una excepción, ejecuta la stack que ejecuta esos destructores hasta que encuentres una captura coincidente.

La segunda estrategia mueve esta información a tablas fuera de la stack. Ahora, cuando ocurre una excepción, la stack de llamadas se usa para descubrir qué ámbitos se ingresan pero no se salen. Luego se busca en las tablas estáticas para determinar dónde se manejará la excepción lanzada y qué destructores se ejecutarán en el medio. Esto significa que hay menos gastos generales de excepción en la stack; las direcciones de retorno son necesarias de todos modos. Las tablas son datos adicionales, pero el comstackdor puede ponerlos en un segmento cargado de demanda del progtwig.

Esto se define en 15.1 Lanzar una excepción del estándar.

El lanzamiento crea un objeto temporal.
Cómo se asigna la memoria para este objeto temporal no está especificado.

Después de la creación del objeto temporal, el control pasa al manejador más cercano en la stack de llamadas. desenrollando la stack entre el tiro y el punto de atrapada. A medida que la stack se desenrolla, las variables de la stack se destruyen en orden inverso a la creación.

A menos que se vuelva a lanzar la excepción, el temporal se destruye al final del controlador donde fue capturado.

Nota: Si captura por referencia, la referencia se referirá a la temporal. Si captura por valor, el objeto temporal se copia en el valor (y por lo tanto requiere un constructor de copia).

Consejo de S.Meyers (Catch by const reference).

 try { // do stuff } catch(MyException const& x) { } catch(std::exception const& x) { } 

Puede echar un vistazo aquí para una explicación detallada.

También puede ser útil echar un vistazo a un truco utilizado en la C simple para implementar algún tipo de manejo de excepciones básico. Esto implica el uso de setjmp () y longjmp () de la siguiente manera: el primero guarda la stack para marcar el manejador de excepciones (como “catch”), mientras que el último se usa para “lanzar” un valor. El valor “arrojado” se ve como si hubiera sido devuelto desde una función llamada. El “bloque try” finaliza cuando se llama de nuevo a setjmp () o cuando la función retorna.

Sé que esta es una vieja pregunta, pero hay una muy buena exposición, que explica los métodos utilizados en cada uno de gcc y VC aquí: http://www.hexblog.com/wp-content/uploads/2012/06/Recon- 2012-Skochinsky-Compiler-Internals.pdf