Наилучшая практика для реализации в Ada (2005 или 2012) эквивалента блока Java finalize

Java имеет блок finalize, который позволяет выполнять некоторые операторы после блока остается (выполняется, даже если возникает исключение). Пример:

try {
  ...
} catch (Exception e) {
  ...
} finally {
  ... // any code here
}

Ada имеет управляемые объекты, которые позволяют реализовать завершить операции но нет эквивалента блока finalize, как в java. Это полезно для ведения журнала, закрытие файлов, транзакций и т. д. (без необходимости создания определенного типа тегов для каждого возможного блок.)

  1. как бы вы реализовали такой блок finalize в Ada 2005 (сохраняя при этом читаемый код)?
  2. есть ли планы в Ada 2012, чтобы легко выполнить любой код завершения?

5 ответов


Я считаю, что этот код будет делать то, что вы просите; он успешно печатает 42 настоящее raise или return. Это реализация предложение Е. Т. Д.

протестировано с GCC 4.5.0 на Mac OS X, Darwin 10.6.0.

with Ada.Finalization;
package Finally is

   --  Calls Callee on deletion.
   type Caller (Callee : not null access procedure)
      is new Ada.Finalization.Limited_Controlled with private;

private

   type Caller (Callee : not null access procedure)
      is new Ada.Finalization.Limited_Controlled with null record;

   procedure Finalize (Object : in out Caller);

end Finally;


package body Finally is

   procedure Finalize (Object : in out Caller)
   is
   begin
      Object.Callee.all;
   end Finalize;

end Finally;


with Ada.Text_IO; use Ada.Text_IO;
with Finally;
procedure Finally_Demo is
begin

   declare

      X : Integer := 21;

      --  The cleanup procedure, to be executed when this block is left
      procedure F
      is
      begin
         Put_Line ("X is " & Integer'Image (X));
      end F;

      --  The controlled object, whose deletion will execute F
      F_Caller : Finally.Caller (F'Access);

   begin

      X := 42;

      raise Constraint_Error;

   end;

end Finally_Demo;

Как упоминает Адриен в комментарии,Finalize это больше похоже на деструктор.

чтобы получить что-то аппроксимирующее исключение/конечную последовательность, вы можете сделать что-то по этим строкам (предупреждение, не скомпилировано, просто набрано--мы будем работать над любыми ошибками вместе : -) Смотрите также раздел "исключения" из ADA RM.

with Ada.Exceptions;  use Ada.Exceptions;

procedure Do_Something is

   -- Variables and what-not...

   -- In case you have an exception and want to reraise it after you've done
   -- the 'final' processing.
   Exception_Caught : Exception_Occurrence := Null_Occurrence;

begin
   -- You can have some statements, like initializations, here that will not
   -- raise exceptions.  But you don't have to, it can all just go in the
   -- following block. However you want to do it...

   declare
      -- If you need to declare some entities local to a block, put those here.
      -- If not, just omit this declare section.  Be aware, though, that if
      -- you initialize something in here and it raises an exception, the
      -- block's exception handler will not catch it. Such an exception will
      -- propagate out of the whole procedure (unless it has an outermost
      -- exception handler) because you're _not_ in the block's scope yet.

   begin
      -- Main processing that might raise an exception

      ...

   exception
      when E : others =>
         -- Handle any exception that's raised.  If there are specific
         -- exceptions that can be raised, they should be explicitly
         -- handled prior to this catch-all 'others' one.

         -- Save the exception occurrence, i.e. make a copy of it that can
         -- be reraised in the 'Final' section if needed.  (If you want to
         -- reraise for a specific exception, do this in those handlers as
         -- well.
         Save_Occurrence(Exception_Caught, E);

   end;

   -- Final processing. Everything from here to the end of the procedure is
   -- executed regardless of whether an exception was raised in the above
   -- block.  By it including an others handler, it ensured that no exception
   -- will propagate out of this procedure without hitting this 'Final' code.

   -- If an exception was raised and needs to be propagated:
   if Exception_Caught /= Null_Occurrence then
      Reraise_Exception(Exception_Caught);
   end if;

end Do_Something;

предполагая, что вы поняли разницу между ada.finalization и finalize block В java, я бы сделал что-то подобное следующему, что должно иметь тот же эффект.

procedure x is 
begin

  -- some code
  begin
    -- more code (your try)
  exception 
    -- handle exception if necessary (caught exception)
  end;
  -- yet more code which is executed regardless of any exception handling.

end x;

Marc C имеет правильный подход для попытки эмулировать это в прямолинейном процедурном коде.

однако, IMHO эта структура в основном является способом взломать систему OO Java, для тех, кто хочет одно из структурных преимуществ OO в старомодном процедурном программировании. Даже в Java вам почти всегда лучше создавать правильный класс вместо этого.

поэтому я не думаю, что это слишком много, чтобы сказать, что правильный способ получить эту функциональность в Ada было бы сделать правильный объект и сделать ваш объект ребенком Ada.Finalization.Controlled.

Если вы не хотите беспокоиться о создании фактического объекта, вы можете просто создать фиктивный, поместить в него свой код завершения и объявить его в стеке в верхней части блока, для которого вы хотите его запустить. Недостатком этого является то, что сами управляемые типы (по крайней мере, в последний раз, когда я их использовал) должны быть объявлены на уровне пакета. Когда это так, вы не сможете поместить прямые ссылки на ниже-объявленные в них объекты. Они утверждали, что собираются исправить это в будущем языковом пересмотре, но я не пробовал это в последнее время, чтобы увидеть, если они это сделали.


просто подумал о другом ответе. Это немного тяжело (и, возможно, больше проблем, чем стоит). Но это даст вам что-то, что немного похоже на ваш старый блок finalize

идея заключалась бы в том, чтобы поместить ваш "завершаемый" код в задачу. Вы не можете оставить область, в которой объявлена задача, до тех пор, пока задача не завершится. Таким образом, вы можете поместить свой рабочий код в задачу и свой "окончательный" код вне области, в которой определена задача. Родительская задача будет сидеть и ждать рабочая задача заканчивается (так или иначе), а затем она будет запускать код "finally" независимо от того, как она закончилась. Недостатком является то, что если задача создает исключение, она останавливается на задаче. Таким образом, вы все еще не совсем понимаете поведение, которое вы можете создать исключение, и оно будет распространяться автоматически во время запуска кода "finalize". Возможно, вы могли бы вернуть это поведение, добавив рандеву и вторую задачу (это проблема с задачами. Они как картофельные чипсы...он всегда нужен. более.)

procedure Finalized is
begin
    declare
        task Worker is end Worker;
        task body Worker is begin
            --// Working code in here. Can throw exceptions or whatever. 
            --// Does not matter.
        end Worker;
    begin
    end;

    --// If we get here, we know the task finished somehow (for good or ill)
    --// so the finalization code goes here.

end Finalized;

Мне кажется, что может быть способ сделать что-то подобное с защищенными объектами. Я оставлю это на усмотрение других.