Programación en Ada/Excepciones

← Unidades genéricas Excepciones Unidades predefinidas/Ada.Exceptions →


En Ada, cuando se produce algún error durante la ejecución de un programa, se eleva una excepción. Dicha excepción puede provocar la terminación abrupta del programa, pero se puede controlar y realizar las acciones pertinentes. También se pueden definir nuevas excepciones que indiquen distintos tipos de error.

Excepciones predefinidas

editar

En Ada, dentro del paquete Standard, existen unas excepciones predefinidas, éstas son:

Constraint_Error
cuando se intenta violar una restricción impuesta en una declaración, tal como indexar más allá de los límites de un array o asignar a una variable un valor fuera del rango de su subtipo.
Program_Error
se produce cuando se intenta violar la estructura de control, como cuando una función termina sin devolver un valor.
Storage_Error
es elevada cuando se requiere más memoria de la disponible.
Tasking_Error
cuando hay errores en la comunicación y manejo de tareas.
Numeric_Error
en Ada 83 se podía presentar cuando ocurría un error aritmético. A partir del estándar Ada 95, desaparece por motivos de portabilidad y pasa a ser un renombrado de Constraint_Error. Por ejemplo, en Ada 83 al dividir entre cero podía saltar Constraint_Error o Numeric_Error (dependiendo del compilador). En Ada 95 este error siempre levanta Constraint_Error.
Name_Error
se produce cuando se intenta abrir un fichero que no existe.

Manejador de excepciones

editar

Cuando se espere que pueda presentarse alguna excepción en parte del código del programa, se puede escribir un manejador de excepciones en las construcciones que lo permitan (bloques o cuerpos de subprogramas, paquetes o tareas), aunque siempre está el recurso de incluir un bloque en cualquier lugar del código.

Su sintaxis sería:

manejador_excepción ::=
  when [ identificador : ] elección_excepción { | elección_excepción } =>
    secuencia_sentencias
elección_excepción ::= identificador | others

A la sentencia que comienza por when, se le denomina manejador de excepción.

La palabra reservada others indica cualquier otra excepción y debe ser la única y última opción. Por ejemplo, en un bloque:

begin
  -- ...
exception
  when Constraint_Error =>
    Put ("Error de rango.");
  when Program_Error | Tasking_Error =>
    Put ("Error de flujo.");
  when others =>
    Put ("Otro error.");
end;

En el momento en el que se produzca la elevación de Constraint_Error durante la ejecución de la secuencia de sentencias entre begin y exception, el flujo de control se interrumpe y se transfiere a la secuencia de sentencias que siguen a la palabra reservada => del manejador correspondiente.

Otro ejemplo con una función:

function Mañana (Hoy: TDía) return TDía is
begin
  return TDía'Succ(Hoy);
exception
  when Constraint_Error =>
    return TDía'First;
end Mañana;

Nótese que no se puede devolver nunca el control a la unidad donde se elevó la excepción. Cuando se termina la secuencia de sentencias del manejador, termina también la ejecución de dicha unidad.

Si no se controla una excepción, ésta se propaga dinámicamente por las sucesivas unidades invocantes hasta que se maneje en otra o directamente termina la ejecución del programa proporcionando un mensaje con la excepción provocada por pantalla.

Declaración y elevación de excepciones

editar

Normalmente, es probable prever una situación de error que no se encuentra entre las excepciones predefinidas, por ello, se puede declarar excepciones. Por ejemplo:

Error: exception;

Con lo que se puede elevar dicha excepción en el momento pertinente mediante la sentencia raise, cuya sintaxis es:

elevación_excepción ::= raise [ identificador ] ;

Por ejemplo, en un paquete de manejo de una pila estática de números enteros:

package Pila_enteros is
  ErrorPilaEnteros: exception;
  procedure Poner (X: Integer);
  function Quitar return Integer;
end Pila_enteros;

package body Pila_enteros is
  Max: constant := 100;
  Pila: array (1..Max) of Integer;
  Cima: Integer range 0..Max;

  procedure Poner (X: Integer) is
  begin
    if Cima = Max then
      raise ErrorPilaEnteros;  -- Se eleva la excepción.
    end if;
    Cima := Cima + 1;
    P(Cima) := X;
  end Poner;

 function Quitar return Integer is
 begin
   if Cima = 0 then
     raise ErrorPilaEnteros;  -- Se eleva la excepción.
   end if;
   Cima := Cima - 1;
   return Pila(Cima+1);
 end Quitar;

begin
  Cima := 0;
end Pila_enteros;

Obsérvese que no hace falta else en la sentencias if, pues al elevar la excepción, finaliza la ejecución del subprograma.

Ahora se podría escribir:

declare
  use Pila_enteros;
begin
  Poner (5);
  -- ...
exception
  when ErrorPilaEnteros =>
    -- ... Manipulación incorrecta de la pila.
  when others =>
    -- ...
end;

Si se quiere que dicha excepción no se propague más allá de la unidad en la que se elevó pero no se quiere manejar, se puede emplear una única sentencia vacía (null) dentro de su manejador correspondiente:

procedure Vaciar_pila_enteros is
  Basura: Integer;
  use Pila_enteros;
begin
  loop
    Basura := Quitar;
  end loop;
exception
  when ErrorPilaEnteros =>
    null;
end Vaciar_pila_enteros;

Aunque esto no evitaría que se terminara la ejecución de la unidad.

En el caso en el que se quiera propagar una excepción después de haber ejecutado las sentencias pertinentes, se incluiría una sentencia raise dentro del manejador:

-- ...
exception
  when ErrorPilaEnteros =>
    Put ("Pila utilizada incorrectamente.");
    Vaciar_pila_enteros;
    raise ErrorProcesamieto;  -- Se propaga otra excepción.
end;

En este caso se propaga otra excepción, pero podría haber sido la misma simplemente con raise, sin crear una nueva ocurrencia de la excepción, por ejemplo:

-- ...
exception
  when FalloEnVálvula =>
    Put ("Se ha producido un fallo en la válvula.");
    raise;  -- Se propaga la misma excepción del manejador.
end;

Así, se puede realizar un manejo de la excepción en varias capas, realizando sucesivas acciones en cada una de ellas. Dicha sentencia raise sin argumentos debe ser invocada directamente en el manejador, no es posible invocarla en un procedimiento llamado por el manejador.


Información de la excepción

editar

Ada proporciona información sobre una determinada excepción haciendo uso del paquete predefinido Ada.Exceptions y tras obtener la ocurrencia de la excepción mediante esta notación:

when Ocurrencia : ErrorSensor =>
   Put_Line (Ada.Exceptions.Exception_Information (Ocurrencia));

Manual de referencia de Ada

editar

Enlaces externos

editar
  • Excepciones: artículo de la Universidad de Valladolid. Ejemplos en Ada y Eiffel.