Лучшие практики для реализации в Аде (2005 или 2012) эквивалента блока завершения Java - PullRequest
6 голосов
/ 26 января 2011

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

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

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

  1. Как бы вы реализовали такой блок финализации в Аде 2005 (при сохранении читабельности кода)?
  2. Есть ли в Ada 2012 планы, позволяющие легко выполнять любой код завершения?

Ответы [ 5 ]

2 голосов
/ 27 января 2011

Предполагая, что вы поняли разницу между ada.finalization и блоком finalize в 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;
2 голосов
/ 27 января 2011

Я верю, что этот код будет делать то, что вы просите; он успешно печатает 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;
2 голосов
/ 26 января 2011

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

Чтобы получить что-то, приближающее исключительную / конечную последовательность, вы можете сделать что-то вроде этого ( ПРЕДУПРЕЖДЕНИЕ, не скомпилировано, просто наберите - мы исправим все ошибки вместе :-) См. Также Раздел исключений Ада РМ.

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;
1 голос
/ 27 января 2011

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

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

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;

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

1 голос
/ 26 января 2011

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

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

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

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

...