Try Catch en C

Muhammad Husnain 12 octubre 2023
  1. Try-Catch en C
  2. Añadir Finally a Try-Catch en C
Try Catch en C

Los mecanismos Try-Catch son comunes en muchos lenguajes de programación como Python, C++ y JavaScript. La estructura general se encuentra a continuación.

try {
  /*
  Insert some lines of code that will probably give you errors
  */
} catch {
  /*
  Write some code to handle the errors you're getting.
  */
}

Le permiten escribir código sin tener que probar cada declaración. Si el programa que se ejecuta en el bloque try llega a una excepción, la excepción se pasa al bloque catch.

Si la excepción coincide con algún tipo de excepción, se ejecuta el código dentro del bloque catch. De lo contrario, la excepción se devuelve al bloque try.

Try-Catch en C

C no admite el manejo de excepciones. Al menos, no tiene ningún mecanismo incorporado para ello.

Esta guía demostrará una posible solución para proporcionar la funcionalidad try-catch en C. Debe tenerse en cuenta que la solución no es necesariamente completa.

Los sistemas de manejo de excepciones no son completos ni seguros sin un mecanismo para liberar memoria cuando se ha atravesado la pila y C no tiene un recolector de basura. Probablemente también necesitaríamos incluir administradores de contexto para liberar memoria.

Esta solución no pretende proporcionar un mecanismo completo y extenso de try-catch. Este concepto puede usarse técnicamente para manejar algunas excepciones.

Construiremos la solución de forma incremental, realizando actualizaciones en el código. Usaremos dos funciones proporcionadas por C, longjmp y setjmp, que se pueden obtener del archivo de cabecera setjmp.h.

Echaremos un vistazo más de cerca a las definiciones de ambas funciones.

int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);

setjmp toma una variable de tipo jmp_buf. Cuando esta función se llama directamente, devuelve 0.

longjmp toma dos variables, y cuando longjmp se invoca con la misma variable jmp_buf, la función setjmp regresa con el mismo valor que el segundo argumento de longjmp (val).

La variable env aquí es esencialmente el entorno de llamada, que representa el estado de los registros y la posición en el código cuando se realiza la llamada a la función. Cuando se llama a longjmp, el estado en el entorno de llamada se copia al procesador y se devuelve el valor almacenado en el argumento val de longjmp.

Para un bloque Try-Catch simple, la idea es asignar la declaración Try a la declaración if y la declaración Catch será entonces el else para el condicional. Aquí es donde podemos hacer un uso inteligente del hecho de que setjmp puede devolver diferentes valores.

Si la función devuelve 0, entonces sabemos que la única pieza de código que se ejecutó fue el código en nuestro bloque TRY. Si la función devuelve algo más, debemos ingresar a nuestro bloque CATCH con el mismo estado que cuando comenzamos.

Podemos llamar a la función longjmp cuando lanzamos una excepción.

Como verá en el código a continuación, también debemos cerrar el bloque TRY. Creamos una función ENDTRY que proporciona la parte de cierre del bloque do-while.

Esto también nos ayuda a crear múltiples declaraciones TRY dentro del mismo bloque. Cabe señalar que no se pueden anidar, ya que estaremos reutilizando la variable buf_state.

A continuación se muestra un ejemplo de esta implementación.

#include <setjmp.h>
#include <stdio.h>

#define TRY            \
  do {                 \
    jmp_buf buf_state; \
    if (!setjmp(buf_state)) {
#define CATCH \
  }           \
  else {
#define ENDTRY \
  }            \
  }            \
  while (0)
#define THROW longjmp(buf_state, 1)

int main() {
  TRY {
    printf("Testing Try statement \n");
    THROW;
    printf(
        "Statement should not appear, as the THROW block has already thrown "
        "the exception \n");
  }
  CATCH { printf("Got Exception \n"); }
  ENDTRY;

  return 0;
}

Producción :

Testing Try statement
Got Exception

Para los sistemas prácticos, esto no es suficiente. Necesitamos tener diferentes tipos de excepciones.

El ejemplo anterior solo admite un tipo de excepción. Una vez más, podemos usar los diferentes valores de retorno de setjmp.

En lugar de usar if-else, cambiaremos esto con un switch-case.

El diseño es el siguiente: la sentencia TRY utilizará una sentencia switch, y CATCH será una macro con un parámetro que representa el tipo de excepción. Una condición de cada instrucción CATCH será que debe cerrar el case anterior mediante un break.

#include <setjmp.h>
#include <stdio.h>

#define TRY                      \
  do {                           \
    jmp_buf buf_state;           \
    switch (setjmp(buf_state)) { \
      case 0:
#define CATCH(x) \
  break;         \
  case x:
#define ENDTRY \
  }            \
  }            \
  while (0)
#define THROW(x) longjmp(buf_state, x)

#define EXCEPTION1 (1)
#define EXCEPTION2 (2)
#define EXCEPTION3 (3)

int main() {
  TRY {
    printf("Inside Try statement \n");
    THROW(EXCEPTION2);
    printf("This does not appear as exception has already been called \n");
  }
  CATCH(EXCEPTION1) { printf("Exception 1 called \n"); }
  CATCH(EXCEPTION2) { printf("Exception 2 called \n"); }
  CATCH(EXCEPTION3) { printf("Exception 3 called \n"); }
  ENDTRY;

  return 0;
}

Producción :

Inside Try statement
Exception 2 called

Añadir Finally a Try-Catch en C

Necesitamos agregar un bloque FINALLY para una implementación funcional completa de Try-Catch. El bloque finally generalmente se ejecuta después de que se completan los bloques try y catch.

Se ejecuta independientemente de si se lanza o no una excepción.

¿Cómo implementaríamos esto? La idea clave es utilizar el caso default del caso switch para implementar el bloque FINALLY.

Sin embargo, el switch-case no ejecutaría el caso default si se ha invocado una excepción en un caso normal.

Usaremos un mecanismo similar al Dispositivo de Duff. Esencialmente estaremos entrelazando una sentencia switch-case con una sentencia do-while.

La lógica para esto es algo como esto.

switch (an expression) {
  case 0:
    while (1) {
      // code for case 0
      break;
      case 1:
        // code for case 1
        break;
    }
  default:
    // code for default case
}

Usamos una de las características más controvertidas de C: los interruptores no se rompen automáticamente antes de cada etiqueta de caja. Aquí, la instrucción while está anidada dentro del switch-case cuando se llama a un break; sale del bucle while y continúa recorriendo los casos.

En el contexto de nuestro código (como se muestra a continuación), los casos switch son todas nuestras excepciones, y el caso default está en nuestro bloque FINALLY. Naturalmente, caerá en el caso default, ya que el código de excepción ya se habría ejecutado.

Esto se muestra en el siguiente código.

#include <setjmp.h>
#include <stdio.h>

#define TRY                      \
  do {                           \
    jmp_buf buf_state;           \
    switch (setjmp(buf_state)) { \
      case 0:                    \
        while (1) {
#define CATCH(x) \
  break;         \
  case x:
#define ENDTRY \
  }            \
  }            \
  while (0)
#define THROW(x) longjmp(buf_state, x)
#define FINALLY \
  break;        \
  }             \
  default:
#define EXCEPTION1 (1)
#define EXCEPTION2 (2)
#define EXCEPTION3 (3)

int main() {
  TRY {
    printf("Inside Try statement \n");
    THROW(EXCEPTION2);
    printf("This does not appear as exception has already been called \n");
  }
  CATCH(EXCEPTION1) { printf("Exception 1 called \n"); }
  CATCH(EXCEPTION2) { printf("Exception 2 called \n"); }
  CATCH(EXCEPTION3) { printf("Exception 3 called \n"); }
  FINALLY { printf("This will always be called! \n"); }
  ENDTRY;

  return 0;
}

Producción :

Inside Try statement
Exception 2 called
This will always be called!

Esto concluye la guía para hacer un sistema try-catch en C. Por supuesto, aquí hay posibles problemas de memoria y algunas limitaciones (como la falta de soporte para sistemas try-catch anidados), pero esto es un Implementación funcional try-catch en C.

Muhammad Husnain avatar Muhammad Husnain avatar

Husnain is a professional Software Engineer and a researcher who loves to learn, build, write, and teach. Having worked various jobs in the IT industry, he especially enjoys finding ways to express complex ideas in simple ways through his content. In his free time, Husnain unwinds by thinking about tech fiction to solve problems around him.

LinkedIn

Artículo relacionado - C Exception