2009-05-11 9 views
15

Estoy desarrollando una C api para algunas funcionalidades escritas en C++ y quiero asegurarme de que no se propaguen excepciones de ninguna de las funciones C exportadas.Reutilización de código en el manejo de excepciones

La forma más sencilla de hacerlo es asegurarse de que cada función exportada está contenida en una:

try { 
    // Do the actual code 
} catch (...) { 
    return ERROR_UNHANDLED_EXCEPTION; 
} 

Digamos que sé una excepción que se olvida a menudo dentro del código C++ es std :: bad_alloc y quiero tratarlo especialmente me gustaría escribir algo como esto en su lugar:

try { 
    // Run the actual code 
} catch (std::bad_alloc& e) { 
    return ERROR_BAD_ALLOC; 
} catch (...) { 
    return ERROR_UNHANDLED_EXCEPTION; 
} 

¿es posible descomponer esto de alguna forma inteligente para que en todo el mundo pueden tratar algunos errores de forma diferente y sin la adición de una nueva declaración de capturas para el manejador de excepción en todo cada función exportada?

Soy consciente de que esto es posible de resolver con el preprocesador, pero antes de seguir por ese camino, me aseguraré de que no haya otra forma de hacerlo.

Respuesta

27

Usted puede usar sólo una función de controlador para todas las excepciones posibles, y lo llaman entre sí o sus funciones de implementación de la API, como a continuación:

int HandleException() 
{ 
    try 
    { 
     throw; 
    } 

    // TODO: add more types of exceptions 

    catch(std::bad_alloc &) 
    { 
     return ERROR_BAD_ALLOC; 
    } 
    catch(...) 
    { 
     return ERROR_UNHANDLED_EXCEPTION; 
    } 
} 

Y en cada función exportada:

try 
{ 
    ... 
} 
catch(...) 
{ 
    return HandleException(); 
} 
+1

+1: Buena idea :-) –

+0

Funcionó perfectamente. ¡Gracias! – Laserallan

+2

En código real, no olvide capturar excepciones por referencia: catch (std :: bad_alloc &) – Jem

0

Nunca use catch(...), a menos que planee volver a tirar más o menos de inmediato. Seguramente perderá cualquier información de error que pueda haber tenido para ayudarlo a descubrir la causa del error.

Me gusta un poco mejor tu segundo esquema: conocer un conjunto conocido de excepciones, idealmente porque son las únicas que arrojará tu código, y dejar pasar el resto, permitiendo que la aplicación se cuelgue es posiblemente lo mejor que puedes hacer ya que ha invocado un comportamiento desconocido, es mejor "chocar de forma responsable".

+4

En este caso, creo que el uso de la captura (...) es lo correcto.En C++ es difícil, si no imposible, predecir todas las excepciones que pueden arrojarse, pero debe evitar que se propaguen a su código C. –

+0

Si hace esto, piense en ingresar un infierno de malos informes de errores con poca o ninguna información sobre qué causó el problema y no hay manera de obtener más –

+0

. Por lo tanto, preferiría que el programa se bloquee porque alguna biblioteca oscura de la que depende a las dos elimina decide si va a arrojar MyWeirdError? Creo que prefiero ver "Problema no identificado con la operación XXX" en el registro. –

1

¿Qué hay de:

try{ 
    //Your code here 
} catch(std::exception e) 
{ 
    return translateExceptionToErrorCode(e); 
} catch(...) 
{ 
    return UNKNOWN_EXCEPTION_THROWN; 
} 
+2

Normalmente, podría capturar una excepción por referencia, para evitar una copia innecesaria –

+3

Sin mencionar el corte. –

+0

Muy buenos puntos. Tratar mi código como pseudo;) – PaulJWilliams

0

Sería una pena perder información de error en el idioma bo Undary. Deberías intentar traducir todas las excepciones a un código de error utilizable desde C.

Cómo lo haces realmente depende de cómo se vean tus clases de excepción. Si controla su jerarquía de clases de excepción, puede asegurarse de que cada clase proporcione una traducción utilizando un método virtual. Si no es así, puede que aún le resulte práctico utilizar una función de traductor y probar los tipos de la excepción 'std :: exception' que recibe para traducirlo en un código de error, al igual que sugirió Jem (recuerde: las excepciones arrojadas lo dañarán). rendimiento de todos modos, así que no te preocupes porque la traducción sea lenta).

+3

¡Traducir las excepciones a los códigos de error parece ser exactamente lo que está haciendo! –

1

Jem respuesta es un poco más simple que esta solución. Pero es posible sustituir el uso de una macro de preprocesador con el uso de plantillas. Algo como esto (más refinamientos que podría hacer):

template <class T, void (T::*FUNC)()> 
class CatchWrapper 
{ 
public: 

    static void WrapCall(T* instance) 
    { 
     try 
     { 
      (instance->*FUNC)(); 
     } 
     catch (std::bad_alloc&) 
     { 
      // Do Something 1 
     } 
     catch (std::exception& e) 
     { 
      // Do Something 2 
     } 
     catch (...) 
     { 
      // Do Something 3 
     } 
    } 
}; 


class Foo 
{ 
public: 
    void SomeCall() 
    { 
     std::cout << "Do Something" << std::endl; 
    } 
}; 


int main(int argc, char* argv[]) 
{ 
    Foo i; 
    CatchWrapper<Foo, &Foo::SomeCall>::WrapCall(&i); 
    return 0; 
} 
+0

eso es lo que he pensado primero también (y en realidad lo que he usado en algunos de mis proyectos). Pero el perfil de Jem es más limpio desde mi punto de vista. Si bien no me molesta, a veces el uso de plantillas molesta a otros programadores ..:/ –

4

Ya existe una buena respuesta. Pero solo para su información, se llama modismo 'excepción-despachador', vea C++ FAQ 17.15. IMHO 17.16 es también una lectura importante.

+0

Gracias, esto fue una gran lectura. – Laserallan

+0

Ahora es [17.15] [http://www.parashift.com/c%2B%2B-faq-lite/throw-without-an-object.html] – baruch

+0

@baruch: Gracias, corregido a la nueva url. – Abhay