Without copy-pasting my code here, how can I stop my ADA program from executing anymore lines of code during run-time if it calculates a certain value to 'X'?
something like:
variable_name := variable_name +4;
if variable_name >1 then
// END program here and dont execute any lines under this one
end if
I am not new to programming but new to ADA so finding the correct syntax is a pain. Any help?
There isn’t any specific syntax for this.
If you are in the main procedure, a simple return will do.
An Ada83-compatible answer is here on SO.
Both those are OK so long as you don’t have any tasks.
There’s an Ada95 Rosetta Code solution, which will work whether you have tasks or not:
with Ada.Task_Identification; use Ada.Task_Identification;
procedure Main is
-- Create as many task objects as your program needs
begin
-- whatever logic is required in your Main procedure
if some_condition then
Abort_Task (Current_Task);
end if;
end Main;
and a GNAT-specific solution, also OK with tasks:
with Ada.Text_IO; use Ada.Text_IO;
with GNAT.OS_Lib;
procedure Stopping is
procedure P is
begin
GNAT.OS_Lib.OS_Exit (0);
end P;
begin
Put_Line ("starting");
P;
Put_Line ("shouldn't have got here");
end Stopping;
if variable_name >1 then
raise PROGRAM_ERROR with "Aborted because ...";
end if;
will do what you ask. Whether that's what you want is another matter, you haven't given us enough context to guess at that.
The "abort" statement might also be usable, but its normal role is terminating tasks within a multi-tasking program.
Raising an exception is probably easiest, and if you don't like the standard ones, you can always declare your own. With an exception you can also do any tidying up (such as closing files if you need to) in your own exception handler. See the Wikibook for more details.
Related
I need some simple Ada code for test my "system". In order to test it I need some example of code that raise a storage error and a tasking error (I know normally they are avoided but I need to simulate this problems). I know that seems a basic request but I am a little new on ada coding and I find a little difficult to solve these tasks. I have tried to search on the internet for a fitted solution but all are not usable for my needs(or too specific or wrong). What I am searching is to create an .adb with a function/procedure that raises these two errors.
I hope you’ll be happy with different procedures for the two errors?
For Storage_Error,
procedure Storage is
procedure Recursive is
begin
Recursive;
end Recursive;
begin
Recursive;
end Storage;
and for Tasking_Error,
procedure Tasking is
task T is
entry E;
end T;
task body T is
begin
null;
end;
begin
delay 0.01;
T.E;
end Tasking;
This raises the exception because, when the main program calls T.E, there’s no task there at all. To explore this, replace the null; by e.g. delay 5.0; - the program waits 5 seconds before the exception terminates it.
I'm new to Ada and I'm trying to write a simple program but I have some issues I couldn't solve. So I have a task with an execute entry and a signalfound entry, the execute entry is being called first and has to do some calculations until the signalfound entry is being called by the main and then it has to stop.
But the problem is that when Signalfound entry is called it doesn't get executed because the task is stuck in the while loop. Is there an obvious solution to this problem in Ada. I tried googling the problem but without succes. Thanks in advance!
task Example body
ResultFound : Boolean := False;
--- other unimportant things
begin
loop
select
accept Execute do
while (ResultFound = False) loop
---do some calculations
end loop;
end Execute;
or
accept SignalFound do
ResultFound := True;
end SignalFound;
or
-- other unimportant accepts
end select;
end loop;
end Example;
Well, when Execute is called, your task enters the loop, which means it never executes the select statement again, so it can never accept the SignalFound call. Also, the entity calling Execute will never continue since your while loop repeats forever inside the accept statement. Usually, you want to make your critical regions for task synchronisation as small as possible, so that both tasks can carry on with their work after necessary data has been exchanged.
Mind that your code reflects a protocol by which your task operates. Currently, your code says „I will accept both Execute and SignalFound at any loop iteration“, which does not seem to match with the protocol you have in mind based on what you write.
I believe what you actually want to do is something like this:
task Example body
begin
loop
-- simply wait for someone to tell me to execute.
accept Execute;
Calculations : loop
-- check at every loop iteration whether there's a SignalFound waiting
select
accept SignalFound;
exit Calculations;
else
-- do some calculations
end select;
end loop Calculations;
-- will go to next iteration and wait for someone to call Execute again.
end loop;
end Example;
This code enforces a sequence of alternating Execute / SignalFound calls which seems to be what you have in mind.
I want to have an endless loop where the loop runs through nearly the entire program and at any point, based on a condition statement, I want it to exit that particular iteration of the loop and go back to the top and execute.
Is there a way this can be done?
It doesn't have to be an endless loop; it can be a for loop. I just want it to go to the next iteration of that loop.
You could use a goto statement
Raise an exception that you handle in the body of the loop:
procedure Main is
Try_Again: exception;
begin
while Keep_Going loop
begin
-- lots of stuff
if Give_Up_On_This_Iteration then
raise Try_Again;
end if;
-- lots more stuff
exception
when Try_Again =>
null;
end;
end loop;
end Main;
Ada doesn't have the equivalent of a continue. The simplest way to accomplish the same thing is probably just to put the rest of the loop in an if statement. So instead of something like this (here's an example where we're processing input lines and skipping those that begin with #):
while not End_Of_File (Input_File) loop
...
Read_A_Line (Input_File, Line);
if Line(Line'First) = "#" then
continue; -- ERROR--Ada doesn't have a continue statement
end if;
... process the input line
end loop;
you could say
while not End_Of_File (Input_File) loop
...
Read_A_Line (Input_File, Line);
if Line(Line'First) /= "#" then
... process the input line
end if;
end loop;
If it gets complicated, you may want to define a Boolean Need_To_Keep_Processing_Line or something. If you have several points where you need the equivalent of continue, you might end up with a multiply-nested if; if this gets too ugly, then one thing I sometimes do is multiple non-nested if's on the same Boolean:
if Need_To_Keep_Processing_Line then
...
if something then
if something-else then
Need_To_Keep_Processing_Line := false;
end if;
end if;
end if;
if Need_To_Keep_Processing_Line then -- again
...
end if;
Other alternatives have been mentioned: (1) goto; (2) use a procedure and return from various points; (3) exceptions. There are two problems with exceptions: first, this isn't really an exception, so you're using a construct for a purpose for which it wasn't intended, probably making your code less readable; and exceptions are inefficient (some compilers add extra code any time a block with an exception handler is entered; other compilers avoid this by setting up a table at compile time, but then a table search is required when an exception is raised). Using a procedure might be viable, depending on whether the loop body represents a concept that is easily named and identified. If it isn't, then extracting the loop body can hamper readability, because you're adding a procedure that doesn't really have a "procedure" purpose. The reason to avoid goto is that back in the 70's we used to use it indiscriminately, leading to a lot of tangled code that went every which way. Despite what you may have heard about it, it doesn't have to make code unreadable if used in a disciplined fashion. I generally don't use it. But I also avoid continue if programming in C-style languages, since to me it's just a glorified "goto" without the benefit of having a visible label at the end of your function to alert you to the fact that somebody is "going to" there.
But my real feeling about this is that if using if leads to ugly code, your loop body is probably too big and needs to be broken down anyway. The solution wouldn't be to revert to one of the other solutions (exceptions or goto), but rather to figure out the best way to make your loop body smaller and more readable.
To answer your question, the easiest way (syntax-wise) I am aware of is to use another loop, nested in the first one. Exiting the inner loop imitates a continue statement.
loop
loop
...
exit when Some_Condition;
...
end loop;
end loop;
That said, If you need to do something like that, something smells bad. I woulkd personally use what ajb demonstrated in the begining of his answer, which is to use if statements. If the nesting level of the if statements gets too deep, you probably doing too much in the main and need to use functions/procedures anyway.
There are some ‘cleanup’ calls that I want called when an Ada application is shutdown/killed.
For instance if I was in java, I would do something like this to achieve the effect of having something called at shutdown:
Runtime.getRuntime().addShutdownHook(new Thread(){
public void run(){
method();
}
});
Is there anything similar in Ada or another way to achieve this?
Since an Ada main program is treated as a task, you can use the Ada.Task_Termination package to manage post-execution cleanup. There's a writeup on this in the Ada 2005 Rationale, and what follows is a quick demo I put together that builds on the example.
You have to provide a library level protected termination procedure, so here's a package for that:
with Ada.Task_Termination;
with Ada.Task_Identification;
with Ada.Exceptions;
package Main_Program_Finalization is
protected Shutdown_Handler is
procedure Termination_Finalizer
(Cause : in Ada.Task_Termination.Cause_Of_Termination;
T : in Ada.Task_Identification.Task_Id;
X : in Ada.Exceptions.Exception_Occurrence);
end Shutdown_Handler;
end Main_Program_Finalization;
Body:
with Text_IO; use Text_IO;
package body Main_Program_Finalization is
protected body Shutdown_Handler is
procedure Termination_Finalizer
(Cause : in Ada.Task_Termination.Cause_Of_Termination;
T : in Ada.Task_Identification.Task_Id;
X : in Ada.Exceptions.Exception_Occurrence)
is
use Ada.Task_Termination;
use Ada.Task_Identification;
use Ada.Exceptions;
begin
New_Line;
Put_Line("Shutdown information:");
New_Line;
case Cause is
when Normal =>
Put_Line("Normal, boring termination");
when Abnormal =>
Put_Line("Something nasty happened to task ");
Put_Line(Image(T));
when Unhandled_Exception =>
Put_Line("Unhandled exception occurred in task ");
Put_Line(Image(T));
Put_Line(Exception_Information(X));
end case;
end Termination_Finalizer;
end Shutdown_Handler;
end Main_Program_Finalization;
Main program (it's set up for a normal termination as posted, uncomment the last two lines and run it to see the effect of an unhandled-exception triggered termination):
with Main_Program_Finalization;
with Ada.Task_Identification;
with Ada.Task_Termination;
with Text_IO; use Text_IO;
procedure task_term is
use Ada;
Task_ID : Task_Identification.Task_Id
:= Task_Identification.Current_Task;
begin
Put_Line("Main Task ID: " & Task_Identification.Image(Task_ID));
Put_Line("Setting termination finalizer");
Task_Termination.Set_Specific_Handler
(Task_ID,
Main_Program_Finalization.Shutdown_Handler.Termination_Finalizer'Access);
Put_Line("Go off and do things now...");
delay 1.0;
Put_Line("Done with mainline processing, the shutdown handler should now execute");
-- Put_Line("Raise an unhandled exception and see what the shutdown handler does");
-- raise Constraint_Error;
end Task_Term;
You could create a Controlled (or Limited_Controlled) object to the main procedure, which calls the necessary stuff in its Finalization method.
Be aware that you can't access any local variables of the main procedure, so put anything necessary into the controlled object.
example:
with Ada.Text_IO;
with Ada.Finalization;
procedure Main is
type Cleaner is new Ada.Finalization.Limited_Controlled with record
Some_Interesting_Data : Integer;
end record;
overriding procedure Finalize (X : in out Cleaner) is
begin
Ada.Text_IO.Put_Line ("Cleaning..." & Integer'Image (X.Some_Interesting_Data));
end Finalize;
The_Cleaner : Cleaner;
begin
Ada.Text_IO.Put_Line ("Main Procedure.");
The_Cleaner.Some_Interesting_Data := 42;
Ada.Text_IO.Put_Line ("Finished.");
end Main;
If it's a controlled shutdown initiated by the user or because the program is simply done doing what it does, then you can simply add a final call to a cleanup procedure.
If the program is terminated due to an interrupt signal, such as a user sending a SIGINT signal, or the system shutting down, then you can catch those signals and put your cleanup procedure in the registered callback.
I wrote a short example on how to catch interrupts using Ada. It's available at github and as a wiki article.
Another option is using the Florist POSIX package from libre.adacore.com. Perhaps there's something of use in the posix-signals package.
You can raise an exception, which can be one defined by the language or one defined in your program. Your exception handler would then execute.
You can also use Ada.Command_Line.Set_Exit_Status to return a code to the invoking environment.
Addendum: You can also handle external interrupts, as shown here.
Seems like there should be a way to do this in pure Ada, but I couldn't find one.
One idea would be to use Interfaces.C and call atexit() with a callback function that does the cleanup. I haven't tried it, but I can't think of any reason it wouldn't work.
More info on Ada callbacks from C
If the program is allowed to shut down nicely, then you can use standard language facilities like controlled types to provide your "on shutdown" behavior.
If the program isn't allowed to shut down nicely by the OS, then there is no language-defined way to do it in any language. You will have to use some kind of OS call to do it.
Note that the example you showed was not a Java call, but a JVM call. JVM = Java Virtual Machine...essentially the Java OS. You could make the exact same call from Ada, assuming your Ada code is running on the JVM. If you are running under Windows instead you'd have to do it with Win32 system calls. You can make those from Ada, but obviously the exact calls aren't portably defined in the language.
Java has the finalize block which allows to execute some statements after a block
is left (executed even if an exception is raised). Example:
try {
...
} catch (Exception e) {
...
} finally {
... // any code here
}
Ada has the controlled objects which allows to implement a Finalize operation
but there is no finalize block equivalent as in java. This is useful for logging,
closing files, transactions and so on (without having to create a specific tagged type for each possible block).
How would you implement such finalize block in Ada 2005 (while keeping the code readable)?
Are there plans in Ada 2012 to allow executing any finalization code easily?
I believe this code will do what you ask; it successfully prints out 42 with the present raise or with return. It's an implementation of T.E.D's suggestion.
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;
As Adrien mentions in the comment, Finalize is more analogous to a destructor.
To get something approximating an exception/final sequence you can do something along these lines (WARNING, not compiled, just typed--we'll work out any errors together :-) See also the Exceptions section of the 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;
Assuming you have understood the difference between ada.finalization and a 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;
Marc C has the right approach for trying to emulate that in straight-line procedural code.
However, IMHO that structure is mostly a way to hack around Java's OO system, for those who want one of the structural benifits of OO in old-fashioned procedural programming. Even in Java you are almost always better off creating a proper class instead.
So I don't think it is too much of a stretch to say that the proper way to get that functionality in Ada would be to make a proper object, and make your object a child of Ada.Finalization.Controlled.
If you don't want to bother with creating an actual object, you could just create a dummy one, put your finalization code in it, and declare it on the stack at the top of the block you want it run for. The drawback to that is that controlled types themselves (as least the last time I used them) have to be declared at package-level scope. When that's the case, you'd be unable to put direct references to lower-declared objects in them. They claimed they were going to fix that in future language revision, but I haven't tried it recently to see if they did.
Just thought of another answer. Its a bit heavy (and perhaps more trouble than it is worth). But it would give you something that looks a bit like your old finalize block
The idea would be to put your "finalizable" code in a task. You cannot leave the scope a task is declared in until the task terminates. So you could put your work code in the task and your "finally" code just outside of the scope the task is defined in. The parent task would sit there and wait for the work task to end (one way or another), and then it would run the "finally" code no matter how it ended. The drawback is that if the task throws an exception, it will stop at the task. So you still don't quite get the behavior that you can throw an exception and it will propagate out automatically while the "finalize" code gets run. You could maybe get that behavior back by adding a rendezvous and a second task (that's the problem with tasks. They are 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 might be a way to do something like this with protected objects too. I'll leave that one for others to figure out.
Lets put this problem into perspective.
In programming theory there exists concepts of an object's create & destroy and a procedure's try & finally. Both ultimately deal with resource management but they pivot on different things.
With objects we pivot on the potentially long-living tree of objects and variables referenced by the object pointer.
With procedures we pivot on the usually temporary objects and variables existing in the scope of the procedure call (the notable exception is main)
Now, depending on the procedure we might need to spawn a series of resources which, if the procedure is interrupted by a fatal error, must rollback all spawned resources in reverse order. Quite often this is easiest achieved by creating objects dedicated to managing their respective resource. Ada.Finalization becomes quite useful here.
But this can be overkill, and there can be arguments made against this technique on a case-by-case basis. So if we are interested in self-containing resource management to a procedure and making use of a C++-style finally keyword, consider the following.
I have often initially been pleased by the promise of convenience using finally only to be disappointed by how complicated the code can turn out after making use of it. Nesting is required for complex rollback operations, and many times the process is not linear, requiring logic to decide exactly how to roll back depending on how far we made it through the initialization. The nested block structure corners you to use linear logic. Thus when you need something more complicated then you are seriously considering making use of goto.
I've gone through this enough times to realize that, as appetizing as OOP-style finalize can be with what I mentioned, a true try & finally can be achieved in Ada without all the headaches as demonstrated in the following example. Note, it's far from perfect, but I think the good outweighs the bad with this strategy. What I especially like about this strategy is how explicit the finalize process becomes, all steps labeled and checked against Ada's type system, well organized and easy to manage. The C++-style try & finally scatters this kind of code, and Ada's Finalize hide's it and makes it harder to control the higher-level order of fail-over logic.
procedure Proc is
type Init_Stages_All is (Do_Null, Do_Place, Do_Pour, Do_Drink);
subtype Init_Stages is Init_Stages_All range Do_Place .. Do_Drink;
Init_Stage : Init_Stages_All := Do_Null;
procedure Initialize_Next is
begin
Init_Stage := Init_Stages_All'Succ(Init_Stage);
case Init_Stage is
when Do_Place => ...
when Do_Pour => ...
when Do_Drink => ...
when Do_Null => null;
end case;
end Initialize_Next;
procedure Finally is
begin
for Deinit_Stage in reverse Init_Stage .. Init_Stages'Last loop
case Deinit_Stage is
when Do_Place => ...
when Do_Pour => ...
when Do_Drink => ...
end case;
end loop;
end Finally;
begin
Initialize_Next; -- Do_Place
...
Initialize_Next; -- Do_Pour
...
Initialize_Next; -- Do_Drink
...
Finally;
exception
when E : others =>
...
Finally;
raise;
end Proc;
On a final note, this strategy also makes it easier to deal with finalization exceptions. I'd suggest creating a procedure in Finally that is called when an exception is raised and recursively call Finally by manipulating the Init_Stage as well as interject any additional fail-over logic there.