Best practice for embedding in Ada (2005 or 2012) the equivalent of a Java termination block

Java has a finalize block that allows you to execute some instructions after the block (executed even if an exception occurs). Example:

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

Ada has managed objects that allow you to implement the Finalize operation but there is no final block equivalent, as in java. This is useful for logging, closing files, transactions, etc. (without the need to create a specific tagged type for each possible block).

  • How would you implement such a finalize block in Ada 2005 (while maintaining readable code)?
  • Are there any plans in Ada 2012 that make it easy to execute any exit code?
+6
ada finalize
source share
5 answers

I believe that this code will do what you ask; it successfully prints 42 using the current raise or with return . This is an implementation of the TED clause

Tested with GCC 4.5.0 on 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
source share

As Adrien mentions in a comment, Finalize more like a destructor.

To get something closer to the excluding / ending sequence, you can do something in these lines ( WARNING, not compiling, just typed ), we will work out any errors together :-) See also the Exceptions section of 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 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 scope yet. begin -- Main processing that might raise an exception ... exception when E : others => -- Handle any exception that 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, ie 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; 
+2
source share

Assuming you understand the difference between ada.finalization and the finalize block in java, I would do something similar to the following, which should have the same effect.

 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
source share

Marc C has the right approach for trying to emulate this in straightforward procedural code.

However, IMHO, this structure is basically a way to hack the Java OO system, for those who want one of the structural advantages of OO in old-fashioned procedural programming. Even in Java, you are almost always better at creating the right class.

Therefore, I donโ€™t think it is too much to say that the right way to get this functionality in Ada would be to create a suitable object and make your object a child of Ada.Finalization.Controlled .

If you donโ€™t want to worry about creating a real object, you can simply create dummy code, put the completion code in it and declare it on the stack at the top of the block for which you want to run it. The disadvantage of this is that the controlled types themselves (at least the last time I used them) must be declared in the package level area. In this case, you will not be able to directly refer to objects with a lower ad. They said they were going to fix it in a future revision of the language, but I have not tried it lately to make sure they did.

+1
source share

Just thought of a different answer. Its a bit heavy (and maybe more problems than it costs). But this will give you something similar to your old completion block.

The idea would be to put your "finalizable" code in the task. You cannot leave the scope of the task announced until the task completes. Thus, you can put your working code in the task and your "final" code outside the scope of the task. The parent task will sit there and wait for the completion of the work task (one way or another), and then it will run the โ€œfinalโ€ code no matter how it ended. The disadvantage is that if a task throws an exception, it will stop at the task. Thus, you still do not quite understand what behavior you can throw an exception, and it will automatically propagate when you run the "finalize" code. You could return this behavior by adding a rendezvous and a second task (problem with tasks. They look like potato chips ... you always need one more).

 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; 

It seems to me, there may be a way to do something similar with protected objects. I will leave it for others to find out.

+1
source share

All Articles