2012-06-08 9 views
14

Estoy usando libjpeg ahora mismo para guardar imágenes JPEG. Si hay un error, el comportamiento predeterminado de libjpeg es llamar al exit(), lo cual quiero evitar ya que no es un error fatal para mi programa. libjpeg allows you to use your own error manager, y exige que si utiliza su propia función error_exit() (que llama a exit() de forma predeterminada) debe no devolver el control a la persona que llama. libjpeg sugiere usar setjmp.h para cumplir este requisito y no exit() el programa.Lanzar una excepción en C++ en una devolución de llamada en C, posiblemente cruzar el límite dinámico de la biblioteca ... ¿es seguro?

Sin embargo, estoy escribiendo un programa en C++, y tengo acceso a excepciones. This question's answer indica que es seguro (como en un comportamiento bien definido) lanzar una excepción desde la devolución de llamada. Pero no menciona las bibliotecas dinámicas, y hay una regla general que no arroja excepciones a través de los límites dinámicos de la biblioteca.

He aquí un ejemplo:

#include <iostream> 
#include <jpeglib.h> 
#include <cstdio> 
#include <stdexcept> 

static void handleLibJpegFatalError(j_common_ptr cinfo) 
{ 
    (*cinfo->err->output_message)(cinfo); 
    throw std::runtime_error("error in libjpeg, check stderr"); 
} 

int main() 
{ 
    struct jpeg_compress_struct cinfo; 
    struct jpeg_error_mgr jerr; 
    FILE* file = std::fopen("out.jpeg", "wb"); // assume this doesn't fail for this example 

    try 
    { 
     cinfo.err = jpeg_std_error(&jerr); 
     jerr.error_exit = handleLibJpegFatalError; 

     // let's say this triggers a fatal error in libjpeg and handleLibJpegFatalError() is called 
     // by libjpeg 
     jpeg_create_compress(&cinfo); 
    } 
    catch (...) 
    { 
     std::cerr << "Error saving the JPEG!\n"; 
    } 

    jpeg_destroy_compress(&cinfo); 
    std::fclose(file); 
} 

Lo que me gustaría saber es: ¿puedo lanzar una excepción de esta devolución de llamada, y ponerse de nuevo en mi solicitud, incluso si libjpeg se compila como una biblioteca dinámica ? libjpeg puede ser una biblioteca estática o dinámica, y si se trata de una biblioteca dinámica, posiblemente se pueda compilar con un compilador diferente. Sin embargo, el código que arroja y atrapa la excepción estará ciertamente en la misma unidad de compilación. ¿Es seguro el código anterior?

FYI, estoy desarrollando para OS X y Windows (y teniendo en cuenta el futuro de una posibilidad de Linux), por lo que estoy más interesado en si esto se sabe que es un comportamiento bien definido en general, y no para una plataforma/compilador específico.

+0

Eso es perfectamente seguro. ¿Por qué no sería? Las llamadas a bibliotecas compartidas aún usan la misma pila de llamadas. –

+1

Esto podría estar relacionado: http: // stackoverflow.com/questions/10318363/is-it-safe-for-xs-error-handler-to-throw-exceptions – Pubby

+1

@nw: No puedo pensar en por qué no sería guardar; Solo quiero asegurarme de que nada se arruine al desenrollar la pila. Puede ser perfectamente seguro, pero me han mordido en el trasero al asumir cosas del pasado, así que estoy jugando seguro aquí y comprobando dos veces. – Cornstalks

Respuesta

4

No es seguro. Dependiendo de cómo se haya compilado el código de biblioteca relevante que no sea de C++, es posible que no existan las tablas de desenrollado necesarias. Esto es solo un práctico por lo que podría fallar; la razón conceptual es que es simplemente un comportamiento indefinido.

Debe seguir la documentación y el uso de setjmp/longjmp para conseguir las afueras de la llamada a libjpeg código, entonces lanzar una excepción inmediatamente en el cuerpo if (setjmp(...)) { ... } si desea utilizar excepciones.

+0

¿Estás hablando de desenrollar los marcos dentro de la cadena de llamadas de la biblioteca? Esos no se desenrollarán con 'setjmp' tampoco. El código C tendría que sobrescribir específicamente el marco SEH/.eh_frame/etc para estropear el desenrollado de tus llamadas C++, lo que no sucedería a menos que alguien intencionalmente intentara desordenar las cosas. –

+0

No es seguro lanzar una excepción en una devolución de llamada a menos que también se maneje en la devolución de llamada. En cuanto a dónde/cómo puede fallar, si el código de la biblioteca C no tiene información de desenrollado (no '.eh_frame') y no utiliza punteros de cuadro, no hay forma de retroceder más allá del código C y dar sentido a el marco de llamada anterior donde se supone que se debe manejar la excepción. –

4

La otra respuesta se aplica aquí. Nada se arruinará al desenrollar la pila. Ni siquiera importa si la biblioteca usa algún tipo de convención loca de llamadas internamente, siempre y cuando no afecte específicamente a las estructuras de manejo de excepciones de su implementación de C++ (que no es un programa en C). Ninguna implementación de C++ que conozco encuentra el bloque catch al salir de los fotogramas de la pila (lo que haría que la optimización sea una pesadilla), todos mantienen estructuras internas para el manejo de excepciones. Siempre que una llamada más abajo en la cadena de llamadas no altere esas estructuras, el desenrollado de la pila funcionará perfectamente bien para todo su código personal. Ahora, en general, es muy posible que esto deje una biblioteca con un estado interno arruinado ya que nunca devuelve la ejecución a la biblioteca para su limpieza, pero en el caso de la devolución de llamada por error, libjpeg espera que el flujo de control no regrese y tiene presumiblemente ya limpiado después de sí mismo.

En este caso, yo iría por ello. En general, solo lanzaría excepciones fatales de una devolución de llamada C.

Espero que haya ayudado.

Cuestiones relacionadas