2011-01-26 5 views
6

Java имеет блок finalize, который позволяет выполнять некоторые операторы после того, как остался блок (выполняется даже при возникновении исключения). Пример:Лучшая практика для реализации в Ada (2005 или 2012), эквивалентная блоку завершения java

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

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

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

Метод 'Finalize' управляемого объекта больше похож на деструктор. он не имеет никакого сходства с блоком финализации. –

+0

Да, но вы можете «имитировать» каким-то образом такое поведение, но оно тяжелое. – ciceron

+1

Это блок 'finally', который не имеет никакого отношения к вызову методов' finalize'. – Gabe

ответ

2

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

Протестировано с GCC 4.5.0 на Mac OS X, Дарвин 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; 
+0

Это может показаться много кода, но, честно говоря, ** ** возвращает ответ на конечный вопрос о Жизни, Вселенной и обо всем. –

+0

Это 'Program_Error', чтобы поднять исключение во время' Finalize', возможно, должно быть что-то для этого. С другой стороны, это действительно ошибка программы, может быть, лучше сбой и сжигание! Интересно, что такое семантика блока Java 'finally'? –

+0

Java позволяет вам создать исключение в блоке finally. Текущее исключение (если оно есть) теряется. В нескольких случаях вы получаете другой блок try/catch, чтобы игнорировать это исключение. – ciceron

2

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

Чтобы получить что-то аппроксимирующее исключение/конечная последовательность вы можете сделать что-то вдоль этих линий (ВНИМАНИЯ, не компилируется, только что напечатал --we'll работать любые ошибки вместе :-) Смотрите также Exceptions section АДА 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; 
+0

вышеупомянутый фрагмент кода является стандартным способом обработки исключений в Ada, однако он не совсем имитирует блок «finalize»: вы не можете повторно поднимать исключение в блоке исключений или код «финализации» не будет выполняться. и если вы позаботились о выполнении кода «финализации», то вы потеряли исключение, которое было выброшено, что предотвращает ловушку этого исключения на внешнем уровне ... –

+0

Хорошая точка. Обновлен код, чтобы показать, как сохранить это исключение, и, в случае необходимости, сделать ререйз. –

+1

Также следует отметить, что оператор 'return' может выпрыгнуть прямо оттуда, не запустив код завершения. Тем не менее, я думаю, что это самое лучшее, что можно сделать таким образом. –

1

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

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

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

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

2

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

Только что видел ответ Марка Cs - похоже на мою :) – NWS

+0

Yup. Здесь также применяется тот же комментарий. Оператор return выпрыгивает прямо из 'x', не запуская ваш код« finalize », но это, вероятно, лучшее, что можно сделать, не прибегая к контролируемым типам. –

+0

Более важно, если возникает исключение, оно не будет распространено. Решение Marc справляется с этим. Предложение Саймона, вероятно, является лучшим, поскольку оно также заботится о проблеме возврата (но оно довольно тяжелое). – ciceron

1

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

Идея заключалась бы в том, чтобы поставить ваш «финализируемый» код в задаче. Вы не можете оставить область действия, о которой объявлена ​​задача, до тех пор, пока задача не завершится. Таким образом, вы можете поместить свой рабочий код в задачу и свой «окончательный» код за пределы области, в которой задана задача. Родительская задача будет сидеть там и ждать завершения рабочего задания (так или иначе), а затем он будет запускать «окончательный» код независимо от того, как он закончился. Недостатком является то, что если задача выдает исключение, она остановится на задаче. Таким образом, вы по-прежнему не совсем понимаете, какое поведение вы можете генерировать исключение, и оно будет автоматически распространяться при запуске кода «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; 

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