2010-12-15 21 views
15

Estoy desarrollando dll componente C++ que puede ser utilizado por C o C++. las funciones DLL expuestos son los siguientes¿Puede un programa C manejar excepciones C++?

#include <tchar.h> 
#ifdef IMPORT 
#define DLL __declspec(dllimport) 
#else 
#define DLL __declspec(dllexport) 
#endif 

extern "C" { 

    DLL bool __cdecl Init(); 
    DLL bool __cdecl Foo(const TCHAR*); 
    DLL bool __cdecl Release(); 

} 

la implementación interna de estas funciones son clases de C++ que no están expuestos, Asumo utilizando este estilo el DLL se puede utilizar ya sea en ++ aplicaciones C o C. El problema es que no manejo ningún tipo de excepción de C++ (es decir, bad_alloc) y dejé esto al llamador (la capa más alta). Después de mucho debate con mis colegas, ¿debería detectar todas las excepciones y devolver el código de error o al menos falso porque en el caso de la aplicación C no puede manejar las excepciones de C++? ¿es eso cierto? y que debería hacer en general? ¿Existe una regla general para manejar las excepciones si está desarrollando un componente que será utilizado por otro sistema?

+1

No existe una excepción en C. – sje397

+0

http://www.on-time.com/ddj0011.htm – DumbCoder

+3

C++ puede registrar una C -linkage/'extern C' function como manejador de excepción para excepciones no detectadas a través de' std :: set_unexpected() '. Si se conoce el nombre de ese nombre, puede llamarlo directamente desde el C. En caso contrario, puede exponer un' extern C 'envoltorio para ello. No es que esta sea una interfaz conveniente, pero si es absolutamente inevitable que su biblioteca contamine fuera de sus propios límites ... –

Respuesta

16

C no tiene excepciones, por lo tanto, en general, debe detectar todas las excepciones y devolver un código de error y/o proporcionar una función que devuelva la información sobre el último error.

+0

"C no tiene excepciones". Por qué no; El tiempo de ejecución de C++ tiene excepciones, y la interfaz C con el tiempo de ejecución de C++ tiene el tiempo de ejecución de C++, entonces ¿por qué C no puede interceptar el tiempo de ejecución de C++ para una implementación específica de excepciones (diferentes sistemas pueden tener implementaciones diferentes, pero la implementación sigue ahí y debe existir en memoria, e incluso si no está expuesta, puede analizar la memoria para descubrirla y tomar diferentes bytes y llamar a las funciones no expuestas). – Dmitry

18

Si esto es Windows usando MSVC, entonces sí puede capturar excepciones en C, pero no puede atraparlas tan bien. Las excepciones de C++ se alimentan a través del mecanismo de gestión de excepciones estructuradas de OS ABI, y Microsoft tiene una extensión C __try, __except, __finally para manejar las excepciones estructuradas del sistema operativo. Tenga en cuenta que estos incluyen violaciones de acceso, división por cero, etc. que normalmente desea finalizar su programa y registrar un informe de error. Puede identificar las excepciones de C++ con el código 0xE04D5343 (4D 53 43 = "MSC") y arrojar el resto.

Dicho todo esto, es probable que no desee lanzar excepciones a través de un límite de DLL, y ciertamente no si solo está exponiendo una API de C.

+0

bien, pero si el cliente de la DLL es C++, ¿es buena idea propagar excepciones a través de Dll Boundary? –

+1

@Ahmed: No, a menos que desee entregar archivos DLL para cada versión del compilador. –

+0

@Ahmed: en este caso, diría que sí, pero es fácil -> si proporciona una interfaz C, no use excepciones, si proporciona una interfaz C++, puede hacerlo. –

7

Solo propague excepciones a la persona que llama si la persona que llama está diseñada para manejarlas. Supongo que en su caso se llamará terminate() inmediatamente una vez que cualquier excepción escapa del código C++ porque desde el punto de ejecución de C++ esa excepción no se ha manejado.

La misma situación se presenta en el diseño de servidores COM: los clientes pueden estar en cualquier idioma/tecnología. El rul es que ninguna excepción debería escapar a los métodos del servidor COM; todas las excepciones deben capturarse y traducirse en HRESULT y (opcionalmente) IErrorInfo. Deberías hacer lo mismo en tu situación.

En caso de que el código C esté intercalado entre dos capas del código C++ propagating exceptions to C code is still a very bad idea.

11

Como regla general, debe Nunca permitir excepciones de C++ se propaguen más allá del límite de un módulo. Esto se debe a que el estándar de C++ no especifica cómo debe implementarse la propagación de excepciones, y como tal, este es el compilador (y los indicadores del compilador) y dependiente del sistema operativo. No puede garantizar que el código que llama a su módulo se compile con el mismo compilador con los mismos indicadores de compilación que su módulo. De hecho, como demuestra con esta pregunta, no puede garantizar que el código que llama a su módulo se escriba en el mismo idioma.

Para obtener más información, consulte el artículo 62 en C++ Coding Standards por Sutter and Alexandrescu.

8

Ok, ya que se solicitó:

C++ código de ejemplo:

#include <typeinfo> 
#include <exception> 
extern "C" { 
void sethandler(void (*func)(void)) { std::set_terminate(func); } 
int throwingFunc(int arg) { 
    if (arg == 0) 
     throw std::bad_cast(); 
    return (arg - 1); 
} 
} 

C código de ejemplo:

#include <stdio.h> 

extern int throwingFunc(int arg); 
extern void sethandler(void (*func)(void)); 

void myhandler(void) 
{ 
    printf("handler called - must've been some exception ?!\n"); 
} 

int main(int argc, char **argv) 
{ 
    sethandler(myhandler); 

    printf("throwingFunc(1) == %d\n", throwingFunc(1)); 
    printf("throwingFunc(-1) == %d\n", throwingFunc(-1)); 
    printf("throwingFunc(0) == %d\n", throwingFunc(0)); 
    return 0; 
} 

Cuando puedo compilar estos dos, los unen y ejecutar este (Ubuntu 10.04, gcc 4.4.5, x64), obtengo:

$ ./xx 
throwingFunc(1) == 0 
throwingFunc(-1) == -2 
handler called - must've been some exception ?! 
Aborted 

Mientras puede detectar excepciones de C, eso no es suficiente, porque el comportamiento del tiempo de ejecución de C++ después de std::terminate() no está definido, y porque el manejador no obtiene información de estado (para distinguir tipos y/o fuentes de excepción). El controlador no puede limpiar nada.

Por cierto, he elegido deliberadamente std::bad_cast() como tipo de excepción en este ejemplo. Esto es porque throwing ilustra una diferencia de comportamiento entre std::set_unexpected() y std::set_terminate() - se llamará al controlador inesperado para todas las excepciones que no sean std::bad_*, mientras que para detectar excepciones estándar, se requiere un manejador de terminación ... ¿ves el dilema? El arnés es demasiado ancho para ser de uso práctico :(

+0

En realidad, el objetivo de hacer esto es proporcionarle al usuario un mensaje de error antes de abortar. El punto es que lo que se debe atrapar debe atraparse, y para las excepciones restantes, ser lo más descriptivo posible. –

+0

Es por eso que digo "dilema": puede detectar la excepción y registrar un error, pero ¿qué tan descriptivo es _ lo más descriptivo posible_? Por el hecho de que el controlador no se transmite en ningún contexto, yo diría "no muy" ... –

Cuestiones relacionadas