Hoy estaba pensando en los bloques try / catch existentes en otros idiomas. Busqué en Google por un tiempo, pero sin resultado. Por lo que sé, no existe el try / catch en C. Sin embargo, ¿hay alguna manera de "simularlos"?
Claro, hay aserción y otros trucos, pero nada como try / catch, que también capturan la excepción generada. Gracias
101
Respuestas:
C en sí no admite excepciones, pero puede simularlas hasta cierto punto con
setjmp
ylongjmp
llamadas.Este sitio web tiene un buen tutorial sobre cómo simular excepciones con
setjmp
ylongjmp
fuente
try{ x = 7 / 0; } catch(divideByZeroException) {print('divided by zero')};
, ¿no funcionará bien?Utiliza goto en C para situaciones similares de manejo de errores.
Ese es el equivalente más cercano de excepciones que puede obtener en C.
fuente
goto
se usa más para el manejo de errores, pero ¿y qué? La pregunta no es sobre el manejo de errores como tal, sino explícitamente sobre los equivalentes try / catch.goto
no es equivalente a fro try / catch ya que está restringido a la misma función.goto
como un mecanismo de prueba / captura utilizado en una fuente moderna, ampliamente aceptada y revisada por pares. Busquegoto
un equivalente de "lanzamiento" yfinish
un equivalente de "captura".Ok, no pude resistirme a responder a esto. Permítanme decir primero que no creo que sea una buena idea simular esto en C, ya que realmente es un concepto ajeno a C.
Podemos
utilizarabusar de las variables de pila del preprocesador y locales para dar el uso de una versión limitada de C ++ try / tiro / catch.Versión 1 (lanzamientos de alcance local)
La versión 1 es solo un lanzamiento local (no puede salir del alcance de la función). Se basa en la capacidad de C99 para declarar variables en el código (debería funcionar en C89 si el intento es lo primero en la función).
Esta función solo crea una var local para que sepa si hubo un error y usa un goto para saltar al bloque catch.
Por ejemplo:
Esto resulta en algo como:
Versión 2 (salto de alcance)
La versión 2 es mucho más compleja pero básicamente funciona de la misma manera. Utiliza un salto largo de la función actual al bloque try. El bloque try luego usa un if / else para saltar el bloque de código al bloque catch que verifica la variable local para ver si debería atrapar.
El ejemplo se expandió nuevamente:
Esto usa un puntero global para que longjmp () sepa qué intento se ejecutó por última vez. Estamos
usandoabusar de la pila para que las funciones secundarias también puedan tener un bloque try / catch.El uso de este código tiene varias desventajas (pero es un ejercicio mental divertido):
fuente
bool __ErrorCheck(bool &e){bool _e = e;e=false;return _e;}
. Pero la variable local también se redefiniría, por lo que las cosas se saldrían un poco de control.En C99,puede usarsetjmp
/longjmp
para un flujo de control no local.Dentro de un solo alcance, el patrón de codificación estructurado genérico para C en presencia de múltiples asignaciones de recursos y múltiples usos de salidas
goto
, como en este ejemplo . Esto es similar a cómo C ++ implementa llamadas de destructor de objetos automáticos bajo el capó, y si se apega a esto diligentemente, debería permitirle un cierto grado de limpieza incluso en funciones complejas.fuente
Si bien algunas de las otras respuestas han cubierto los casos simples usando
setjmp
ylongjmp
, en una aplicación real hay dos preocupaciones que realmente importan.jmp_buf
hará que estos no funcionen.jmp_buf
causará todo tipo de dolor en esta situación.La solución a estos es mantener una pila local de subprocesos de
jmp_buf
que se actualiza a medida que avanza. (Creo que esto es lo que lua usa internamente).Entonces, en lugar de esto (de la increíble respuesta de JaredPar)
Usarías algo como:
Una vez más, una versión más realista de esto incluiría alguna forma de almacenar información de error en el
exception_state
mejor manejo deMAX_EXCEPTION_DEPTH
(tal vez usando realloc para hacer crecer el búfer, o algo así).DESCARGO DE RESPONSABILIDAD: El código anterior se escribió sin ningún tipo de prueba. Es simplemente para que tengas una idea de cómo estructurar las cosas. Diferentes sistemas y diferentes compiladores necesitarán implementar el almacenamiento local de subprocesos de manera diferente. El código probablemente contiene errores de compilación y errores lógicos, por lo que, si bien puede usarlo como desee, PRUEBE antes de usarlo;)
fuente
Una búsqueda rápida en Google produce soluciones tontas como esta que usan setjmp / longjmp como otros han mencionado. Nada tan sencillo y elegante como el try / catch de C ++ / Java. Soy bastante parcial a la excepción de Ada manejándome.
Verifique todo con declaraciones if :)
fuente
Esto se puede hacer con
setjmp/longjmp
C. P99 tiene un conjunto de herramientas bastante cómodo para esto que también es consistente con el nuevo modelo de rosca de C11.fuente
Esta es otra forma de manejar errores en C que es más eficiente que usar setjmp / longjmp. Desafortunadamente, no funcionará con MSVC, pero si usar solo GCC / Clang es una opción, entonces puede considerarlo. Específicamente, utiliza la extensión "etiqueta como valor", que le permite tomar la dirección de una etiqueta, almacenarla en un valor y saltar a ella incondicionalmente. Lo presentaré usando un ejemplo:
Si lo desea, puede refactorizar el código común en define, implementando efectivamente su propio sistema de manejo de errores.
Entonces el ejemplo se convierte en
fuente
Advertencia: lo siguiente no es muy agradable, pero funciona.
Uso:
Salida:
Tenga en cuenta que esto está usando funciones anidadas y
__COUNTER__
. Estará en el lado seguro si está usando gcc.fuente
Redis usa goto para simular try / catch, en mi humilde opinión es muy limpio y elegante:
fuente
errno
solo debe usarse justo después de la llamada al sistema fallida y no tres llamadas después.En C, puede "simular" excepciones junto con la "recuperación de objetos" automática mediante el uso manual de if + goto para el manejo explícito de errores.
A menudo escribo código C como el siguiente (resumido para resaltar el manejo de errores):
Este es ANSI C completamente estándar, separa el manejo de errores de su código de línea principal, permite el desenrollado (manual) de la pila de objetos inicializados al igual que lo hace C ++, y es completamente obvio lo que está sucediendo aquí. Debido a que está probando explícitamente la falla en cada punto, hace que sea más fácil insertar registros específicos o manejo de errores en cada lugar donde puede ocurrir un error.
Si no le importa un poco de magia macro, puede hacerlo más conciso mientras hace otras cosas, como registrar errores con seguimientos de pila. Por ejemplo:
Por supuesto, esto no es tan elegante como las excepciones + destructores de C ++. Por ejemplo, anidar múltiples pilas de manejo de errores dentro de una función de esta manera no es muy limpio. En su lugar, probablemente desee dividirlos en subfunciones autónomas que manejen los errores de manera similar, inicializar + finalizar explícitamente de esta manera.
Esto también solo funciona dentro de una sola función y no seguirá saltando en la pila a menos que los llamadores de nivel superior implementen una lógica de manejo de errores explícita similar, mientras que una excepción de C ++ seguirá saltando en la pila hasta que encuentre un controlador apropiado. Tampoco le permite lanzar un tipo arbitrario, sino solo un código de error.
La codificación sistemática de esta manera (es decir, con una sola entrada y un solo punto de salida) también hace que sea muy fácil insertar lógica pre y post ("finalmente") que se ejecutará sin importar qué. Simplemente coloque su lógica "finalmente" después de la etiqueta FIN.
fuente
Si está utilizando C con Win32, puede aprovechar su Manejo de excepciones estructurado (SEH) para simular try / catch.
Si está utilizando C en plataformas que no son compatibles
setjmp()
ylongjmp()
, eche un vistazo a este Manejo de excepciones de la biblioteca pjsip, proporciona su propia implementaciónfuente
Quizás no sea un idioma importante (desafortunadamente), pero en APL, existe la operación ⎕EA (significa Execute Alternate).
Uso: 'Y' ⎕EA 'X' donde X e Y son fragmentos de código suministrados como cadenas o nombres de funciones.
Si X se encuentra con un error, se ejecutará Y (generalmente manejo de errores) en su lugar.
fuente