ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ......

279
ADA95 Tutorial Part 2 produit à partir du matériel disponible à l'URL http://www.infres.enst.fr/~pautet/Ada95/a95list.htm

Transcript of ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ......

Page 1: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

ADA95 TutorialPart 2

produit à partir du matériel disponible à l'URL http://www.infres.enst.fr/~pautet/Ada95/a95list.htm

Page 2: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

ADA 95 TUTORIALThis tutorial teaches the entire Ada 95 dialect of the Ada language. It is composed of 33 chapters which should be studied in order since topics are introduced in a logical order and build upon topics introduced in previous chapters. It is to the students benefit to download the source code for the example programs, then compile and execute each program as it is studied. The diligent student will modify the example program in some way, then recompile and execute it to see if he understands the material studied for that program. This will provide the student with valuable experience using his compiler.

The recommended method of study is to print the text for one or two chapters, download the example programs, and study the material by loading the example programs in the compiler's editor for viewing. Following successful completion of each chapter, additional chapters can be downloaded as progress is made.

Note that completion of the first part of this tutorial will give the student the ability to write very significant programs in Ada, but completion of the second part will give the student the ability to use all of the capabilities of Ada.

Version 2.5 - February 1, 1998

The original for this page is located at http://www.swcp.com/~dodrill/a95doc/a95list.htm and is the only fully authorized site for distribution of this tutorial. Many persons have downloaded one or more of our tutorials for redistribution without our consent, and occasionally do not include all of the components needed for the complete package. You can be assured that the tutorial will be complete and up to date only at the home site, since we have no control over the actions of other web site operators.

This tutorial is distributed as shareware which means that you do not have to pay to use it. However, the author spent a good deal of time and financial resources to develop this tutorial and requests that you share in the financial burden in a very small way, but only if you felt the tutorial was valuable to you as an aid in learning to program in Ada. If you wish to remit a small payment to the author, full instructions for doing so will be given by clicking the link below. I hope you find programming in Ada to be rewarding and profitable. I personally think Ada is the best language to use for a large project with more than a single programmer because of the careful interface checking done by the compiler.

How to Remit Payment For this Tutorial!

Page 3: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Part 2 - Advanced Ada 95 Tutorial

Chapter 17 - Exceptions Chapter 18 - Advanced Subprogram Topics Chapter 19 - Advanced Array Topics Chapter 20 - Advanced Record Topics Chapter 21 - Advanced Packages & Private Types Chapter 22 - Object Oriented Programming Chapter 23 - More Object Oriented Programming Chapter 24 - Binary Input/Output Chapter 25 - Dynamic Allocation Chapter 26 - Tasking Chapter 27 - The Simple Rendezvous Chapter 28 - The Conditional Rendezvous Chapter 29 - Additional Tasking Topics Chapter 30 - Generic Subprograms Chapter 31- Generic Packages Chapter 32 - Control of Representation Chapter 33 - More Example Programs

Page 4: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Ada Tutorial - Chapter 17

EXCEPTIONS

EXCEPTIONS ARE NOT NEW TO YOU

Assuming you have completed part 1 of this tutorial, you have seen many references to exceptions, but we have said little about how you can use them. The purpose of this chapter is to instruct you on the use of exceptions, and by the time you complete it, you will have the ability to use exceptions to develop a program with its own error handling ability.

WHY DO WE NEED EXCEPTIONS?

The original charter for the development of Ada included the ability to operate in a real-time environment. You already understand, if you have much programming experience, that if it is possible for an error to surface, it will eventually surface. Many programming languages simply terminate operation if a "fatal error" is detected, but this could be a disaster if the program was controlling a real-time system upon which human lives or safety depended. A 747 in final approach to the airport, or a system used in a hospital operating room would be two examples of systems that simply could not be terminated abruptly because a bad data point was somehow accumulated. The careful application of Ada exceptions will allow the software to gracefully recover from such a situation rather than aborting operation completely.

OUR FIRST EXCEPTION

Example program ------> e_c17_p1.ada

-- Chapter 17 - Program 1with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Except1 is

procedure Divide_Loop is Divide_Result : INTEGER; begin for Index in 1..8 loop Put("Index is"); Put(Index, 3); Put(" and the answer is"); Divide_Result := 25 / (4 - Index); Put(Divide_Result, 3); New_Line; end loop; exception when Constraint_Error => Put_Line(" Divide by zero error."); end Divide_Loop;

begin Put_Line("Begin program here."); Divide_Loop; Put_Line("End of program.");end Except1;

-- Result of Execution

-- Begin program here.-- Index is 1 and the answer is 8-- Index is 2 and the answer is 12

Page 5: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Index is 3 and the answer is 25-- Index is 4 and the answer is Divide by zero error.-- End of program.

Examine the program named e_c17_p1.ada for our first example program with an exception handler. Ignore lines 18 and 19 for the moment and you will have a program that is not at all unusual, and should pose no problem for you to understand. The program does have a carefully introduced error however, because when we reach a value of 4 for Index in line 14, we will be attempting to divide by zero. Dividing by zero is not allowed in any programming language, because the answer is infinite and therefore undefined. The Ada runtime system will, by definition, cause the exception named Constraint_Error to be raised, which is the Ada way of saying that a divide by zero was attempted. This signals the system to do something about it. (Actually, there are many other ways to get the Constraint_Error exception raised but we will worry about them later.)

The Ada system will search, in a very definite way, for any instructions we have given about this error, and if it finds none, will terminate operation of the program after issuing a message concerning the error. If we have given instructions about what to do with the error, it will execute the instructions and continue operation as we direct it to do. The method of giving the system these instructions is illustrated in lines 18 and 19.

HOW ARE EXCEPTIONS HANDLED?

When any exception is raised, the system immediately looks at the end of the current block or subprogram for the reserved word exception. If it is found, and if the specific exception that was raised is defined there, the instructions associated with that exception are executed, and the subprogram or block is exited.

To define a handler for a specific exception, the reserved word when is used, followed by the name of the exception, and finally the sequence of statements to be executed following the => operator. The sequence of statements can be of arbitrary complexity, but should be kept simple due to the nature of exception handling. In this case, we output a message to the monitor and do nothing else. As many different exceptions as desired can be handled at the end of any block or subprogram by adding additional constructs of the form,

when <exception-name> => instructions;

following the single instance of the reserved word exception. We will study examples of multiple exception handlers later in this chapter.

WHAT HAPPENS FOLLOWING EXCEPTION HANDLING?

Following handling of the exception, the program executes an otherwise normal return to the calling program, and the normal sequence of instructions in the calling program is executed. Note that it is impossible to jump back into the subprogram or block in which the exception was raised from the exception handling routine at the end of that block. In this case, because of the logic used, the loop defined in line 10 is terminated early because we essentially jumped out of the loop and the program is ended. If an exception handler or a group of exception handlers is included, it must be the last thing in the block. If normal execution of the block reaches the end of the executable statements by coming to the reserved word exception, the block is terminated normally. You cannot drop into the exception handler at the end of a block. The only way to get to the exception handling code is through raising an exception.

In spite of the additional questions you have at this point, compile and execute this program. Observe the results, and if you do not understand the output, reread the above text until you do, because these fundamental points are essential to understanding the entire topic of exceptions.

LET'S USE SEVERAL EXCEPTIONS

Page 6: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Example program ------> e_c17_p2.ada

-- Chapter 17 - Program 2with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Except2 is

procedure Divide_Loop(Index : in INTEGER) is Divide_Result : INTEGER; begin Put("Index is"); Put(Index, 3); Put(" and the answer is"); Divide_Result := 25 / (4 - Index); Put(Divide_Result, 4); New_Line; exception when Constraint_Error => Put(" Divide by zero error"); Put_Line(" in loop 1."); end Divide_Loop;

procedure New_Divide_Loop(Index : in INTEGER) is My_Own_Exception : exception; Divide_Result : INTEGER; begin Put("Index is"); Put(Index, 3); Put(" and the answer is"); if Index = 4 then raise My_Own_Exception; end if; Divide_Result := 25 / (4 - Index); Put(Divide_Result, 4); New_Line; exception when My_Own_Exception => Put(" Divide by zero error"); Put_Line(" in loop 3."); when Constraint_Error => Put_Line("This shouldn't happen."); Put_Line("But is included anyway."); when others => Put_Line("Some other exception found."); end New_Divide_Loop;

begin Put_Line("Begin program here."); for Count in 1..6 loop -- begin loop number 1 Divide_Loop(Count); end loop; Put_Line("End of first loop.");

for Count in 1..6 loop -- begin loop number 2 declare Divide_Result : INTEGER; begin Put("Count is"); Put(Count, 3); Put(" and the answer is"); Divide_Result := 25 / (4 - Count); Put(Divide_Result, 4); New_Line; exception when Constraint_Error => Put(" Divide by zero error");

Page 7: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Put_Line(" in loop 2."); end; end loop; Put_Line("End of second loop.");

for Count in 1..6 loop -- begin loop number 3 New_Divide_Loop(Count); end loop; Put_Line("End of program.");

end Except2;

-- Result of Execution

-- Begin program here.-- Index is 1 and the answer is 8-- Index is 2 and the answer is 12-- Index is 3 and the answer is 25-- Index is 4 and the answer is Divide by zero error in loop 1.-- Index is 5 and the answer is -25-- Index is 6 and the answer is -12-- End of first loop.-- Count is 1 and the answer is 8-- Count is 2 and the answer is 12-- Count is 3 and the answer is 25-- Count is 4 and the answer is Divide by zero error in loop 2.-- Count is 5 and the answer is -25-- Count is 6 and the answer is -12-- End of second loop.-- Index is 1 and the answer is 8-- Index is 2 and the answer is 12-- Index is 3 and the answer is 25-- Index is 4 and the answer is Divide by zero error in loop 3.-- Index is 5 and the answer is -25-- Index is 6 and the answer is -12-- End of program.

Examine the program named e_c17_p2.ada for additional examples of exceptions. This program will answer many of your questions about exceptions.

In the last program we terminated the loop when the exception was raised, but we may desire to continue execution of the program following the exception. The first procedure in this program is logically identical to the last example program except the loop is moved to the calling program. When the divide by zero is detected by the system, which raises the Constraint_Error exception, the exception is handled by the exception handler defined in lines 17 and 18, and the return to the calling program is effected. In this case however, when control returns to the calling program, we are still inside of the loop, and the loop completes normally. This should indicate to you that by careful selection of where you handle exceptions, you can control the overall result. We will see more about this as we continue our study of exceptions.

The logic of the second group of instructions, found in lines 49 through 64, is identical to the logic of the first group as studied in the last paragraph. The only difference is that the procedure has been changed into a block and inserted into the code in an inline fashion. This has been done to illustrate the use of an exception in a block of code, and to illustrate that the exception handler for the block of code is put at the end of that block. After the exception is raised and handled, execution begins at the first statement following the block. Because the block is contained within the loop, the

Page 8: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

exception is handled within the loop and the loop runs to completion.

MULTIPLE EXCEPTION HANDLERS

Finally, we come to the section of code in lines 66 through 69, consisting of a simple loop calling the procedure New_Divide_Loop. The procedure itself, defined in lines 21 through 40, contains an example of a new operation, the ability to make up our own exception, raise it our self, and handle it with our own exception handler.

Line 22 declares the identifier My_Own_Exception as being the name of an exception, and is defined in much the same way that we would declare a variable. We cannot assign a value to it, but we can raise it anywhere within its defined scope which is the same as the scope of a variable declared at the same place. The exception is automatically initialized to the "not raised" condition by the system.

Beginning in line 34, we define three different exception handlers, which will cover any exceptions raised anywhere within this procedure. The first two are named exception handlers but the third handler uses the reserved word others to indicate that it will be used for any exceptions that are not handled by the two named exception handlers. The others clause is optional, but if it is included, it must be last.

RAISING AN EXCEPTION

If we reach line 28 with a value of 4, which we eventually will because of the logic of the calling program, we will detect the divide by zero that would be attempted upon reaching line 31. Instead of letting the system generate the exception named Constraint_Error, we generate our own exception named My_Own_Exception, using the reserved word raise followed by the name of the exception. As soon as we raise this exception, the system jumps to the end of the block, looks for the reserved word exception, which it finds, then looks for the exception handler with the name that was raised. Upon finding it, the statements are executed, resulting in a message being output to the display, and we return to the calling program.

In this case, the system will not raise the exception Constraint_Error, because we are detecting the error before it actually happens. You could raise it yourself by inserting the statement "raise Constraint_Error;" somewhere in this procedure, possibly when the value of Index is equal to 3. It would be a good exercise for you to insert that in the code to see that you can raise one of the system exceptions as well as your own.

Be sure to compile and execute this program to verify proper operation according to this description and to ascertain your understanding of the same.

Note that if an exception occurs, formal parameters of mode out or in out are not updated since a normal return is not accomplished. Partial results will therefore not be returned to the calling program. This is not illustrated here, but is left for the student to investigate if desired.

WHAT ARE THE PREDEFINED EXCEPTIONS?

There are four predefined exceptions which can be raised by the system to indicate a very specific problem. A brief definition follows;

1. Constraint_Error - This will occur if something goes out of its assigned range. 2. Program_Error - This will occur if we attempt to violate an Ada control structure such as

dropping through the bottom of a function without a return. 3. Storage_Error - This will occur if we run out of storage space through either recursive calls

or storage allocation calls. 4. Tasking_Error - This will occur when attempting to use some form of tasking in violation

of the rules.

WHAT ABOUT AN UNHANDLED EXCEPTION?

Page 9: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Example program ------> e_c17_p3.ada

-- Chapter 17 - Program 3with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Except3 is

procedure Divide_By_Zero(Count : INTEGER) is Divide_Result : INTEGER; begin Put("Count is"); Put(Count, 3); Put(" and the answer is"); Divide_Result := 25 / (Count - 4); Put(Divide_Result, 4); New_Line; exception when Constraint_Error => Put_Line(" Divide by zero occurred"); end Divide_By_Zero;

procedure Raise_An_Error(Count : INTEGER) is My_Own_Error : exception; Another_Result : INTEGER; begin Put("Count is"); Put(Count, 3); Another_Result := 35 / (Count - 6); -- untested divide by zero if Count = 3 then raise My_Own_Error; end if; Put_Line(" and is a legal value"); exception when My_Own_Error => Put_Line(" My own error occurred"); end Raise_An_Error;

begin Put_Line("Begin program here."); for Count in 1..7 loop Divide_By_Zero(Count); Raise_An_Error(Count); end loop; Put_Line("End of program.");

exception when Constraint_Error => Put(" Constraint error detected at"); Put_Line(" the main program level."); Put_Line("Program terminated.");end Except3;

-- Result of Execution

-- Begin program here.-- Count is 1 and the answer is -8-- Count is 1 and is a legal value-- Count is 2 and the answer is -12-- Count is 2 and is a legal value-- Count is 3 and the answer is -25-- Count is 3 My own error occurred

Page 10: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Count is 4 and the answer is Divide by zero occurred-- Count is 4 and is a legal value-- Count is 5 and the answer is 25-- Count is 5 and is a legal value-- Count is 6 and the answer is 12-- Count is 6 Constraint error detected at the main program level.-- Program terminated.

Examination of the program named e_c17_p3.ada will reveal what happens if an exception is raised that is not handled by the program. In a word, the program will be terminated, but we need to understand how termination occurs so we can intelligently prevent it.

There is a loop in the main program which calls two procedures successively, Divide_By_Zero and Raise_An_Error. The first procedure is identical to that in the first two example programs and the only exception raised is Constraint_Error, which is handled properly.

The second procedure has its own exception defined, named My_Own_Error which it raises and handles itself in the manner defined previously in this chapter. It also has a divide by zero problem in line 26 that will raise the exception Constraint_Error when Count is equal to 6. Of course, the logic is defined to make this happen and illustrate the error.

PROPAGATION OF EXCEPTIONS

When the exception Constraint_Error is raised at line 26, the system searches for the reserved word exception which it finds in line 31 at the end of the procedure. It then searches for a sequence of statements for Constraint_Error which it does not find. Since an exception handler is not found within the procedure, the exception is propagated to the calling program in such a way that the exception appears to have been raised by the calling statement. In this case it will appear to the logic as if the exception Constraint_Error was raised by the statement in line 39. Once again, the exception rules are applied, and the system searches for an exception section at the end of the block or subprogram, in this case being the main program. Finding the reserved word exception in line 43, the system looks for the desired exception handler, which it finds and executes, then drops out of the bottom of the main program and returns to the operating system.

If there were no handler for the exception, the exception would be propagated to the operating system, and it would issue some sort of nasty message on the standard output device about an unhandled exception leading to program termination.

It should be somewhat obvious to you that if you added another level of subprogram nesting, you could report the error yourself, and possibly recover operation of the program. It is all a matter of program definition.

CAN YOU EXECUTE AN EXCEPTION WITHOUT RAISING IT?

As mentioned before, the section of code at the end of a block or subprogram, following the reserved word exception, is never executed without raising an exception. It can never be executed by dropping into it.

Be sure to compile and execute this program to observe the operation of the exceptions.

EXCEPTIONS CAN OCCUR DURING DECLARATIONS

Example program ------> e_c17_p4.ada

-- Chapter 17 - Program 4with Ada.Text_IO;use Ada.Text_IO;

procedure Except4 is

Page 11: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

procedure Try_It is VALUE : constant := 8; subtype LIMIT_RANGE is INTEGER range 14..33; Funny : LIMIT_RANGE := VALUE; begin Put_Line("We are in the Try_It procedure"); exception when Constraint_Error => Put_Line("Constraint error occurred"); Put_Line("Detected in procedure Try_It"); end Try_It;

procedure Try_To_Fix_It is begin Put_Line("We are in the Try_To_Fix_It procedure."); Try_It; exception when Constraint_Error => Put_Line("Constraint error occurred"); Put_Line("Detected in procedure Try_To_Fix_It"); end Try_To_Fix_It;

begin Put_Line("Begin program here."); for Count in 1..4 loop Put_Line("Ready to call Try_To_Fix_It."); Try_To_Fix_It; New_Line; end loop; Put_Line("End of program.");

exception when Constraint_Error => Put ("Range error detected at the"); Put_Line(" main program level."); Put_Line("Program terminated.");end Except4;

-- Result of execution

-- Begin program here.-- Ready to call Try_To_Fix_It.-- We are in the Try_To_Fix_It procedure.-- Constraint error occurred-- Detected in procedure Try_To-Fix_It---- Ready to call Try_To_Fix_It.-- We are in the Try_To_Fix_It procedure.-- Constraint error occurred-- Detected in procedure Try_To-Fix_It---- Ready to call Try_To_Fix_It.-- We are in the Try_To_Fix_It procedure.-- Constraint error occurred-- Detected in procedure Try_To-Fix_It---- Ready to call Try_To_Fix_It.

Page 12: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- We are in the Try_To_Fix_It procedure.-- Constraint error occurred-- Detected in procedure Try_To-Fix_It---- End of program.

Examine the program named e_c17_p4.ada for an example of an exception that occurs during the declaration part of the program.

When a procedure is called, its declarations are elaborated prior to the logic being executed, as we have stated before. If one of the declarations cannot be properly elaborated, then an error occurs and an exception is raised. Examining the procedure Try_It will reveal an error in lines 8 through 10, where he variable Funny is declared to be of type LIMIT_RANGE with limits of 14 through 33, then it is initialized to the value 8. Since this is out of the allowed range, the exception Constraint_Error will be raised. The executable part of the procedure is not yet ready for use, so the exception handler defined within it cannot be used, and the exception will be propagated to the calling program where it will be handled just as if it occurred in the calling statement, which is line 22 in this case. The exception will therefore be handled by lines 24 through 26 of the procedure Try_To_Fix_It. Note that we never executed the code in the procedure named Try_It.

Be sure to compile and run this program, then study the results.

ADDITIONAL PREDEFINED EXCEPTIONS

You will find that there are actually additional exceptions predefined by your compiler, but these are all defined in additional packages supplied with your compiler. Packages such as Ada.Text_IO, Ada.Sequential_IO, or Ada.Calendar (to be discussed later with tasking), have some number of exceptions defined as a part of their interfaces, but there are only four exceptions predefined as a part of Ada. These were listed and discussed earlier.

A FEW MORE TOPICS CONCERNING EXCEPTIONS

Example program ------> e_c17_p5.ada

-- Chapter 17 - Program 5package Stuff is Funny_Add_Error : exception; procedure Add_One(Number : in out INTEGER); function Subtract_One(Number : INTEGER) return INTEGER;end Stuff;

with Ada.Text_IO;use Ada.Text_IO;

package body Stuff is

procedure Add_One(Number : in out INTEGER) is begin Number := Number + 1; exception when Funny_Add_Error => Put_Line("Funny add error raised"); when Constraint_Error => Put_Line("Constraint error raised"); end Add_One;

function Subtract_One(Number : INTEGER) return INTEGER is Funny_Subtract_Error : exception; begin raise Funny_Subtract_Error;

Page 13: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

return Number - 1; exception when Funny_Add_Error => Put_Line("Funny add error raised"); raise; when Funny_Subtract_Error => Put_Line("Funny subtract error raised"); raise; end Subtract_One;

begin null;exception-- Numeric_Error is obsolete in Ada 95. It is another name for-- the exception Constraint_Error.-- when Numeric_Error =>-- Put_Line("Numeric error during elaboration"); when Constraint_Error => Put_Line("Constraint_Error during elaboration"); when Funny_Add_Error => Put_Line("Funny Add error during elaboration");-- when Funny_Subtract_Error =>-- Put_Line("Funny subtract error during elaboration");end Stuff;

with Ada.Text_IO, Stuff;use Ada.Text_IO, Stuff;

procedure Except5 is Index : INTEGER := 5; Add_Error : exception renames Stuff.Funny_Add_Error;begin Add_One(Index); Index := Subtract_One(Index);exception when Numeric_Error | Constraint_Error => Put_Line("Numeric error or constraint error raised."); when Add_Error => Put_Line("Addition error detected"); when others => Put_Line("An unknown exception raised"); raise; -- Raise it again for the operating systemend Except5;

-- Result of execution

-- Funny subtract error raised-- An unknown exception raised-- Unhandled exception; funny_subtract_error

-- (Note, The last line above will be different for each compiler,-- but will say something about an unhandled exception. It-- will probably output about 10 lines of text.)

The example program named e_c17_p5.ada illustrates a few additional topics about exceptions and illustrates how they are used in a package. This is a very strange program with lots of exception

Page 14: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

handling examples for your study. You will be left on your own to study the overall operation of this program, but the unique exception handling techniques will be pointed out to you.

The package body contains a section of initialization code in lines 37 through 49 which is composed of nothing but a null statement and several exception handlers. These are only used during initialization of the package since they are not within the executable portion of either of the subprograms. You will notice that the exception named Funny_Add_Error is declared in the package specification so it is visible in the exception handler in line 46, but the exception named Funny_Subtract_Error is not visible there because it is declared within the function. We will see soon however, that even this exception can be propagated to the main program.

When the program is executing, a call to the function Subtract_One raises the exception Funny_Subtract_Error which is handled by the exception handler at the end of the function in line 32. A message is displayed and the same exception is raised by the isolated raise statement in line 34. This statement simply raises the exception that caused the jump to the exception handler in the first place. The isolated raise statement can only be used within an exception handler. The exception is passed on to the calling program, even though it has already been handled here.

Because the exception named Funny_Subtract_Error is not visible to the main program, it cannot handle it by name but even this exception can be handled by an others clause as is done in line 68. After printing a message, the same exception is once again raised in line 70 where it is passed on to the operating system. You will see when you execute this program that the exception is known by name to the operating system. It will give you a nasty message about the unhandled exception and terminate operation.

THE others CLAUSE IN AN EXCEPTION HANDLER

If the others clause is used, it must be the last in the exception handler list and it cannot be combined with any other exceptions such as illustrated in line 64. As in other Ada constructs, two or more exceptions can be "or"ed and use the same exception handler. Numeric_Error was available inn Ada 83, but is considered obsolete in Ada 95. The name Numeric_Error is a synonym for Constraint_Error in Ada 95 to permit legacy code to compile without error.

Note line 59 where an exception is renamed to reduce the length of its name. Any exception can be renamed in a similar fashion.

Be sure to compile and execute this program and spend the time necessary to understand the exception propagation illustrated here.

WHAT IS AN EXCEPTION OCCURRENCE?

Many times it is adequate to simply report that an exception occurred and the general nature of the exception is sufficient to permit a recovery from the exceptional condition. There are times however, that it is necessary to provide additional information about the exception and what caused it. The exception occurrence is available in a predefined Ada 95 package for this purpose. The exception occurrence gives a unique name to one instance of raising an exception and provides hooks to completely analyze where and when the exception occurred. As always in this tutorial, an example program provides the best illustration, so examine the example program named e_c17_p6.ada.

Example program ------> e_c17_p6.ada

-- Chapter 17 - Program 6

with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Exceptions;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Occur is

Except_ID : Ada.Exceptions.EXCEPTION_OCCURRENCE;

Page 15: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

procedure Divide_Loop is Divide_Result : INTEGER; begin

for Index in 1..8 loop Put("Index is"); Put(Index, 3); Put(" and the answer is"); Divide_Result := 25 / (4 - Index); Put(Divide_Result, 3); New_Line; end loop;

exception when Except_ID: Constraint_Error => Put_Line(" Divide by zero error."); New_Line; Put_Line(" Exception name:"); Put_Line(Ada.Exceptions.Exception_Name(Except_ID)); New_Line; Put_Line(" Exception information:"); Put_Line(Ada.Exceptions.Exception_Information(Except_ID)); New_Line;

when Except_ID: Storage_Error => Put_Line(" Storage error detected."); Put_Line(Ada.Exceptions.Exception_Information(Except_ID)); New_Line;

when Except_ID: others => Put_Line(" Unknown exception detected."); Put_Line(Ada.Exceptions.Exception_Information(Except_ID)); New_Line;

end Divide_Loop;

begin Put_Line("Begin program here."); Divide_Loop; Put_Line("End of program.");end Occur;

-- Result of Execution

-- Begin program here.-- Index is 1 and the answer is 8-- Index is 2 and the answer is 12-- Index is 3 and the answer is 25-- Index is 4 and the answer is Divide by zero error.---- Exception name:-- Constraint_Error---- Exception message:-- Constraint_Error (divide by zero)---- End of program.

Page 16: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

In line 2 we with the package named Ada.Exceptions which provides us with the ability to name and use an exception occurrence, and we name one in line 8. The name Except_ID can be used to refer to any single exception at a time, but can be reused to refer to any other exception at a later time. This example program executes a loop with a contrived error in it, a divide by zero. In fact, you may recognize it as a modification of the first example program in this chapter.

When we arrive at the divide by zero condition, the exception Constraint_Error is raised and the exception handler defined in line 23 is found and execution begins. However, because of the occurrence name given immediately following the reserved word when, this particular occurrence of Constraint_Error is given the name Except_ID. Once we have the occurrence name, we can use it to retrieve a formatted message with the name of the exception, even if the exception name in no longer within scope. This is done via a call to Exception_Name which is a part of the Ada.Exceptions package. This is illustrated in line 28 where the returned line of text is simply copied to the monitor. We can also get a more detailed message including a complete call stack listing by calling the function named Exception_Message with the name of the exception occurrence as the only parameter. This result is also copied to the monitor for inspection.

The actual format is implementation dependent, and your compiler may give a very verbose line of text or a very sparse line of text. The compiler used for this compilation emitted a very scarce text.

If we were to correct the error and allow the program to continue, another divide by zero condition would cause the Constraint_Error and the Except_ID exception occurrence could be used to analyze that particular exception. It should be obvious that you could use this to log certain exceptions to a log file for post execution analysis of what caused some particular catastrophic failure. If properly planned, such a log file would have a complete history of system operation prior to failure.

USING THE OCCURRENCE MORE THAN ONCE

In line 34 of the example program, we use the same occurrence name to retrieve and display information about any occurrence of the Storage_Error exception. This indicates that the exception occurrence acts just like a variable and can be used to refer to any exception that occurs during execution of the program. Note that it is very unlikely that the Storage_Error exception will be raised in this program, but is given here only for illustration.

The same name can be used with the others condition to cover any otherwise unhandled exceptions that occur.

Be sure to compile and execute this program with your compiler to see the output format provided for you. There is no standard way to format this data, so your output could look very different from the example output provided in the result of execution. The same information will be provided for you in some meaningful manner.

PROGRAMMING EXERCISES

1. Change line 31 of e_c17_p2.ada to cause a divide by zero when Index is equal to 2 and see that both exceptions will be handled correctly.(Solution)

-- Chapter 17 - Programming example 1with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure CH17_1 is

procedure Divide_Loop(Index : in INTEGER) is Divide_Result : INTEGER; begin Put("Index is"); Put(Index, 3); Put(" and the answer is");

Page 17: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Divide_Result := 25 / (4 - Index); Put(Divide_Result); New_Line; exception when Constraint_Error => Put(" Divide by zero error"); Put_Line(" in loop 1."); end Divide_Loop;

procedure New_Divide_Loop(Index : in INTEGER) is My_Own_Exception : exception; Divide_Result : INTEGER; begin Put("Index is"); Put(Index, 3); Put(" and the answer is"); if Index = 4 then raise My_Own_Exception; end if; Divide_Result := 25 / (2 - Index); Put(Divide_Result); New_Line; exception when My_Own_Exception => Put(" Divide by zero error"); Put_Line(" in loop 3."); when Constraint_Error => Put_Line("This shouldn't happen."); Put_Line("But is included anyway."); when others => Put_Line("Some other exception found."); end New_Divide_Loop;

begin Put_Line("Begin program here."); for Count in 1..6 loop -- begin loop number 1 Divide_Loop(Count); end loop; Put_Line("End of first loop.");

for Count in 1..6 loop -- begin loop number 2 declare Divide_Result : INTEGER; begin Put("Count is"); Put(Count, 3); Put(" and the answer is"); Divide_Result := 25 / (4 - Count); Put(Divide_Result); New_Line; exception when Constraint_Error => Put(" Divide by zero error"); Put_Line(" in loop 2."); end; end loop; Put_Line("End of second loop.");

for Count in 1..6 loop -- begin loop number 3 New_Divide_Loop(Count); end loop; Put_Line("End of program.");

end CH17_1;

Page 18: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Result of Execution

-- Begin program here.-- Index is 1 and the answer is 8-- Index is 2 and the answer is 12-- Index is 3 and the answer is 25-- Index is 4 and the answer is Divide by zero error in loop 1.-- Index is 5 and the answer is -25-- Index is 6 and the answer is -12-- End of first loop.-- Count is 1 and the answer is 8-- Count is 2 and the answer is 12-- Count is 3 and the answer is 25-- Count is 4 and the answer is Divide by zero error in loop 2.-- Count is 5 and the answer is -25-- Count is 6 and the answer is -12-- End of second loop.-- Index is 1 and the answer is 25-- Index is 2 and the answer isThis shouldn't happen.-- But is included anyway.-- Index is 3 and the answer is -25-- Index is 4 and the answer is Divide by zero error in loop 3.-- Index is 5 and the answer is -8-- Index is 6 and the answer is -6-- End of program.

2. Also in e_c17_p2.ada, declare a new exception in line 22, and raise it in line 29 to see how the others clause handles it.(Solution)

-- Chapter 17 - Programming example 2with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure CH17_2 is

procedure Divide_Loop(Index : in INTEGER) is Divide_Result : INTEGER; begin Put("Index is"); Put(Index, 3); Put(" and the answer is"); Divide_Result := 25 / (4 - Index); Put(Divide_Result); New_Line; exception when Constraint_Error => Put(" Divide by zero error"); Put_Line(" in loop 1."); end Divide_Loop;

procedure New_Divide_Loop(Index : in INTEGER) is My_Own_Exception, Surprise : exception; Divide_Result : INTEGER; begin Put("Index is"); Put(Index, 3); Put(" and the answer is"); if Index = 4 then raise Surprise; end if;

Page 19: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Divide_Result := 25 / (4 - Index); Put(Divide_Result); New_Line; exception when My_Own_Exception => Put(" Divide by zero error"); Put_Line(" in loop 3."); when Constraint_Error => Put_Line("This shouldn't happen."); Put_Line("But is included anyway."); when others => Put_Line(" Some other exception found."); end New_Divide_Loop;

begin Put_Line("Begin program here."); for Count in 1..6 loop -- begin loop number 1 Divide_Loop(Count); end loop; Put_Line("End of first loop.");

for Count in 1..6 loop -- begin loop number 2 declare Divide_Result : INTEGER; begin Put("Count is"); Put(Count, 3); Put(" and the answer is"); Divide_Result := 25 / (4 - Count); Put(Divide_Result); New_Line; exception when Constraint_Error => Put(" Divide by zero error"); Put_Line(" in loop 2."); end; end loop; Put_Line("End of second loop.");

for Count in 1..6 loop -- begin loop number 3 New_Divide_Loop(Count); end loop; Put_Line("End of program.");

end CH17_2;

-- Result of Execution

-- Begin program here.-- Index is 1 and the answer is 8-- Index is 2 and the answer is 12-- Index is 3 and the answer is 25-- Index is 4 and the answer is Divide by zero error in loop 1.-- Index is 5 and the answer is -25-- Index is 6 and the answer is -12-- End of first loop.-- Count is 1 and the answer is 8-- Count is 2 and the answer is 12-- Count is 3 and the answer is 25-- Count is 4 and the answer is Divide by zero error in loop 2.-- Count is 5 and the answer is -25-- Count is 6 and the answer is -12-- End of second loop.

Page 20: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Index is 1 and the answer is 8-- Index is 2 and the answer is 12-- Index is 3 and the answer is 25-- Index is 4 and the answer is Some other exception found.-- Index is 5 and the answer is -25-- Index is 6 and the answer is -12-- End of program.

Page 21: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Ada Tutorial - Chapter 18

ADVANCED SUBPROGRAM TOPICS

In part 1 of this tutorial we covered the topic of subprograms in some detail, but there are many other things to discuss about them, so we return to them for more advanced topics.

DEFAULT PARAMETERS

Example program ------> e_c18_p1.ada

-- Chapter 18 - Program 1with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Defaults is

Index : INTEGER;Animal_Sum : INTEGER;

procedure Animals(Total : in out INTEGER; Cows : in INTEGER := 0; Pigs : in INTEGER := 0; Dogs : in INTEGER := 0) is begin Total := Cows + Pigs + Dogs; Put("Cows ="); Put(Cows, 3); Put(" Pigs ="); Put(Pigs, 3); Put(" Dogs ="); Put(Dogs, 3); Put(" and they total"); Put(Total, 4); New_Line; end Animals;

begin Index := 3; Animals(Animal_Sum, 2, 3, 4); Animals(Animal_Sum, 3, Index, 4); Animals(Dogs => 4, Total => Animal_Sum); Animals(Total => Animal_Sum, Pigs => 2 * Index + 1, Cows => 5); Animals(Dogs => Index + 4, Total => Animal_Sum); Animals(Animal_Sum, Dogs => 4, Pigs => Index, Cows => 2); Animals(Animal_Sum);end Defaults;

-- Result of Execution

-- Cows = 2 Pigs = 3 Dogs = 4 and they total 9-- Cows = 3 Pigs = 3 Dogs = 4 and they total 10-- Cows = 0 Pigs = 0 Dogs = 4 and they total 4-- Cows = 5 Pigs = 7 Dogs = 0 and they total 12-- Cows = 0 Pigs = 0 Dogs = 7 and they total 7-- Cows = 2 Pigs = 3 Dogs = 4 and they total 9-- Cows = 0 Pigs = 0 Dogs = 0 and they total 0

Page 22: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Examine the program named e_c18_p1.ada for some examples of default parameters used in the definition of a procedure. The procedure has four formal parameters, of which the first is of mode in out, and the other three are of the mode in. The three in parameters have default values of zero assigned to each of them. When we call this procedure, we are not required to supply a value for each variable, and those we do not supply a value for will be defaulted to zero upon execution. Of course the first variable in the list, named Total, must have a variable name supplied so it can return a value. Therefore, it cannot be defaulted.

The procedure itself, and the first two calls to it, in lines 29 and 30, should pose no problem for you to understand. When we arrive at line 31, however, we have a few things to point out.

NAMED NOTATION FOR ACTUAL PARAMETERS

We are using the named aggregate notation for the actual parameters in line 31, so they can be listed in any order, but due to the defaults defined in the procedure header, we do not have to specify every parameter, allowing the default values to take effect upon a call to the procedure. You will see, when you compile and run this program, that Cows and Pigs will have the default values of zero. Lines 32 and 33 also use the named aggregate notation and should be clear as to their operation. Line 34 uses the mixed aggregate notation, and as we discussed before, the positional aggregate notation can be used initially, but after switching to the named notation, all remaining entries must be named, unless they are allowed to default. Line 35 illustrates the degenerate case where only the result is used, with all three input variables defaulting to zero.

PARAMETERS WITH out MODE CANNOT BE DEFAULTED

Since the parameters that are of either mode in out or mode out must be able to return a value, they must have a variable defined as their actual parameter, and cannot therefore be defaulted.

Default parameters are not new to you, because you have actually used them in procedure calls before. When you call the procedure New_Line, you have an optional number following it as in New_Line(2). The number of lines to space up on the monitor is defaulted to one in the package Ada.Text_IO, which was supplied to you with your compiler, but you can override the default by inserting a value for the number of lines. It is defined in Annex A.10.1 of the Ada 95 Reference Manual (ARM), where the formal parameter named Spacing is defaulted to the value of 1. Refer to either your documentation or the ARM and see this in use.

DYNAMIC DEFAULT PARAMETERS

Example program ------> e_c18_p2.ada

-- Chapter 18 - Program 2with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Default2 is

Index : INTEGER;Animal_Sum : INTEGER;

function Cow_Constant return INTEGER is begin return 7; end Cow_Constant;

function Pig_Constant return INTEGER is Animals : INTEGER := Cow_Constant - 3; begin return 2 * Animals + 5; end Pig_Constant;

procedure Animals(Total : in out INTEGER;

Page 23: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Cows : in INTEGER := 2 * Cow_Constant; Pigs : in INTEGER := Cow_Constant + Pig_Constant; Dogs : in INTEGER := 0) is begin Total := Cows + Pigs + Dogs; Put("Cows ="); Put(Cows, 3); Put(" Pigs ="); Put(Pigs, 3); Put(" Dogs ="); Put(Dogs, 3); Put(" and they total"); Put(Total, 4); New_Line; end Animals;

begin Index := 3; Animals(Animal_Sum, 2, 3, 4); Animals(Animal_Sum, 2, Index, 4); Animals(Dogs => 4, Total => Animal_Sum); Animals(Total => Animal_Sum, Pigs => 2 * Index + 1, Cows => 5); Animals(Dogs => Index + 4, Total => Animal_Sum); Animals(Animal_Sum, Dogs => 4, Pigs => Index, Cows => 2); Animals(Animal_Sum);end Default2;

-- Result of Execution

-- Cows = 2 Pigs = 3 Dogs = 4 and they total 9-- Cows = 2 Pigs = 3 Dogs = 4 and they total 9-- Cows = 14 Pigs = 20 Dogs = 4 and they total 38-- Cows = 5 Pigs = 7 Dogs = 0 and they total 12-- Cows = 14 Pigs = 20 Dogs = 7 and they total 41-- Cows = 2 Pigs = 3 Dogs = 4 and they total 9-- Cows = 14 Pigs = 20 Dogs = 0 and they total 34

The program named e_c18_p2.ada is identical to the last example program except for one detail. The definition of the default values of the formal parameters are declared differently here. You will notice that the default values are not only arithmetic combinations, but the values to combine are the results of function calls, where the values are dynamically evaluated each time the procedure Animals is called. The default values are constants for each call of the procedure, but they are evaluated for each call and could therefore be different each time the procedure is called and executed. As mentioned previously, this is called elaboration. If the return from Cow_Constant, in line 12, returned the value of a global variable for example, the main program could modify the value of the global variable and therefore modify the value of the default variables prior to each call to the procedure. It would therefore be possible to set up different default values in each of several different procedures based on the currently read time of day, the ambient temperature, or whatever other variable conditions could be read into the system.

Be sure to compile and run this program and observe the results, comparing them with the results you expected the program to output.

THE MYSTERY OF RECURSION

Page 24: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Example program ------> e_c18_p3.ada

-- Chapter 18 - Program 3with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Recurson is

Index : INTEGER;

procedure Print_And_Decrement(Value : in INTEGER) is New_Value : INTEGER; begin Put("The value of the index is now"); Put(Value, 3); New_Line; New_Value := Value - 1; if New_Value > 0 then Print_And_Decrement(New_Value); end if; end Print_And_Decrement;

begin Index := 7; Print_And_Decrement(Index);end Recurson;

-- Result of execution

-- The value of the index is now 7-- The value of the index is now 6-- The value of the index is now 5-- The value of the index is now 4-- The value of the index is now 3-- The value of the index is now 2-- The value of the index is now 1

This topic will be no problem for the experienced Pascal programmer, but for the FORTRAN programmer, it may be an entirely new and somewhat perplexing topic. Stay with it, and you will see exactly what recursion is and how to use it effectively. Examine the example program named e_c18_p3.ada, which is the simplest recursive program possible, but which is excellent for describing what recursion is and how it works.

Beginning at the main program, we assign the variable named Index the value of 7 then call the procedure named Print_And_Decrement, taking along the value of Index as an actual parameter. Arriving at the procedure itself, we use the name Value for the formal parameter, and we display the value on the monitor with an appropriate line of text. Continuing on to line 15, we decrement the value of the passed variable then compare the result to zero. If the value is greater than zero, we call the procedure named Print_And_Decrement taking along the newly decremented value called New_Value. Here is where the FORTRAN programmer notices something new. We are calling the procedure from within itself, and that is just what recursion is. Assume for a moment that we call another complete copy of the procedure, decrement the value once again, and if it is still not zero, call another copy of the procedure. Eventually, the value of the passed variable will be reduced to zero and the procedure calls will all be completed, each returning to the procedure that called it until we arrive once again at the main program.

Page 25: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

You should compile and run this program to see that it really does what we say it does, then return for additional discussion of this program and what it is doing. This is a really dumb way to count from 7 down to 1, but it is a very simple way to illustrate the use of recursion. Later in this tutorial, we will have illustrations of excellent uses of recursion for your instruction.

WHAT ACTUALLY HAPPENED?

When we called the procedure Print_And_Decrement, it began by elaborating its formal variables and assigning them the values passed by the calling program. These are stored on the stack, an internal portion of memory set aside by the Ada system to store dynamic variables and constants, and are available for later use. The local variables are then generated and elaborated, although in this case there was only one, and stored on the stack also with means to refer to them. Finally, the program code itself is actually executed, and when it is completed, the local variables and formal variables are erased from the stack and no longer exist. In a recursive call, the stack grows with each new call, and when control returns to an earlier call of the code, the variables for that call are still on the stack and available for use. In this program, we actually have only one copy of the executable code stored, but we have many copies of the formal variable and the local variable stored on the stack.

ALL ADA SUBPROGRAMS ARE RE-ENTRANT

If you have experience in systems programming and understand what it means for a program to be re-entrant, you will understand how this means of variable storage allows all Ada subprograms to be re- entrant. The ARM requires that all Ada subprograms be re-entrant, but if you don't know what it means, don't worry about it.

It would be a good exercise for you to insert some code to display the value of the formal parameter following the recursive call to see that the value of the formal variable is still available when we return from each recursive call. This code would be inserted immediately after line 18.

You should spend the time necessary to completely understand this program before continuing on to the next example program.

A RECURSIVE FUNCTION

Example program ------> e_c18_p4.ada

-- Chapter 18 - Program 4with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure FuncRecr is

START : constant := -2; STOP : constant := 5; Result : INTEGER; Data_Value : INTEGER;

function Factorial_Possible(Number : INTEGER) return BOOLEAN;

function Factorial(Number : INTEGER) return INTEGER is begin if not Factorial_Possible(Number) then Put("Factorial not possible for"); Put(Number, 4); New_Line; return 0; end if; if Number = 0 then return 1; elsif Number = 1 then

Page 26: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

return 1; else return Factorial(Number - 1) * Number; end if; end Factorial;

function Factorial_Possible(Number : INTEGER) return BOOLEAN is begin if Number >= 0 then return TRUE; else return FALSE; end if; end Factorial_Possible;

begin Put("Factorial program"); New_Line(2);

for Number_To_Try in START..STOP loop Put(Number_To_Try, 3); if Factorial_Possible(Number_To_Try) then Result := Factorial(Number_To_Try); Put(" is legal to factorialize and the result is"); Put(Result, 6); else Put(" is not legal to factorialize."); end if; New_Line; end loop;

New_Line; Data_Value := 4; Result := Factorial(2 - Data_Value); -- Factorial(-2) Result := Factorial(Data_Value + 3); -- Factorial(7) Result := Factorial(2 * Data_Value - 3); -- Factorial(5) Result := Factorial(Factorial(3)); -- Factorial(6) Result := Factorial(4); -- Factorial(4) Result := Factorial(0); -- Factorial(0)

end FuncRecr;

-- Result of Execution

-- Factorial program---- -2 is not legal to factorialize.-- -1 is not legal to factorialize.-- 0 is legal to factorialize and the result is 1-- 1 is legal to factorialize and the result is 1-- 2 is legal to factorialize and the result is 2-- 3 is legal to factorialize and the result is 6-- 4 is legal to factorialize and the result is 24-- 5 is legal to factorialize and the result is 120---- Factorial not possible for -2

Examine the program named e_c18_p4.ada for an example of a recursive function, which is actually

Page 27: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

no different than a recursive procedure. This program is used to illustrate two other Ada concepts, neither of which is new to you, but both of which contain valuable insights for you. The first is the partial declaration which will be discussed at the outset, and the other is a return to exceptions which will be deferred for a couple of paragraphs.

THE PARTIAL DECLARATION

Ada requires that you define everything prior to its use, and this rule is never broken. Suppose you wished to have a program with two procedures, two functions, or even a procedure and a function, calling each other recursively. If A were declared first, it could be called by B, but A could not call B because it would not be defined by the time A was elaborated, and we cannot break the rule of defining everything prior to its use. This is more properly called linear declaration. Ada gets around this by allowing a partial declaration of a type, procedure, function, or package. If you remember, we used the partial declaration with respect to a record when we studied the access type variable, so this concept is not entirely new to you. It is also proper to refer to the partial declaration as a function specification.

In the present example program, we desire to place the functions in the program in the order shown, for no good reason other than to illustrate that it can be done, but we have the problem of linear declaration because Factorial calls Factorial_Possible and they are in the wrong order. The partial declaration in line 12 tells the system that the function Factorial_Possible will be defined later and what its characteristics will be, because the formal parameter is defined along with its return type. The function Factorial is then completely defined, and it can call the other function because it has the definition of its interface. We then have the complete definition of the function Factorial_Possible and we have accomplished our desired goals, while meeting the requirements of linear declaration.

NOW FOR THE RECURSIVE FUNCTION

Assuming you are familiar with the factorial function which is a part of higher mathematics, we will continue with the program description. Since Factorial(N) is the same as N times Factorial(N-1), we can calculate the factorial of any number using a recursive technique. We continue to recurse until we reach a point where we need the value of Factorial(1), which we define as 1, and begin going back up the recursive chain, each time multiplying by the value with which we entered into that particular recursive call. By defining the value of Factorial(0) as 1, and making it illegal to take the factorial of any negative number, we can now write the complete program. Most of the program involves checking limits and outputting messages, but the actual work is done by line 27 which is the recursive call as defined earlier in this paragraph.

EXTRA CHECKS ARE DONE

The program should not be at all difficult for you to follow and understand, so you will be left on your own to study it. A few comments on style should be mentioned, however. The function Factorial calls the other function to verify that the value is factoriable before attempting to do so, and the main program, in lines 44 through 54, checks the value before calling the function to attempt to factorialize the number. A redundant check like this is not necessarily bad, because the procedure was written to be all inclusive, and the main program may wish to do something entirely different than that dictated by the procedure. In lines 58 through 63 however, the main program accepts the default error handling provided by the procedure, by calling the procedure without first checking the data.

Compile and run this program, observing the various messages output depending on which portion of the program handled the errors. Note carefully that the program is not meant to be an illustration of good programming style, only as an illustration of a few things that can be done using Ada.

ANOTHER LOOK AT EXCEPTIONS

The last program was unusual because it has the ability to illustrate three of the four standard

Page 28: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

exceptions defined by the Ada system. They can be illustrated as follows;

• Program_Error - Remove the return from line 27 and you will drop out of the bottom of the function. (Actually, a validated compiler will generate a compile error.)

• Constraint_Error - Change the type of the formal parameter in line 14 to POSITIVE, which covers the range of all numbers legal to compute a factorial for. Then call the function with -1 as a parameter.

• Storage_Error - Call Factorial(100000). The stack should overflow prior to completing the required number of recursions.

A FUNCTION RETURNING AN ARRAY

Example program ------> e_c18_p5.ada

-- Chapter 18 - Program 5with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Revers is

type MY_ARRAY is array(3..10) of INTEGER;

Store_Here : MY_ARRAY := (3, 16, -5, 6, 12, 66, -13, 57); Another : MY_ARRAY;

function Reverse_Array(Data : MY_ARRAY) return MY_ARRAY is Temp : MY_ARRAY; begin for Index in Data'RANGE loop Temp(Index) := Data(Data'FIRST + Data'LAST - Index); end loop; return Temp; end Reverse_Array;

begin

for Index in Store_Here'Range loop Put(Store_Here(Index), 5); end loop; New_Line;

Another := Reverse_Array(Store_Here);

for Index in Another'Range loop Put(Another(Index), 5); end loop; New_Line;

Another := Reverse_Array(Another);

for Index in Another'Range loop Put(Another(Index), 5); end loop; New_Line;

end Revers;

-- Result of Execution

Page 29: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- 3 16 -5 6 12 66 -13 57-- 57 -13 66 12 6 -5 16 3-- 3 16 -5 6 12 66 -13 57

The example program named e_c18_p5.ada will give you an example of a function that returns more than a single scalar variable, in fact, it returns an entire array of INTEGER type variables. The program itself is extremely simple, the only thing that is different from most functions is the type of return listed in line 12, and the actual return in line 18. These must, of course, agree in type or a type mismatch error will be issued during compilation.

Using the technique given here, there is no reason why a function cannot return a record, or even an array of records, as long as the types are correctly defined and they agree when used.

THE INLINE pragma

A pragma is a compiler directive, as we have mentioned previously, and the INLINE pragma tells the compiler to expand the called subprogram body and insert it into the calling program for each call. Since the code is inserted inline, there is no calling sequence and therefore no time wasted in setting up subprogram linkage, but there is a separate section of code for each invocation of the subprogram. The result will usually be a larger but faster program. Use of this pragma is illustrated as follows;

pragma INLINE(subprogram1, subprogram2, ... );

This line is inserted in the declarative part of the compilation unit, following the subprogram specifications. By definition, the meaning of a subprogram is not affected by the pragma.

OPERATOR OVERLOADING AND THE "use" CLAUSE

Example program ------> e_c18_p6.ada

-- Chapter 18 - Program 6package Shape is

type BOX is record Length : INTEGER; Width : INTEGER; Height : INTEGER; end record;

function Make_A_Box(In_Length, In_Width, In_Height : INTEGER) return BOX; function "+"(Left, Right : BOX) return BOX; function "+"(Left : INTEGER; Right : BOX) return BOX; function "*"(Left : INTEGER; Right : BOX) return BOX; procedure Print_Box(Input_Box : BOX);

end Shape;

with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

package body Shape is

function Make_A_Box(In_Length, In_Width, In_Height : INTEGER) return BOX is Temp_Box : BOX;begin

Page 30: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Temp_Box.Length := In_Length; Temp_Box.Width := In_Width; Temp_Box.Height := In_Height; return Temp_Box;end Make_A_Box;

function "+"(Left, Right : BOX) return BOX is Temp_Box : BOX;begin Temp_Box.Length := Left.Length + Right.Length; Temp_Box.Width := Left.Width + Right.Width; Temp_Box.Height := Left.Height + Right.Height; return Temp_Box;end "+";

function "+"(Left : INTEGER; Right : BOX) return BOX is Temp_Box : BOX;begin Temp_Box.Length := Left + Right.Length; Temp_Box.Width := Left + Right.Width; Temp_Box.Height := Left + Right.Height; return Temp_Box;end "+";

function "*"(Left : INTEGER; Right : BOX) return BOX is Temp_Box : BOX;begin Temp_Box.Length := Left * Right.Length; Temp_Box.Width := Left * Right.Width; Temp_Box.Height := Left * Right.Height; return Temp_Box;end "*";

procedure Print_Box(Input_Box : BOX) isbegin Put("Length = "); Put(Input_Box.Length, 3); Put(" Width = "); Put(Input_Box.Width, 3); Put(" Height = "); Put(Input_Box.Height, 3); New_Line;end Print_Box;

end Shape;

with Shape;use type Shape.BOX;

procedure OperOver is

Small_Box : Shape.BOX; Big_Box : Shape.BOX;

begin

Small_Box := Shape.Make_A_Box(2, 3, 2); Big_Box := Shape.Make_A_Box(4, 5, 3);

Shape.Print_Box(Small_Box);

Page 31: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Shape.Print_Box(Big_Box); Shape.Print_Box(Small_Box + Big_Box); Shape.Print_Box(4 + Small_Box); Shape.Print_Box(10 * Big_Box); Shape.Print_Box(Small_Box + 5 * Big_Box);

Shape.Print_Box(Shape."+"(Small_Box, Shape."*"(5, Big_Box)));

end OperOver;

-- Result of execution

-- Length = 2 Width = 3 Height = 2 -- Length = 4 Width = 5 Height = 3 -- Length = 6 Width = 8 Height = 5 -- Length = 6 Width = 7 Height = 6 -- Length = 40 Width = 50 Height = 30 -- Length = 22 Width = 28 Height = 17 -- Length = 22 Width = 28 Height = 17

Operator overloading has been mentioned in this tutorial before, but it is necessary to cover a little more ground on this very important topic, and we will use e_c18_p6.ada as a vehicle for discussion.

We define a package specification in lines 2 through 18 named Shape with a record type named BOX defined within the package. The subprograms Make_A_Box and Print_Box provide us with the ability to generate and display any variable of the BOX type, and we provide three overloaded operators for use with this type. Two of the subprograms overload the + operator for use with variables of this type, and the other provides us with a multiply capability. Many more overloadings could be defined if desired, but these three illustrate the technique.

The body for the package is given in lines 22 through 75. The overloaded operations make little sense in the real world, since adding two boxes would not usually be done by simply adding the size of all three dimensions. However, we are far more interested in the technique of overloading operators, so the interface is what really matters here. The implementation should be simple for you to study on your own.

THE CALLING PROGRAM

The calling program is definitely the most interesting part of this example, beginning with line 80 where we have a use type construct which is new to us. A short digression is in order at this point to explain what this does.

Many experienced Ada programmers feel that the use statement should never be used in an Ada program because it hides the source of any subprogram calls or type definitions. For this reason many projects outlaw its use. It seems reasonable to this author, that Ada.Text_IO should be a permitted violation of this rule, but that is yet another digression so we will not consider it further. In order for the overloaded operators to be available for use as infix operators, as illustrated in lines 94 through 97 of this example program, the use clause must be included. If it is not included, line 97 would need to be written as illustrated in line 99, a very ugly and difficult to read notation. The use of the use type construct in line 80 makes the overloaded operators available for use as infix operators, but does not permit the use of any other subprograms from the Shape package without the package prefix. This gives the ease of readability, and the extra safety desired by some project leaders.

The package name is required when defining variables in lines 84 and 85, and when calling subprograms that are not operator overloads, such as illustrated in lines 89 through 99. Note that

Page 32: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

line 97 calculates the result we expect because the multiplication is done prior to the addition. It is not possible to change the order of precedence when overloading operators.

PROGRAMMING EXERCISES

1. As suggested earlier, include an output statement in e_c18_p3.ada to display the value of Value after the recursive call to see the recursion come back up through the chain of recursive calls.(Solution)

-- Chapter 18 - Programming example 1with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure CH18_1 is

Index : INTEGER;

procedure Print_And_Decrement(Value : in INTEGER) is New_Value : INTEGER; begin Put("The value of the index is now"); Put(Value, 6); New_Line; New_Value := Value - 1; if New_Value > 0 then Print_And_Decrement(New_Value); end if; Put("Following the recursive call, value ="); Put(Value, 6); New_Line; end Print_And_Decrement;

begin Index := 7; Print_And_Decrement(Index);end CH18_1;

-- Result of execution

-- The value of the index is now 7-- The value of the index is now 6-- The value of the index is now 5-- The value of the index is now 4-- The value of the index is now 3-- The value of the index is now 2-- The value of the index is now 1-- Following the recursive call, value = 1-- Following the recursive call, value = 2-- Following the recursive call, value = 3-- Following the recursive call, value = 4-- Following the recursive call, value = 5-- Following the recursive call, value = 6-- Following the recursive call, value = 7

2. Write a recursive program with two procedures named Increment and Display which call each other and increment a variable, initialized to 1, until it reaches a count of 8.(Solution)

-- Chapter 18 - Programming exercise 2

Page 33: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure CH18_2 is

Data_Point : INTEGER;

Procedure Increment(In_Val : in out INTEGER);

procedure Display(Dis_Val : in out INTEGER) isbegin Put("The value of the variable is now "); Put(Dis_Val); New_Line; Increment(Dis_Val);end Display;

procedure Increment(In_Val : in out INTEGER) isbegin if In_Val < 8 then In_Val := In_Val + 1; Display(In_Val); end if;end Increment;

begin Data_Point := 1; Increment(Data_Point);end CH18_2;

-- result of execution

-- The value of the variable is now 2-- The value of the variable is now 3-- The value of the variable is now 4-- The value of the variable is now 5-- The value of the variable is now 6-- The value of the variable is now 7-- The value of the variable is now 8

Page 34: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Ada Tutorial - Chapter 19

ADVANCED ARRAY TOPICS

Example program ------> e_c19_p1.ada

-- Chapter 19 - Program 1with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Summer is

type MY_ARRAY is array (POSITIVE range <>) of INTEGER;

Dummy1 : constant MY_ARRAY := (27, 14, 13, 33); Dummy2 : constant MY_ARRAY := (112 => 27, 113 => 14, 114 => 13, 115 => 33);

My_List : MY_ARRAY(1..12); Stuff : MY_ARRAY(4..11) := (12, 13, 7, 11, 125, others => 17); Total : INTEGER;

function Sum_Up(In_Array : MY_ARRAY) return INTEGER is Sum : INTEGER := 0; begin for Index in In_Array'FIRST..In_Array'LAST loop Sum := Sum + In_Array(Index); end loop; Put("The sum of the numbers is"); Put(Sum, 4); New_Line; return Sum; end Sum_Up;

begin My_List := (0, 1, 2, 3, 4, 3, 2, 1, 2, 3, 4, 7); Stuff := (4 => 12, 8 => 11, 5 => 13, 7 => 1, others => 9);

Total := Sum_Up(My_List); Total := Sum_Up(Stuff);end Summer;

-- Result of Execution

-- The sum of the numbers is 32-- The sum of the numbers is 73

Examine the file named e_c19_p1.ada for an example of an unconstrained array type illustrated in line 7. The box (<>), as it is called, refers to something that must be filled in later, and is a construct that will be used in many other places in Ada. Note that the index type in line 7 is POSITIVE, a fact that will have a bearing on its use later.

USE OF THE UNCONSTRAINED ARRAY TYPE

In line 9, we declare an array constant of type MY_ARRAY and we assign values to the constants by use of the positional aggregate. Since we do not give the range of the indices of the array, the system will assign them, and in this case will use a range of 1 through 4, because of the use of the

Page 35: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

type POSITIVE for the index type. If we would have used type INTEGER for the index, the selected range would have been -2,147,483,648 through -2,147,483,645, which is the lower end of the INTEGER range, unless your implementation used a different lower limit for INTEGER. Careful selection of the array index type is important. In line 10, we declare another constant array, but this time we use a named aggregate and the system does not get the opportunity to pick the range, we pick it explicitly ourselves. The indices do not have to be named in consecutive order as is done here, but all values must be given.

We declare an uninitialized variable array in line 13, which covers the range of 1 through 12, and an initialized variable array in line 14, which uses the positional aggregate method of initialization. The others portion will fill in positions 9, 10, and 11 with the value of 17. The others construct used in the array aggregate is new in Ada 95. It can only be used with a constrained array and it must be last in the list.

A VERY FLEXIBLE FUNCTION

The function in lines 17 through 27 appears to use an unconstrained array for its formal parameter variable, and it does, but with each use of the function, the formal parameter array is constrained to the limits of the actual variable. When the function is called in line 33 of the program with My_List as the actual array variable, the range of My_List, 1 through 12, is used for the range of the formal parameter in the function. This makes it possible to use the array attributes within the function, in the manner shown, such that they are dependent on which array is used for the actual parameter. When Stuff is used for the actual parameter, the formal parameter will have a range of 4 through 11 within the function. The function can therefore be written in such a way that it has flexibility built into it, even though strong type checking continues to be done, since the function cannot be used with a different unconstrained type if we had one in this example program. Note that all array attributes are available here as they are with any array.

A FEW NOTES ABOUT THIS TYPE

All elements of every variable of type MY_ARRAY will be of type INTEGER, and all indices will be of subtype POSITIVE including the range limits. The variables named My_List and Stuff are of the same type with different limits, so the slice can be used with them. After you study this program, compile and execute it so you can observe the output.

AN ARRAY WITH AN ENUMERATED INDEX

Example program ------> e_c19_p2.ada

-- Chapter 19 - Program 2with Ada.Text_IO, Ada. Float_Text_IO;use Ada.Text_IO, Ada.Float_Text_IO;

procedure EnumAry is

type DAY is (MON, TUE, WED, THU, FRI, SAT, SUN);

Hours : array(DAY) of FLOAT; Total_Hours : FLOAT; Today : DAY;

begin for Today in MON..FRI loop Hours(Today) := 8.0; end loop;

Hours(SAT) := 4.0; Hours(SUN) := 0.0;

Total_Hours := 0.0;

Page 36: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

for Today in DAY loop Total_Hours := Total_Hours + Hours(Today); end loop;

Put("Total hours for the week ="); Put(Total_Hours, 5, 2, 0); New_Line;

end EnumAry;

-- Result of Execution

-- Total hours for the week = 44.00

Examine the program named e_c19_p2.ada for an example of an array with an enumerated variable for an index. Since the index of an array may be any discrete type, and an enumerated type is discrete, it can be used for an array index according to the Ada standard.

We define an enumerated type named DAY, that includes the days of the week as elements, then declare an array using the type DAY for the index. It should be apparent to you that this is an anonymous array type, the use of which is discouraged in a significant program, but should cause no type conversion problems in a simple program such as this one. Finally, we declare two other simple variables for later use and begin the executable part of the program.

We use a loop to assign 8.0 hours to each day from MON through FRI, then assign 4.0 hours to SAT, and 0.0 hours to SUN. Finally we sum up the hours for the week and display them on the monitor. It is a very simple program, but it illustrates a very useful construct in Ada. For that reason, you should spend enough time studying it until you thoroughly understand it. Be sure to compile and execute e_c19_p2.ada.

ARRAY OPERATORS

Example program ------> e_c19_p3.ada

-- Chapter 19 - Program 3with Ada.Text_IO;use Ada.Text_IO;

procedure ArrayOps is

type ARY_INT is array(1..6) of INTEGER; type ARY_BOOL is array(4..7) of BOOLEAN;

Do_They_Compare : BOOLEAN; Crowd, Group1, Group2 : ARY_INT; Result, Answer1, Answer2 : ARY_BOOL;

begin

Group1 := (12, 17, -1, 3, -100, 5); Group2 := (13, -2, 22, 1, 1242, -12);

Do_They_Compare := Group1 <= Group2; Do_They_Compare := Group1 > Group2;

if Group1 = Group2 then Put("The arrays are equal."); New_Line;

Page 37: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

end if;

-- Crowd := Group1 + Group2;-- Crowd := Group1 - Group2;-- Crowd := Group1 * Group2;-- Crowd := Group1 / Group2;-- Crowd := Group1 mod Group2;-- Crowd := Group1 rem Group2;

Answer1 := (TRUE, FALSE, TRUE, FALSE); Answer2 := (TRUE, FALSE, FALSE, TRUE);

Result := Answer1 and Answer2; Result := not Answer2; Result := Answer1 or Answer2; Result := Answer1 xor Answer2;

if Answer1 /= Answer2 then Put("The BOOLEAN arrays are not equal."); New_Line; end if;

end ArrayOps;

-- Result of execution

-- The BOOLEAN arrays are not equal.

Some of the operators discussed earlier in this tutorial are available for use with arrays. They operate on each element of the array as if the operation were done in a loop. The example program e_c19_p3.ada will illustrate the use of a few of these operations.

Lines 19 through 24 illustrate the logical operators being used with entire arrays. The arrays are compared element by element, and as soon as a difference is found in two corresponding elements, the comparison result of these two elements is returned as the overall comparison result. If all corresponding elements are equal, the result of the comparison is equal.

Note that it is legal and sometimes very useful to use an array of records. Arrays of records can be compared for equality or inequality, but not for the other four operators ( >, >=, <, <= ) because they are illegal for use with individual record elements. Some thought on your part would have led to this conclusion without us explicitly stating it. Many such combinations of operations are possible with Ada. It will be up to you to try a few yourself, as you need them, because there are too many permutations for us to delineate all of them.

ARITHMETIC ARRAY OPERATORS

The operators illustrated in comments in lines 26 through 31 are not available as a part of Ada, but they can be made available to your programs by use of operator overloading. This will be illustrated in the next example program.

BOOLEAN ARRAY OPERATORS

Two of the BOOLEAN arrays are assigned some values using positional aggregates in lines 33 and 34, then used in lines 36 through 44 as complete arrays. The logical operators are used in much the same way that the comparison operators were used previously in this file. Note that these operators result in another array, each element being the result of the logical operation of the corresponding

Page 38: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

elements in the compared arrays. The comparison operators are available with boolean arrays in a manner similar to that with other scalar arrays, only a single BOOLEAN type result value is returned. Compile and run this program after you understand the new material presented.

HOW TO WRITE THE ARITHMETIC ARRAY OPERATORS

Example program ------> e_c19_p4.ada

-- Chapter 19 - Program 4with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure ArrayOp2 is

type ARY_INT is array(1..6) of INTEGER;

Crowd, Group1, Group2 : ARY_INT;

function "+"(In_Array1, In_Array2 : ARY_INT) return ARY_INT is Temp_Array : ARY_INT; begin for Index in ARY_INT'RANGE loop Temp_Array(Index) := In_Array1(Index) + In_Array2(Index); end loop; return Temp_Array; end "+";

function "mod"(In_Array1, In_Array2 : ARY_INT) return ARY_INT is Temp_Array : ARY_INT; begin for Index in ARY_INT'RANGE loop Temp_Array(Index) := In_Array1(Index) mod In_Array2(Index); end loop; return Temp_Array; end "mod";

begin

Group1 := (12, 17, -1, 3, -100, 5); Group2 := (13, -2, 22, 1, 1242, -12);

Crowd := Group1 + Group2; for Index in ARY_INT'RANGE loop Put(Group1(Index), 6); Put(Group2(Index), 6); Put(Crowd(Index), 6); New_Line; end loop;

-- Crowd := Group1 - Group2;-- Crowd := Group1 * Group2;-- Crowd := Group1 / Group2; Crowd := Group1 mod Group2;-- Crowd := Group1 rem Group2;

end ArrayOp2;

-- Result of execution

-- 12 13 25

Page 39: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- 17 -2 15-- -1 22 21-- 3 1 4-- -100 1242 1142-- 5 -12 -7

The example program named e_c19_p4.ada illustrates how to write the arithmetic array operators. This is actually an overloading of the usual arithmetic operators. Overloading these operators is nothing new, you have been doing it all through this tutorial, because the plus sign, for example, has been available for adding integer types, as well as fixed and floating point types. There is no reason the plus sign could not be used to add arrays, element by element, and that is exactly what we will illustrate here.

The "+" function is listed in lines 11 through 18 and is a very simple function, with no difficult code. The name of the function is the unusual thing about this function, its name being "+". This is the usual Ada method of overloading operators which we have illustrated before in this tutorial. After the function is defined, it is called by using an infix notation, as illustrated in line 34 of the program where all 6 elements of Group1 are added to the corresponding elements of Group2, and the 6 results being assigned to the 6 elements of Crowd. The results of the addition are displayed in lines 35 through 40 to show that all elements were summed.

In a similar manner, the function named "mod" is used to provide an infix notation for the modulo operator, and is called in line 45. The other four arithmetic operators are not defined here since they are so similar to the two which are illustrated. The overloading of the mod operator in this way should indicate to you why it is important to think of mod as an operator rather than a subprogram call.

OVERLOADING RULES

There are two rules which must be considered when overloading operators, the first being that you are permitted as many overloadings for a given operator as you desire, provided that the parameters of the inputs and result have unique types for each of the overloadings. This is because the system uses the types of the parameters and the type of the result to determine which overloading you wish to use each time you use the operator. This implies, correctly, that you cannot redefine the use of an existing operator for a given type.

The second rule is simple. You can overload existing Ada operators, but you cannot define an infix operator that is not predefined in Ada. Be sure to compile and execute this program.

UNARY OPERATOR OVERLOADING

Example program ------> e_c19_p5.ada

-- Chapter 19 - Program 5

procedure UnaryOp is

type ARY_INT is array(1..6) of INTEGER;

Crowd, Group1, Group2 : ARY_INT;

function "+"(In_Array1, In_Array2 : ARY_INT) return ARY_INT is Temp_Array : ARY_INT; begin for Index in ARY_INT'RANGE loop Temp_Array(Index) := In_Array1(Index) + In_Array2(Index); end loop; return Temp_Array; end "+";

Page 40: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

function "-"(In_Array1, In_Array2 : ARY_INT) return ARY_INT is Temp_Array : ARY_INT; begin for Index in ARY_INT'RANGE loop Temp_Array(Index) := In_Array1(Index) - In_Array2(Index); end loop; return Temp_Array; end "-";

function "+"(In_Array : ARY_INT) return ARY_INT is begin return In_Array; end "+";

function "-"(In_Array : ARY_INT) return ARY_INT is Temp_Array : ARY_INT; begin for Index in ARY_INT'RANGE loop Temp_Array(Index) := - In_Array(Index); end loop; return Temp_Array; end "-";

begin

Group1 := (12, 17, -1, 3, -100, 5); Group2 := (13, -2, 22, 1, 1242, -12);

Crowd := Group1 + Group2; Crowd := Group1 - Group2; Crowd := +Group1; Crowd := -Group1; Crowd := (Group1 + Group2) - (-Group1 + Group2);

end UnaryOp;

-- Result of execution

-- (There is no output from this program)

Examine the program named e_c19_p5.ada for an example of overloading the unary operators "+" and "-". The function in lines 27 through 30 overloads the "+" operator for the array defined and is of little interest since nothing is really accomplished here. The function in lines 32 through 39 overloads the "-" operator and negates each element of the array. It may seem silly to even bother with overloading the "+" operator, but it is illustrated here and can be used to advantage when we come to a study of generic packages. It may be necessary to allow use of such a seemingly silly construct in order to write a general purpose generic package.

Be sure to compile and run this program and see that the illustrated overloadings work as we have stated.

OPERATOR HIDING

In all of the overloadings we have examined, we were careful to use overloadings that introduced new type combinations so there was never an instance of hiding another subprogram. If the type combinations are already overloaded in a global manner when a further overloading is declared at a

Page 41: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

lower level of nesting, the subprogram called by the global combination will be hidden and the nested subprogram will be called each time. Even though care must be exercised when overloading operators or identifiers, do not fear use of this powerful technique made available to you by the designers of Ada.

PROGRAMMING EXERCISES

1. Modify the program named e_c19_p2.ada to output the name of each day as an enumerated output along with the number of hours worked each day.(Solution)

-- Chapter 19 - Programming exercise 1with Ada.Text_IO, Ada.Float_Text_IO;use Ada.Text_IO, Ada.Float_Text_IO;

procedure CH19_1 is

type DAY is (MON,TUE,WED,THU,FRI,SAT,SUN);

package Enum_IO is new Ada.Text_IO.Enumeration_IO(DAY); use Enum_IO;

Hours : array(DAY) of FLOAT; Total_Hours : FLOAT; Today : DAY;

begin for Today in MON..FRI loop Hours(Today) := 8.0; end loop;

Hours(SAT) := 4.0; Hours(SUN) := 0.0;

Total_Hours := 0.0; for Today in DAY loop Total_Hours := Total_Hours + Hours(Today); Put(Hours(Today), 4, 2, 0); Put(" hours were worked on "); Put(Today); New_Line; end loop;

Put("Total hours for the week ="); Put(Total_Hours, 8, 2, 0); New_Line;

end CH19_1;

-- Result of Execution

-- 8.00 hours were worked on MON-- 8.00 hours were worked on TUE-- 8.00 hours were worked on WED-- 8.00 hours were worked on THU-- 8.00 hours were worked on FRI-- 4.00 hours were worked on SAT-- 0.00 hours were worked on SUN-- Total hours for the week = 44.00

Page 42: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

2. Write functions for any two of the remaining four arithmetic operators in the example program named e_c19_p4.ada, and test the two new functions.(Solution)

-- Chapter 19 - Programming exercise 2with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure CH19_2 is

type ARY_INT is array(1..6) of INTEGER;

Crowd, Group1, Group2 : ARY_INT;

function "+"(In_Array1, In_Array2 : ARY_INT) return ARY_INT is Temp_Array : ARY_INT; begin for Index in ARY_INT'RANGE loop Temp_Array(Index) := In_Array1(Index) + In_Array2(Index); end loop; return Temp_Array; end "+";

function "-"(In_Array1, In_Array2 : ARY_INT) return ARY_INT is Temp_Array : ARY_INT; begin for Index in ARY_INT'RANGE loop Temp_Array(Index) := In_Array1(Index) - In_Array2(Index); end loop; return Temp_Array; end "-";

function "*"(In_Array1, In_Array2 : ARY_INT) return ARY_INT is Temp_Array : ARY_INT; begin for Index in ARY_INT'RANGE loop Temp_Array(Index) := In_Array1(Index) * In_Array2(Index); end loop; return Temp_Array; end "*";

function "/"(In_Array1, In_Array2 : ARY_INT) return ARY_INT is Temp_Array : ARY_INT; begin for Index in ARY_INT'RANGE loop Temp_Array(Index) := In_Array1(Index) / In_Array2(Index); end loop; return Temp_Array; end "/";

function "mod"(In_Array1, In_Array2 : ARY_INT) return ARY_INT is Temp_Array : ARY_INT; begin for Index in ARY_INT'RANGE loop Temp_Array(Index) := In_Array1(Index) mod In_Array2(Index); end loop; return Temp_Array; end "mod";

function "rem"(In_Array1, In_Array2 : ARY_INT) return ARY_INT is Temp_Array : ARY_INT; begin

Page 43: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

for Index in ARY_INT'RANGE loop Temp_Array(Index) := In_Array1(Index) rem In_Array2(Index); end loop; return Temp_Array; end "rem";

begin

Group1 := (12, 17, -1, 3, -100, 5); Group2 := (13, -2, 22, 1, 124, -12);

Crowd := Group1 + Group2; for Index in ARY_INT'RANGE loop Put(Group1(Index), 6); Put(Group2(Index), 6); Put(Crowd(Index), 6); New_Line; end loop;

Crowd := Group1 - Group2; Crowd := Group1 * Group2; Crowd := Group1 / Group2; Crowd := Group1 mod Group2; Crowd := Group1 rem Group2;

end CH19_2;

-- Result of execution

-- 12 13 25-- 17 -2 15-- -1 22 21-- 3 1 4-- -100 124 24-- 5 -12 -7

-- Note; The fifth value of Group2 was changed to prevent a-- numeric_error on a small machine.

Page 44: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Ada Tutorial - Chapter 20

ADVANCED RECORD TOPICS

A RECORD WITH A DISCRIMINANT

Example program ------> e_c20_p1.ada

-- Chapter 20 - Program 1with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Discrim1 is

type SQUARE is array(INTEGER range <>, INTEGER range <>) of INTEGER;

type LINEAR_TYPE is array(INTEGER range <>) of POSITIVE;

type STUFF(List_Size : POSITIVE) is record Matrix : SQUARE(1..List_Size, 1..List_Size); Elements : INTEGER := List_Size * List_Size; Linear : LINEAR_TYPE(1..List_Size); Number : INTEGER := List_Size; end record;

type ANOTHER_STUFF is new STUFF;

subtype STUFF_5 is STUFF(5);

Data_Store : STUFF(5); Big_Store : STUFF(12); Extra_Store : ANOTHER_STUFF(5); More_Store : STUFF(5); Five_Store : STUFF_5; Name_Store : STUFF(List_Size => 5);

begin

for Index1 in Data_Store.Matrix'RANGE(1) loop Data_Store.Linear(Index1) := Index1; for Index2 in Data_Store.Matrix'RANGE(2) loop Data_Store.Matrix(Index1, Index2) := Index1 * Index2; end loop; end loop;

Five_Store := Data_Store; More_Store := Five_Store;

Put("The number of elements in More_Store.Matrix is"); Put(More_Store.Elements, 5); New_Line;

end Discrim1;

-- Result of execution

-- The number of elements in More_Store.Matrix is 25

Page 45: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Examine the file named e_c20_p1.ada for our first example of a record with a discriminant. It will take a little time and study before we get to the discriminant and what it does, but we will take it a step at a time.

We begin by defining an unconstrained two dimensional array type in line 7 of the declaration part, and an unconstrained one dimensional array type in line 10. Next we define a record type, beginning in line 12, with a discriminant, the discriminant being a variable named List_Size which is of type POSITIVE. The record is composed of four fields, each of which is defined in part by the discriminant. The variable named Matrix is a square array whose size is given by the value of the discriminant, while Elements is initialized to the square of the discriminant. Likewise, the other two fields are defined as a function of the discriminant. Keep in mind that the discriminant does not yet have a value, it is only used as a part of the pattern of the record.

For later use, we define a derived type in line 20, and a subtype in line 22. The subtype is defined as being of type STUFF but with the discriminant being fixed at 5. We will have more to say about these two types later.

WE NEED TO DEFINE SOME DATA NOW

In line 24, we declare a variable named Data_Store to be of type STUFF with the discriminant set equal to 5. Therefore, the Matrix variable which is a part of the record named Data_Store has two subscripts, and both cover a range of 1 through 5. The variable named Elements will be initialized to the square of 5, and the other fields will likewise be defined. The variable Big_Store will have larger arrays, and the value of its subfield, named Elements, will be initialized to a value of the square of 12. Since these two variables have different numbers of elements, they are not assignment compatible, nor can they be compared for equality or inequality.

The variable Extra_Store is declared as being of type ANOTHER_STUFF with a discriminant of 5, but since the types are different, this variable is not assignment compatible with the first variable named Data_Store. More_Store is declared as being of type STUFF with a discriminant of 5, so it is assignment compatible with Data_Store. Five_Store, because it is a subtype of STUFF, and its discriminant is 5, as defined in the subtype declaration, is assignment compatible with Data_Store. Finally, it should be clear that the last example, Name_Store, is assignment compatible with Data_Store, since its only difference is that it uses the named method of discriminant selection. This is a hint to you that additional discriminants can be used, and there is actually no limit to the number that can be a part of a discriminated record type. We will have an example program soon that has three discriminants.

WHO IS ASSIGNMENT COMPATIBLE WITH WHO?

As mentioned before, the variables named Data_Store, More_Store, Five_Store, and Name_Store, are all of the same type and can be freely assigned to each other. They can also be compared for equality or inequality with each other. The other variables are of different types and cannot be assigned as a complete record to each other, or compared for equality or inequality.

The discriminant, once declared, is considered to be a constant, and cannot be modified. The discriminant of the variable Data_Store is accessed in the program as Data_Store.List_Size for purposes of reading it. The executable part of the program should be clear. One of the declared Matrix variables is assigned values using the RANGE attribute for the loop limits. The entire record is then assigned to some additional record variables, and a single data point is displayed as an example.

When you understand the program, compile and execute it to prove to yourself that it works as shown. Notice that, as always, the elements of the record cannot be anonymous types but must be named.

HOW DO WE USE THE NEW RECORDS?

Example program ------> e_c20_p2.ada

Page 46: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Chapter 20 - Program 2with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Discrim2 is

type SQUARE is array(INTEGER range <>, INTEGER range <>) of INTEGER;

type LINEAR_TYPE is array(INTEGER range <>) of POSITIVE;

type STUFF(List_Size : POSITIVE) is record Matrix : SQUARE(1..List_Size, 1..List_Size); Elements : INTEGER := List_Size * List_Size; Linear : LINEAR_TYPE(1..List_Size); Number : INTEGER := List_Size; end record;

Data_Store : STUFF(5); Big_Store : STUFF(12);

function Add_Elements(In_Array : STUFF) return INTEGER is Total : INTEGER := 0; begin for Index1 in In_Array.Matrix'RANGE(1) loop for Index2 in In_Array.Matrix'RANGE(2) loop Total := Total + In_Array.Matrix(Index1, Index2); end loop; end loop; return Total; end Add_Elements;

procedure Set_To_Ones(Work_Array : in out STUFF) is begin for Index1 in Work_Array.Matrix'RANGE(1) loop for Index2 in Work_Array.Matrix'RANGE(2) loop Work_Array.Matrix(Index1, Index2) := 1; end loop; end loop; end Set_To_Ones;

begin

for Index1 in 1..Data_Store.List_Size loop Data_Store.Linear(Index1) := Index1; for Index2 in 1..Data_Store.List_Size loop Data_Store.Matrix(Index1, Index2) := Index1 * Index2; end loop; end loop;

Set_To_Ones(Big_Store);

Put("The total of Data_Store is"); Put(Add_Elements(Data_Store), 5); New_Line;

Put("The total of Big_Store is "); Put(Add_Elements(Big_Store), 5); New_Line;

end Discrim2;

Page 47: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Result of execution

-- The total of Data_Store is 225-- The total of Big_Store is 144

Examine the example program named e_c20_p2.ada for a few examples of how to use the discriminated record. The type declarations are identical to the last program, but only two records are declared this time, Data_Store and Big_Store, which are of different types because they have different discriminants.

The declaration part of the program has a function declaration and a procedure declaration added to it in this program. If you look closely, you will see that the type used for the formal variable in both subprograms is of the record type STUFF, but there is no discriminant defined for either. These are unconstrained records and add flexibility to the use of subprograms. The loops within the subprograms use limits that are dependent upon the limits of the actual type as defined in the calling program, and the subprograms can therefore be used for any record variable of type STUFF regardless of the value of the discriminant.

USE OF THE UNCONSTRAINED SUBPROGRAMS

The nested loop in lines 45 through 50, assigns the elements contained in the array variable Data_Store.Matrix to a multiplication table for later use. In line 52, we call the procedure Set_To_Ones with the record Big_Store to set all of its Matrix values to 1. Finally, we display the sums of all of the elements by calling the function Add_Elements once for each record. Even though the records are actually of different types, the function works correctly with both, because of the flexibility built into the function itself. Note that even though the record is unconstrained in each subprogram, it is constrained when the subprogram is called since the discriminant is constrained to the value of the actual parameter in the call.

Be sure to compile and execute this program and study the output until you are sure you understand the results.

A VARIABLE DISCRIMINANT

Example program ------> e_c20_p3.ada

-- Chapter 20 - Program 3with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Discrim3 is

type SQUARE is array(INTEGER range <>, INTEGER range <>) of INTEGER;

type LINEAR_TYPE is array(INTEGER range <>) of POSITIVE; subtype SMALL_POS is POSITIVE range 1..20;

type STUFF(List_Size : SMALL_POS := 2) is record Matrix : SQUARE(1..List_Size, 1..List_Size); Elements : INTEGER := List_Size * List_Size; Linear : LINEAR_TYPE(1..List_Size); Number : INTEGER := List_Size; end record;

Page 48: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Data_Store : STUFF(5); Big_Store : STUFF(12); Var_Store : STUFF;

function Add_Elements(In_Array : STUFF) return INTEGER is Total : INTEGER := 0; begin for Index1 in In_Array.Matrix'RANGE(1) loop for Index2 in In_Array.Matrix'RANGE(2) loop Total := Total + In_Array.Matrix(Index1, Index2); end loop; end loop; return Total; end Add_Elements;

procedure Set_To_Ones(Work_Array : in out STUFF) is begin for Index1 in Work_Array.Matrix'RANGE(1) loop for Index2 in Work_Array.Matrix'RANGE(2) loop Work_Array.Matrix(Index1, Index2) := 1; end loop; end loop; end Set_To_Ones;

begin

for Index1 in 1..Data_Store.List_Size loop Data_Store.Linear(Index1) := Index1; for Index2 in 1..Data_Store.List_Size loop Data_Store.Matrix(Index1, Index2) := Index1 * Index2; end loop; end loop;

Var_Store := Data_Store; Put("The total of Var_Store is "); Put(Add_Elements(Var_Store), 4); New_Line;

Set_To_Ones(Var_Store); Put("The total of Var_Store is "); Put(Add_Elements(Var_Store), 4); New_Line;

Set_To_Ones(Big_Store); Var_Store := Big_Store; Put("The total of Var_Store is "); Put(Add_Elements(Var_Store), 4); New_Line;

end Discrim3;

-- Result of execution

-- The total of Var_Store is 225-- The total of Var_Store is 25-- The total of Var_Store is 144

Page 49: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Examine the program named e_c20_p3.ada for an example of a discriminant that can be changed dynamically. This program is nearly identical to the last example program, but there are two very small changes. The discriminant is initialized to a value of 2 in line 14, which will be used for the discriminant if none is given in the variable declaration. The second change is illustrated in line 24, where the variable Var_Store is declared to be of type STUFF, and is defaulted to the initialization value of 2 for its discriminant. There is one other property that the variable named Var_Store has acquired, and that is the ability to have its discriminant changed to any legal value during execution of the program.

The two variables named Data_Store and Big_Store have their discriminants fixed at 5 and 12 respectively and cannot be changed during program execution.

HOW DO WE CHANGE THE DISCRIMINANT?

The discriminant can only be changed by changing the entire record in a single statement as is illustrated in line 55 where the entire record named Data_Store, with a discriminant of 5, is assigned to the variable Var_Store. The variable Var_Store can then be used anywhere it is legal to use a record of discriminant 5 as shown in lines 56 through 62. Note that prior to the assignment in line 55 the variable Var_Store can be used as a record with its discriminant set to 2 because that is the value of the default.

In line 66, the variable Var_Store is assigned the entire record of Big_Store which effectively changes it into a record variable with a discriminant of 12. The fact that it is changed is evidenced by the output which you can see after you compile and execute this program. Note that all of the values contained in Big_Store are copied into Var_Store. Be sure to compile and execute this program.

A MULTIPLE DISCRIMINANT

Example program ------> e_c20_p4.ada

-- Chapter 20 - Program 4with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Discrim4 is

type SQUARE is array(INTEGER range <>, INTEGER range <>) of INTEGER;

type LINEAR_TYPE is array(INTEGER range <>) of POSITIVE;

subtype SMALL_POS is POSITIVE range 1..20;

type STUFF(List_Length : SMALL_POS := 3; List_Width : SMALL_POS := 5; Linear_Size : SMALL_POS := 7) is record Matrix : SQUARE(1..List_Length, 1..List_Width); Elements : INTEGER := List_Length * List_Width; Linear : LINEAR_TYPE(1..Linear_Size); Number : INTEGER := Linear_Size; end record;

Data_Store : STUFF(5, 5, 5); Big_Store : STUFF(12, 12, 12); Different : STUFF(5, 7, 4); More_Store : STUFF(Linear_Size => 15, List_Length => 6, List_Width => 2); Variable : STUFF;

Page 50: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

function Add_Elements(In_Array : STUFF) return INTEGER is Total : INTEGER := 0; begin for Index1 in In_Array.Matrix'RANGE(1) loop for Index2 in In_Array.Matrix'RANGE(2) loop Total := Total + In_Array.Matrix(Index1, Index2); end loop; end loop; return Total; end Add_Elements;

procedure Set_To_Ones(Work_Array : in out STUFF) is begin for Index1 in Work_Array.Matrix'RANGE(1) loop for Index2 in Work_Array.Matrix'RANGE(2) loop Work_Array.Matrix(Index1, Index2) := 1; end loop; end loop; end Set_To_Ones;

begin

for Index1 in 1..Data_Store.List_Length loop Data_Store.Linear(Index1) := Index1; for Index2 in 1..Data_Store.List_Width loop Data_Store.Matrix(Index1, Index2) := Index1 * Index2; end loop; end loop; Variable := Data_Store; Put("The total of Variable is"); Put(Add_Elements(Variable), 6); New_Line;

Variable := More_Store; Set_To_Ones(Big_Store); Set_To_Ones(Different); Set_To_Ones(More_Store); Set_To_Ones(Variable);

Put("The total of Variable is"); Put(Add_Elements(Variable), 6); New_Line;

Variable := Different;

Put("The total of Variable is"); Put(Add_Elements(Variable), 6); New_Line;

end Discrim4;

-- Result of execution

-- The total of Variable is 225-- The total of Variable is 12-- The total of Variable is 35

Page 51: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Examining the example program named e_c20_p4.ada gives you an example of how to use a multiple discriminant in a record type. The first difference is in lines 14 through 16 where three discriminants are defined for the record type, and the next big difference is in lines 24 through 30 where 5 variables are declared with this type illustrating the various kinds of discriminant initialization described in this chapter.

Since we have studied all of these in the last few example programs, we will not elaborate on them, except to mention that the variable defined in line 30, named Variable, can be assigned the value of any of the other variables. It will then have the entire set of discriminants assigned to it that the assignment variable possesses and it will be assigned all of the current values stored in the source record. This is exactly the same as what was illustrated in the last program.

Be sure to compile and run this program after you understand the concepts it is meant to convey to you.

A VARIANT RECORD

Example program ------> e_c20_p5.ada

-- Chapter 20 - Program 5

procedure Variant1 is

type POWER is (GAS, STEAM, DIESEL, NONE);

type VEHICLE (Engine : POWER) is record Model_Year : INTEGER range 1888..1992; Wheels : INTEGER range 2..18; case Engine is when GAS => Cylinders : INTEGER range 1..16; when STEAM => Boiler_Size : INTEGER range 5..22; Coal_Burner : BOOLEAN; when DIESEL => Fuel_Inject : BOOLEAN; when NONE => Speeds : INTEGER range 1..15; end case; end record;

Ford : VEHICLE(GAS); Truck : VEHICLE(DIESEL); Schwinn : VEHICLE(NONE); Stanley : VEHICLE(STEAM);

begin

Ford.Model_Year := 1956; -- Component assignment Ford.Wheels := 4; Ford.Cylinders := 8;

Ford := (GAS, 1956, 4, 8); -- Positional aggregate assignment

-- Named aggregate assignment Ford := (Model_Year => 1956, Cylinders => 8, Engine => GAS, Wheels => 4);

Stanley.Model_Year := 1908; Stanley.Wheels := 4; Stanley.Boiler_Size := 21; Stanley.Coal_Burner := FALSE; -- Mixed aggregate assignment Stanley := (STEAM, 1908, 4, Coal_Burner => FALSE, Boiler_Size => 21);

Page 52: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Schwinn.Speeds := 10; Schwinn.Wheels := 2; Schwinn.Model_Year := 1985;

Truck.Model_Year := 1966; Truck.Wheels := 18; Truck.Fuel_Inject := TRUE;

end Variant1;

-- Result of Execution

-- (No results are output.)

Examine the file named e_c20_p5.ada for our first example of a variant record. A variant record must have a discriminant, by definition, since the discriminant will define which of the various variants each record variable will be composed of. If you are a Pascal programmer you will find the variant record to be much more confining than in Pascal where you can change the variant at will. If you remember that Ada is a very strongly typed language, and will accept no deviation from its defined standard in order to detect errors inadvertently introduced by the programmer, you will appreciate the terse definition and restrictions.

The discriminant for our example record is declared in line 5 as an enumerated type with four allowable values. A variable named Engine is declared, which is of type POWER, which will be used as the discriminant and two variables are declared as the first part of the record. The variant part of the record begins in line 11 with the reserved word case followed by the name of the discriminant variable, which is Engine in this example. The four variants are declared in much the same way as a normal case statement with variable declarations in place of the executable statements. There must be a clause listed for each possible value of the discriminant, and any number of variables may be defined for each, including none. If no variables are declared for a case, the reserved word null is used to indicate to the compiler that you really mean to include no variables there.

If there is a variant to the record, it must be the last part of the record with all common variables declared first. One or more of the variant parts of the record can have a variant part itself, provided it is the last part of the variant part. There is no defined limit to the nesting.

HOW DO WE USE THE VARIANT RECORD?

In line 20, we declare the variable Ford to be a record of type VEHICLE, and constrain it to be the GAS variant of the record. Because the Ford variable is constrained to the GAS variant, it can only use the variable declared as part of the GAS variant, and of course the two common variables. It would be illegal to assign data to the fields of the Ford variable which are declared in the other variants of the record type. Moreover, since the variable Ford has been assigned the discriminant value of GAS, it can never be changed, but is a constant. Likewise, the variable named Truck will always be a record of type VEHICLE with the variant DIESEL because that is what it is declared with. The same is true of the other two variables declared in lines 22 and 23. We will see in the next program, that it is possible to declare a variable in such a way that it can be modified dynamically to any of the variants during program execution.

HOW DO WE ASSIGN VALUES TO THE VARIABLES?

Lines 27 through 29 should be familiar to you since this is the method used to assign values to a

Page 53: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

record with no variant, which we studied earlier. Line 31 illustrates value assignment by using a positional aggregate notation, and line 34 illustrates assignment of values using the named aggregate notation. In both of these cases, all four fields must be named even if some are not changed. Even the invariant discriminant must be included in the aggregate, which seems to be somewhat of a nuisance, since it is a constant. The reasons for these last two rules are probably well founded and have to do with ease of writing a compiler, which is certainly no small job.

The statements in lines 37 through 40 assign values to each of the subfields of the record variable named Stanley, and the mixed aggregate assignment is illustrated in line 42, where all five variables are mentioned even though the discriminant is a constant. Finally, the Schwinn and Truck variables are assigned values in lines 45 through 51. Compile and execute this program to assure yourself that your compiler will indeed compile it correctly.

A VARIABLE VARIANT RECORD

Example program ------> e_c20_p6.ada

-- Chapter 20 - Program 6

procedure Variant2 is

type POWER is (GAS, STEAM, DIESEL, NONE);

type VEHICLE (Engine : POWER := NONE) is record Model_Year : INTEGER range 1888..1992; Wheels : INTEGER range 2..18; case Engine is when GAS => Cylinders : INTEGER range 1..16; when STEAM => Boiler_Size : INTEGER range 5..22; Coal_Burner : BOOLEAN; when DIESEL => Fuel_Inject : BOOLEAN; when NONE => Speeds : INTEGER range 1..15; end case; end record;

Ford, Truck, Schwinn : VEHICLE; Stanley : VEHICLE(STEAM);

begin

Ford := (GAS, 1956, 4, 8); Ford := (DIESEL, 1985, Fuel_Inject => TRUE, Wheels => 8);

Truck := (DIESEL, 1966, 18, TRUE); Truck.Model_Year := 1968; Truck.Fuel_Inject := FALSE;

Stanley.Model_Year := 1908; -- This is constant as STEAM Stanley.Wheels := 4; Stanley.Boiler_Size := 21; Stanley.Coal_Burner := FALSE;

Schwinn.Speeds := 10; -- This defaults to NONE Schwinn.Wheels := 2; Schwinn.Model_Year := 1985;

end Variant2;

Page 54: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Result of Execution

-- (No output when executed)

Examine the program named e_c20_p6.ada for an example of a variant record in which we can change the variant dynamically during program execution. A major difference here is that the discriminant is defaulted to the value listed in line 7, namely the value of NONE. If no variant is declared, it is defaulted to NONE in the declarations as is done in line 20, where three variable records are declared using the default value of the discriminant. The variable Stanley is once again declared to be of the variant STEAM, and this will remain constant throughout the execution of the program, because any variable declared with a discriminant value cannot have the value of its discriminant changed dynamically but is a constant, although the individual elements of the record can be changed.

NOW TO USE SOME OF THE NEW VARIABLES

In line 25, the variable Ford is assigned data such that it is of the GAS variant, using the positional aggregate notation, and is redefined in the next statement to be of the DIESEL variant, by using the mixed aggregate notation. This is done to illustrate to you that it is possible to change the variant of a variable if it was declared with the default variant. In line 28, the variable Truck is assigned the DIESEL variant with the positional aggregate notation, and two of the fields are changed in the next two statements.

Any of the fields can be changed individually with the exception of the discriminant variable, which can only be changed by use of an aggregate in which all values are listed. Remember that this is the aggravating part of this construct, that all of the fields must be mentioned in every record aggregate. Even though you can change individual values of the record, you are limited to using those variables that are part of the current variant. Pascal allows you to use variable names of other variants but Ada will not permit this. You are permitted to use the positional, named, or mixed aggregate notation, however. Be sure to compile and execute this program after you understand the concepts.

OPERATOR OVERLOADING

Example program ------> e_c20_p7.ada

-- Chapter 20 - Program 7with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Infix is

type THREE_INTS is record Length, Height, Width : INTEGER; end record;

Big, Medium, Sum : THREE_INTS;

function "+"(Record1, Record2 : THREE_INTS) return THREE_INTS is Temp : THREE_INTS; begin Temp.Length := Record1.Length + Record2.Length; Temp.Height := Record1.Height + Record2.Height; Temp.Width := Record1.Width + Record2.Width; return Temp; end "+";

begin

Page 55: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Big.Length := 10 + 2; Big.Height := 22; Big.Width := 17; Medium.Length := 5; Medium.Height := 7; Medium.Width := 4; Sum := Big + Medium; Put("The sum is"); Put(Sum.Length, 4); Put(Sum.Height, 4); Put(Sum.Width, 4); New_Line;end Infix;

-- Result of Execution

-- The sum is 17 29 21

The example program named e_c20_p7.ada could be placed in several different places in this tutorial, because it doesn't really fit anywhere too well. Since one of the most advantageous places to use operator overloading is with record type variables, this seemed like a good place for it. The program itself is very simple, but the concept is potentially very powerful.

We first define a record composed of three simple variables all being of type INTEGER, and use this definition to declare three variables of this type. The next declaration is of a function named "+", which we can define to do anything we wish it to do. In this case we input two variables of the record type THREE_INTS and return one record of the same type. Within the function, we add all three fields of one variable to the corresponding fields of the other variable, and return the record consisting of the sum of the two input records. To make it even more convenient, Ada allows you to use the overloaded operator in an infix notation as illustrated in line number 30 of the program where two records are added together and assigned to the record variable named Sum. This line is actually a call to the function we defined earlier and named "+".

It could be a bit confusing if you consider the addition in line 24 where the usual addition operator is used. Ada will decide which + operator to use based on the type of the constants or variables to be added together. If you were to try to use this operator on a record of some other type, such as the VEHICLE record from the last program, the system would generate a type error during compilation. As mentioned previously, it is only possible to overload existing operators. You cannot define a new operator, such as &=, and use it for some operation.

Recall the chapter named Advanced Array Topics where we overloaded the "+" operator in the example program named e_c19_p4.ada. Both overloadings of this operator could be included in a single program and the system would be able to find the correct overloading for each use by checking the types used. Be sure to compile and execute this program even though it has no output.

PROGRAMMING EXERCISES

1. Add some output statements to e_c20_p5.ada to display some of the results.(Solution) -- Chapter 20 - Programming exercise 1with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure CH20_1 is

type POWER is (GAS, STEAM, DIESEL, NONE);

Page 56: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

package Enum_IO is new Ada.Text_IO.Enumeration_IO(POWER); use Enum_IO;

type VEHICLE (Engine : POWER) is record Model_Year : INTEGER range 1888..1992; Wheels : INTEGER range 2..18; case Engine is when GAS => Cylinders : INTEGER range 1..16; when STEAM => Boiler_Size : INTEGER range 5..22; Coal_Burner : BOOLEAN; when DIESEL => Fuel_Inject : BOOLEAN; when NONE => Speeds : INTEGER range 1..15; end case; end record;

Ford : VEHICLE(GAS); Truck : VEHICLE(DIESEL); Schwinn : VEHICLE(NONE); Stanley : VEHICLE(STEAM);

begin

Ford.Model_Year := 1956; -- Component assignment Ford.Wheels := 4; Ford.Cylinders := 8;

Ford := (GAS, 1956, 4, 8); -- Positional aggregate assignment

-- Named aggregate assignment Ford := (Model_Year => 1956, Cylinders => 8, Engine => GAS, Wheels => 4);

Stanley.Model_Year := 1908; Stanley.Wheels := 4; Stanley.Boiler_Size := 21; Stanley.Coal_Burner := FALSE; -- Mixed aggregate assignment Stanley := (STEAM, 1908, 4, Coal_Burner => FALSE, Boiler_Size => 21);

Schwinn.Speeds := 10; Schwinn.Wheels := 2; Schwinn.Model_Year := 1985;

Truck.Model_Year := 1966; Truck.Wheels := 18; Truck.Fuel_Inject := TRUE;

Put("Stanley engine type is "); Put(Stanley.Engine); Put(", and has"); Put(Stanley.Wheels,3); Put_Line(" wheels.");

Put("The truck is a"); Put(Truck.Model_Year,5); Put(" and has"); Put(Truck.Wheels,3); Put_Line(" wheels.");

end CH20_1;

Page 57: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Result of Execution

-- Stanley engine type is STEAM, and has 4 wheels.-- The truck is a 1966 and has 18 wheels.

2. Try to change the variant of Stanley in the same way we changed the variant of Ford in the example program named e_c20_p6.ada to see what kind of error messages you get.(Solution)

-- Chapter 20 - Programming exercise 2with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure CH20_2 is

type POWER is (GAS, STEAM, DIESEL, NONE);

type VEHICLE (Engine : POWER := NONE) is record Model_Year : INTEGER range 1888..1992; Wheels : INTEGER range 2..18; case Engine is when GAS => Cylinders : INTEGER range 1..16; when STEAM => Boiler_Size : INTEGER range 5..22; Coal_Burner : BOOLEAN; when DIESEL => Fuel_Inject : BOOLEAN; when NONE => Speeds : INTEGER range 1..15; end case; end record;

Ford, Truck, Schwinn : VEHICLE; Stanley : VEHICLE(STEAM);

begin

Ford := (GAS, 1956, 4, 8); Ford := (DIESEL, 1985, Fuel_Inject => TRUE, Wheels => 8);

Truck := (DIESEL, 1966, 18, TRUE); Truck.Model_Year := 1968; Truck.Fuel_Inject := FALSE;

Stanley.Model_Year := 1908; -- This is constant as STEAM Stanley.Wheels := 4; Stanley.Boiler_Size := 21; Stanley.Coal_Burner := FALSE;

Stanley := (DIESEL, 1966, 18, TRUE); -- This is an error

Schwinn.Speeds := 10; -- This defaults to NONE Schwinn.Wheels := 2; Schwinn.Model_Year := 1985;

end CH20_2;

Page 58: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Result of Execution

-- (You will probably get a compiler warning stating that there-- is a constraint error. During runtime, you will get the error-- stating that there is a length or discriminant clash.)

3. Add another overloading function to e_c20_p7.ada which uses the "+" operator to add all six elements of two records together and return a single INTEGER type variable. The system can tell which overloading you wish to use by the return type associated with the function call.(Solution)

-- Chapter 20 - Programming exercise 3with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure CH20_3 is

type THREE_INTS is record Length, Height, Width : INTEGER; end record;

Big, Medium, Sum : THREE_INTS; Grand_Total : INTEGER;

function "+"(Record1, Record2 : THREE_INTS) return THREE_INTS is Temp : THREE_INTS; begin Temp.Length := Record1.Length + Record2.Length; Temp.Height := Record1.Height + Record2.Height; Temp.Width := Record1.Width + Record2.Width; return Temp; end "+";

function "+"(Record1, Record2 : THREE_INTS) return INTEGER is Total : INTEGER; begin Total := Record1.Length + Record2.Length + Record1.Height + Record2.Height + Record1.Width + Record2.Width; return Total; end "+";

begin Big.Length := 10 + 2; Big.Height := 22; Big.Width := 17; Medium.Length := 5; Medium.Height := 7; Medium.Width := 4; Sum := Big + Medium; Put("The sum is"); Put(Sum.Length, 5); Put(Sum.Height, 5); Put(Sum.Width, 5); New_Line;

Page 59: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Grand_Total := Big + Medium; Put("The grand total is "); Put(Grand_Total, 5); New_Line;

end CH20_3;

-- Result of Execution

-- The sum is 17 29 21-- The grand total is 67

Page 60: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Ada Tutorial - Chapter 21

ADVANCED PACKAGES & PRIVATE TYPES

THIS IS FOR LARGE PROJECTS

The material in this chapter is used for large projects when there are several programmers working together as a team. In such a case, there is a demand for good communication between the various programmers, regardless of what programming language is used. If Ada is the language chosen for the project, and if the principles set down in this chapter are carefully followed, the amount of communication required can be minimized. Of course, these techniques can be used by a lone programmer to improve the quality of his code also, especially if it is a large program.

TWO GOOD PROGRAMMERS

In this chapter, we will assume we have a project being developed by two programmers, and that they are both sharp and well versed in how to develop quality software. We will follow along with the example programs as they develop a simple system to add or subtract groups of three numbers. One has been assigned the responsibility to write a package that the other programmer can use to add or subtract groups of three numbers of arbitrary structure.

This is a trivial problem, of course, but will illustrate the concept of information hiding, which is so necessary on a large project.

THE FIRST SOLUTION

Example program ------> e_c21_p1.ada

-- Chapter 21 - Program 1

-- This package uses a data structure composed of three INTEGER-- variables. It allow the user to add two structures, component-- by component, or subtract component by component. Provision is-- also made to build a structure from three numbers, or decompose-- a structure into its components.

package Three is

type DATA_STRUCTURE is record Value1 : INTEGER; Value2 : INTEGER; Value3 : INTEGER; end record;

function "+"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE;function "-"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE;function Build_Structure(Val1, Val2, Val3 : INTEGER) return DATA_STRUCTURE;procedure Decompose(Data1 : DATA_STRUCTURE; Val1, Val2, Val3 : out INTEGER);end Three;

package body Three is

function "+"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE isTemp : DATA_STRUCTURE;begin Temp.Value1 := Data1.Value1 + Data2.Value1; Temp.Value2 := Data1.Value2 + Data2.Value2;

Page 61: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Temp.Value3 := Data1.Value3 + Data2.Value3; return Temp;end "+";

function "-"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE isTemp : DATA_STRUCTURE;begin Temp.Value1 := Data1.Value1 - Data2.Value1; Temp.Value2 := Data1.Value2 - Data2.Value2; Temp.Value3 := Data1.Value3 - Data2.Value3; return Temp;end "-";

function Build_Structure(Val1, Val2, Val3 : INTEGER) return DATA_STRUCTURE isTemp : DATA_STRUCTURE;begin Temp.Value1 := Val1; Temp.Value2 := Val2; Temp.Value3 := Val3; return Temp;end Build_Structure;

procedure Decompose(Data1 : DATA_STRUCTURE; Val1, Val2, Val3 : out INTEGER) isbegin Val1 := Data1.Value1; Val2 := Data1.Value2; Val3 := Data1.Value3;end Decompose;

end Three;

-- This program exercises the package Three as an illustration.

with Ada.Text_IO; use Ada.Text_IO;with Three; use Three;

procedure NoPrivat is

My_Data, Extra_Data : DATA_STRUCTURE;

begin

My_Data := Build_Structure(3, 7, 13); Extra_Data := Build_Structure(-4, 77, 0); My_Data := My_Data + Extra_Data;

if My_Data /= Extra_Data then Put_Line("The two structures are not equal."); end if;

My_Data := Extra_Data;

if My_Data = Extra_Data then Put_Line("The two structures are equal now.");

Page 62: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

end if;

My_Data.Value1 := My_Data.Value1 + 13;

end NoPrivat;

-- Result of execution

-- The two structures are not equal.-- The two structures are equal now.

The example file named e_c21_p1.ada illustrates the first attempt to solve the problem, however it does not practice any information hiding. Lines 1 through 69 represent the work of the package writer, and lines 74 through 101 are written by the user of the package. The package writer used a record to store the three numbers and because he declared the record in the specification package, he gave the user full access to the record for his own use.

The package writer, by providing three functions and a procedure, gave the user the ability to add the corresponding elements, subtract the corresponding elements, and two additional capabilities. The function named Build_Structure allows the user to build a new structure given the individual components, and the procedure named Decompose, allows the user to separate the elements into their respective values. We might add that this package is a result of a meeting between the two programmers where they agreed on what the system must do and what the interface between them must be. The package specification is intended to be the only information needed to define the interface between the programmers. Notice the comments in lines 3 through 7 that define clearly and completely what the package does.

The user wished to change one value of the variable My_Data, and since he had the entire structure available, he cheated in line 99 by circumventing the available structure handling method provided by the package writer. He feels he saved a few instructions and a little execution time, so the little bit of cheating is justified. Line 99 is perfectly legal, but could cause a problem in this instance due to the immense size of the project. Recall that he had previously agreed to use only the functionality provided by the package writer because the package writer is supplying the same package to another large project and must maintain conformity with both users.

A PROBLEM DEVELOPS

The package writer, for reasons of efficiency and conformity to the other project, decides to change the way he stores the data from a three element record, to a three element array. With this modification in place and tested, the user suddenly finds that his program will no longer compile, even though nothing has been changed in his program. The only problem is with line 99, which has been working just fine for months now, but as we stated earlier, is actually cheating on the interface agreed upon by the user and the package writer. Information hiding would have prevented this problem, which in a real world system could cause hundreds of compiler errors following a seemingly minor change in one of the packages.

In this case, the user cheated and resulted ultimately in a rather large problem. We need to prevent him from cheating. Of course we could make a project rule that says "no cheating", but we will shortly define a way to let the compiler enforce the rule for us. Using the word "cheating" is simply to enhance the story. Actually, it is more of an oversight on the part of the user. While debugging after a 14 hour day of programming, it is easy to forget the previous agreement on the interface and use some of the data directly as is done in line 99. We need a way for the compiler to remind us not to do that.

Page 63: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

It should be obvious to you that in a real-life large system, the called package and the calling package would not be in the same file, but would be separately compiled. They were combined to make it easier for you to compile and execute, which you should do at this point.

A BETTER WAY, THE PRIVATE TYPE

Example program ------> e_c21_p2.ada

-- Chapter 21 - Program 2

-- This package uses a data structure composed of three INTEGER-- variables. It allow the user to add two structures, component-- by component, or subtract component by component. Provision is-- also made to build a structure from three numbers, or decompose-- a structure into its components.

package Three is

type DATA_STRUCTURE is private;

function "+"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE;function "-"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE;function Build_Structure(Val1, Val2, Val3 : INTEGER) return DATA_STRUCTURE;procedure Decompose(Data1 : DATA_STRUCTURE; Val1, Val2, Val3 : out INTEGER);

private type DATA_STRUCTURE is record Value1 : INTEGER; Value2 : INTEGER; Value3 : INTEGER; end record;end Three;

package body Three is

function "+"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE isTemp : DATA_STRUCTURE;begin Temp.Value1 := Data1.Value1 + Data2.Value1; Temp.Value2 := Data1.Value2 + Data2.Value2; Temp.Value3 := Data1.Value3 + Data2.Value3; return Temp;end "+";

function "-"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE isTemp : DATA_STRUCTURE;begin Temp.Value1 := Data1.Value1 - Data2.Value1; Temp.Value2 := Data1.Value2 - Data2.Value2; Temp.Value3 := Data1.Value3 - Data2.Value3; return Temp;end "-";

function Build_Structure(Val1, Val2, Val3 : INTEGER) return DATA_STRUCTURE isTemp : DATA_STRUCTURE;

Page 64: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

begin Temp.Value1 := Val1; Temp.Value2 := Val2; Temp.Value3 := Val3; return Temp;end Build_Structure;

procedure Decompose(Data1 : DATA_STRUCTURE; Val1, Val2, Val3 : out INTEGER) isbegin Val1 := Data1.Value1; Val2 := Data1.Value2; Val3 := Data1.Value3;end Decompose;

end Three;

-- This program exercises the package Three as an illustration.

with Ada.Text_IO; use Ada.Text_IO;with Three; use Three;

procedure Privat1 is

My_Data, Extra_Data : DATA_STRUCTURE; Temp : DATA_STRUCTURE;

begin

My_Data := Build_Structure(3, 7, 13); Extra_Data := Build_Structure(-4, 77, 0); My_Data := My_Data + Extra_Data;

if My_Data /= Extra_Data then Put_Line("The two structures are not equal."); end if;

My_Data := Extra_Data;

if My_Data = Extra_Data then Put_Line("The two structures are equal now."); end if;

-- The following line is illegal with the private type.-- My_Data.Value1 := My_Data.Value1 + 13;

Temp := Build_Structure(13, 0, 0); My_Data := My_Data + Temp;

end Privat1;

-- Result of execution

-- The two structures are not equal.-- The two structures are equal now.

Page 65: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Examine the program named e_c21_p2.ada for an example of a private type which forces the user to follow the rules. In the package specification, the type DATA_STRUCTURE is defined to be of type private, a reserved word in Ada, which means that the type name is available for the user's use in declaring variables of this type, but the structure of the type is not available to him. The actual type is defined at the end of the package specification following another use of the reserved word private.

Because the type is private, the user can make no use of the structure, even though he may be able to see the structure, as you can at this moment. If the package writer wished to do so, he could actually remove the type definition from the listing and give the resulting listing to the user, but the fact that it is private is as good as physically hiding it from the user.

Because the type is private, the user has the ability to compare two variables of that type for either equality or inequality, and he can assign another variable or constant of that type to a variable of the same type. No other operations on a variable of this private type are available to him, except for those specifically defined in the package specification itself.

NO TRICKS THIS TIME

The trick, in line 99 of the last program is therefore illegal, and will result in a compile error. If the user needs the ability to do some operation on this type variable, he must ask the package writer to provide it for him. The package writer has the ability to do anything with the components of the structure within the package itself, and define any new procedures or functions necessary for the intelligent use of the type. In order for the user to add 13 to one member, it is now necessary to declare a temporary record variable, load it with all fields, and add the entire temporary variable of the type DATA_STRUCTURE to the variable. This is illustrated in lines 106 and 107. It is rather obvious that these two statements could be combined and the variable named Temp not needed, but this was done for clarity.

WHERE IS THIS TYPE AVAILABLE?

Following the partial declaration of the record type in line 11, it is available for use in the remainder of the specification package, and throughout the entire package body. It is important to remember that the private type is available in these areas just as if it were not private. It is only treated in a different manner with respect to the using program. We actually have two different views of the private data, a partial view available to any outside calling program, and a full view available to the implementation or body of the defining class. Actually, only the partial view is available in the specification prior to the private section, and the full view is available within the private section following the complete definition of the record.

The remaining details of the program should be simple for you to understand, compile and execute. Note that the output is identical to that from the previous example program.

A NEW PACKAGE

Example program ------> e_c21_p3.ada

-- Chapter 21 - Program 3

-- This package uses a data structure composed of three INTEGER-- variables. It allow the user to add two structures, component-- by component, or subtract component by component. Provision is-- also made to build a structure from three numbers, or decompose-- a structure into its components.

package Three is

type DATA_STRUCTURE is private;

Page 66: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

function "+"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE;function "-"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE;function Build_Structure(Val1, Val2, Val3 : INTEGER) return DATA_STRUCTURE;procedure Decompose(Data1 : DATA_STRUCTURE; Val1, Val2, Val3 : out INTEGER);

private type DATA_STRUCTURE is array(1..3) of INTEGER;end Three;

package body Three is

function "+"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE isTemp : DATA_STRUCTURE;begin Temp(1) := Data1(1) + Data2(1); Temp(2) := Data1(2) + Data2(2); Temp(3) := Data1(3) + Data2(3); return Temp;end "+";

function "-"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE isTemp : DATA_STRUCTURE;begin Temp(1) := Data1(1) - Data2(1); Temp(2) := Data1(2) - Data2(2); Temp(3) := Data1(3) - Data2(3); return Temp;end "-";

function Build_Structure(Val1, Val2, Val3 : INTEGER) return DATA_STRUCTURE isTemp : DATA_STRUCTURE;begin Temp(1) := Val1; Temp(2) := Val2; Temp(3) := Val3; return Temp;end Build_Structure;

procedure Decompose(Data1 : DATA_STRUCTURE; Val1, Val2, Val3 : out INTEGER) isbegin Val1 := Data1(1); Val2 := Data1(2); Val3 := Data1(3);end Decompose;

end Three;

-- This program exercises the package Three as an illustration.

Page 67: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

with Ada.Text_IO; use Ada.Text_IO;with Three; use Three;

procedure Privat2 is

My_Data, Extra_Data : DATA_STRUCTURE; Temp : DATA_STRUCTURE;

begin

My_Data := Build_Structure(3, 7, 13); Extra_Data := Build_Structure(-4, 77, 0); My_Data := My_Data + Extra_Data;

if My_Data /= Extra_Data then Put_Line("The two structures are not equal."); end if;

My_Data := Extra_Data;

if My_Data = Extra_Data then Put_Line("The two structures are equal now."); end if;

-- The following line is illegal with the private type.-- My_Data.Value1 := My_Data.Value1 + 13;

Temp := Build_Structure(13, 0, 0); My_Data := My_Data + Temp;

end Privat2;

-- Result of execution

-- The two structures are not equal.-- The two structures are equal now.

The package writer, for his own mysterious reasons, decides to change the package to use a different structure. The example program named e_c21_p3.ada does exactly that. The only change from e_c21_p2.ada is the method of defining the structure, and in this case an array is used. Since the user was forced to follow the rules, his program will run with no modifications. See if you can find anything you can do in the main program using the structure defined in either program that cannot be done in the other. According to the design of Ada, it should be impossible for you to do so.

Notice that the internal workings of the package body are significantly different to reflect the difference in the two data structures, but externally, you cannot tell the difference. Even though the package was changed significantly, none of the users will see a difference and there will be no incompatibility problems. Notice that the main program in this example program is identical to the main program in the last example program. Be sure to compile and execute this program.

THE LIMITED PRIVATE TYPE

Example program ------> e_c21_p4.ada

-- Chapter 21 - Program 4

Page 68: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- This package uses a data structure composed of three INTEGER-- variables. It allow the user to add two structures, component-- by component, or subtract component by component. Provision is-- also made to build a structure from three numbers, or decompose-- a structure into its components.

package Three is

type DATA_STRUCTURE is limited private;

function "+"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE;function "-"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE;function Build_Structure(Val1, Val2, Val3 : INTEGER) return DATA_STRUCTURE;procedure Decompose(Data1 : DATA_STRUCTURE; Val1, Val2, Val3 : out INTEGER);procedure Assign_Struct(Data1 : out DATA_STRUCTURE; Data2 : in DATA_STRUCTURE);function Compare(Data1, Data2 : DATA_STRUCTURE) return BOOLEAN;

private type DATA_STRUCTURE is record Value1 : INTEGER; Value2 : INTEGER; Value3 : INTEGER; end record;end Three;

package body Three is

function "+"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE isTemp : DATA_STRUCTURE;begin Temp.Value1 := Data1.Value1 + Data2.Value1; Temp.Value2 := Data1.Value2 + Data2.Value2; Temp.Value3 := Data1.Value3 + Data2.Value3; return Temp;end "+";

function "-"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE isTemp : DATA_STRUCTURE;begin Temp.Value1 := Data1.Value1 - Data2.Value1; Temp.Value2 := Data1.Value2 - Data2.Value2; Temp.Value3 := Data1.Value3 - Data2.Value3; return Temp;end "-";

function Build_Structure(Val1, Val2, Val3 : INTEGER) return DATA_STRUCTURE isTemp : DATA_STRUCTURE;begin Temp.Value1 := Val1; Temp.Value2 := Val2; Temp.Value3 := Val3; return Temp;end Build_Structure;

Page 69: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

procedure Decompose(Data1 : DATA_STRUCTURE; Val1, Val2, Val3 : out INTEGER) isbegin Val1 := Data1.Value1; Val2 := Data1.Value2; Val3 := Data1.Value3;end Decompose;

procedure Assign_Struct(Data1 : out DATA_STRUCTURE; Data2 : in DATA_STRUCTURE) isbegin Data1 := Data2;end Assign_Struct;

function Compare(Data1, Data2 : DATA_STRUCTURE) return BOOLEAN isbegin return Data1 = Data2;end Compare;

end Three;

-- This program exercises the package Three as an illustration.

with Ada.Text_IO; use Ada.Text_IO;with Three; use Three;

procedure LimPriv is

My_Data, Extra_Data : DATA_STRUCTURE;

begin

Assign_Struct(My_Data, Build_Structure(3, 7, 13)); Assign_Struct(Extra_Data, Build_Structure(-4, 77, 0)); Assign_Struct(My_Data, My_Data + Extra_Data);

if not Compare(My_Data, Extra_Data) then Put_Line("The two structures are not equal."); end if;

Assign_Struct(My_Data, Extra_Data);

if Compare(My_Data, Extra_Data) then Put_Line("The two structures are equal now."); end if;

-- The following line is illegal with the limited private type-- My_Data.Value1 := My_Data.Value1 + 13;

end LimPriv;

Page 70: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Result of execution

-- The two structures are not equal.-- The two structures are equal now.

Examine the program named e_c21_p4.ada for an example of a limited private type. The biggest difference between this program and the previous one is the addition of the reserved word limited in line 11 which declares the type DATA_STRUCTURE to be of type limited private. This gives the package writer even more control over what the user can do with a variable of this type. There are no operations the user can perform on data of this type except for those explicitly spelled out by the package writer in the form of procedures and functions.

If the user were to declare two variables of this type, the compiler would not allow him to compare them for equality or inequality, nor could he assign one variable to the other. In fact, he could not even declare a variable of this type and initialize it to some value, because that would require an implied assignment statement. He cannot declare a constant of this type either since that would also require an assignment statement. The package writer provided a procedure named Assign_Struct, and a function named Compare so the user could perform these operations. The program itself, beginning in line 95, is a bit messy because of the procedure calls required to do the assignments. You are surely asking, "why should we go to all of the trouble to use the limited private type?"

WHY USE LIMITED TYPES?

It is conceivable that you, as the package writer, do not wish to give the user the ability to compare or assign variables of the type in question because of the nature of the project. You may be working on some form of a data encryption system, or a limited access file management system, and you wish to be sure that all users are positively locked out of access to certain files. The lack of compares and assignments would make it impossible for a user to access the lower levels of your system. This is a possible use for the limited type or the limited private type, but there is a much more important need for it. Compile and execute this program and we will discuss the reason for using a limited type.

OVERLOADING THE "=" OPERATOR

The equals operator is defined for the private type, so it cannot be overloaded in the same manner that we have been overloading operators in this tutorial. The limited private type does not have the equality operator available for use, so it can be overloaded in any manner the package writer decrees. Suppose for example that you were using the dynamic string package, named e_c16_p3.ada, from chapter 16 of this tutorial, and you wished to compare two strings for equality. If the strings were, for example, 80 characters long, but they only had 31 characters in use at this time, an overloading of the equality operator could provide a function that would compare only the 31 "active" characters of the string, stopping short of the additional characters in the static string declaration. This would allow the comparison to be done using an infix operator rather than a function call for the comparison. Overloading the "=" operator is not permitted for any type except the limited private type or the limited type in all of Ada.

Example program ------> e_c21_p5.ada

-- Chapter 21 - Program 5

-- Note: This program is based on the example program from chapter-- 18 named operover.ada, and is used to illustrate the different-- style required when using a limited type.

package Shape is

type BOX is limited

Page 71: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

record Length : INTEGER; Width : INTEGER; Height : INTEGER; end record;

-- function Make_A_Box(In_Length, In_Width, In_Height : INTEGER)-- return BOX; procedure Make_A_Box(Result_Box : out BOX; In_Length, In_Width, In_Height : INTEGER); function "="(Left, Right : BOX) return BOOLEAN; function "+"(Left, Right : BOX) return BOX; procedure Print_Box(Input_Box : BOX);

end Shape;

with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

package body Shape is

procedure Make_A_Box(Result_Box : out BOX; In_Length, In_Width, In_Height : INTEGER) isbegin Result_Box.Length := In_Length; Result_Box.Width := In_Width; Result_Box.Height := In_Height;end Make_A_Box;

function "="(Left, Right : BOX) return BOOLEAN isbegin if (Left.Length = Right.Length and Left.Width = Right.Width and Left.Height = Right.Height) then return TRUE; else return FALSE; end if;

end "=";

function "+"(Left, Right : BOX) return BOX is Temp_Box : BOX;begin Temp_Box.Length := Left.Length + Right.Length; Temp_Box.Width := Left.Width + Right.Width; Temp_Box.Height := Left.Height + Right.Height; return Temp_Box;end "+";

procedure Print_Box(Input_Box : BOX) isbegin Put("Length = "); Put(Input_Box.Length, 3); Put(" Width = "); Put(Input_Box.Width, 3); Put(" Height = "); Put(Input_Box.Height, 3); New_Line;end Print_Box;

Page 72: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

end Shape;

with Ada.Text_IO, Shape;use Ada.Text_IO;use type Shape.BOX;

procedure OperOver is

Small_Box : Shape.BOX; Big_Box : Shape.BOX;

begin

-- Small_Box := Shape.Make_A_Box(2, 3, 2);-- Big_Box := Shape.Make_A_Box(4, 5, 3);

Shape.Make_A_Box(Small_Box, 2, 3, 2); Shape.Make_A_Box(Big_Box, 4, 5, 3);

Shape.Print_Box(Small_Box); Shape.Print_Box(Big_Box);

if Small_Box = Small_Box then Put_Line("The small box is the same size as itself."); else Put_Line("The small box is not itself."); end if;

if Small_Box /= Big_Box then Put_Line("The boxes are different sizes."); else Put_Line("The boxes are the same size."); end if;

end OperOver;

-- Result of execution

-- Length = 2 Width = 3 Height = 2 -- Length = 4 Width = 5 Height = 3 -- The small box is the same size as itself.-- The boxes are different sizes.

A very useful example is given in the example program named e_c21_p5.ada. The record is defined to be of type limited, which means it has no assignment available and it has no comparison operators for either equality or inequality. This was done on purpose so we could define our own comparison operator which we do in line 20. Since the function "=" returns a BOOLEAN type, it also provides a default comparison for inequality that returns a BOOLEAN and we cannot redefine the inequality operator. If we would have defined the compare for equality operator to return any other type than BOOLEAN, we would not have a compare for inequality automatically defined, and we could overload the "/=" operator to return any type we wish it to.

This program is based on e_c18_p6.ada from chapter 18 which used the Make_A_Box function as illustrated in lines 16 and 17, but this function could not be used here because we have no

Page 73: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

assignment capability with the limited type. This means that we cannot assign the result to a variable of this limited type. It was necessary to convert the function to a procedure so we could return a value without an assignment in the calling program. Lines 88 and 89 are commented out because they had to be rewritten as lines 91 and 92 which do not use an assignment operator in the calling program.

The big thing we gained here is that we are not required to use the default comparison operator. We may wish to use some very strange means as a basis for equality, and use of the limited type will permit us to do so.

You may think that it is very inefficient to write a program that requires a procedure or function call to do anything at all, but that is not necessarily the case. If the compiler writer has done a good job, a subprogram call can be very efficient, and a special purpose comparison could be much more efficient than a general purpose comparison routine that is designed to handle all cases. Because a general purpose routine is designed for flexibility, it may not do any particular comparison very efficiently.

Be sure you compile and execute this program so you can verify that it does the same thing as the last two example programs.

A PACKAGE WITHOUT A BODY

It is possible to declare a package specification without a corresponding body. That would be done if you wished to declare types, variables, and constants that could be used by other packages and subprograms, but included no executable code with the declarations. Programming exercise 3 will illustrate this technique. It is never possible to declare a package body without a corresponding package specification.

PROGRAMMING EXERCISES

1. Add a function to PRIVATE1.ADA named Compare_Sum, which compares the sum of the three elements of one structure to the sum of the three elements of the second structure, returning a TRUE if the sums are equal, and a FALSE otherwise. Add a few statements to the main program to exercise this new function.(Solution)

-- Chapter 21 - Programming exercise 1

-- This package uses a data structure composed of three INTEGER-- variables. It allow the user to add two structures, component-- by component, or subtract component by component. Provision is-- also made to build a structure from three numbers, or decompose-- a structure into its components.

package Three istype DATA_STRUCTURE is private;function "+"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE;function "-"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE;function Build_Structure(Val1, Val2, Val3 : INTEGER) return DATA_STRUCTURE;procedure Decompose(Data1 : DATA_STRUCTURE; Val1, Val2, Val3 : out INTEGER);function Compare_Sum(Data1, Data2 : DATA_STRUCTURE) return BOOLEAN;

private type DATA_STRUCTURE is record Value1 : INTEGER; Value2 : INTEGER; Value3 : INTEGER; end record;end Three;

Page 74: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

package body Three is

function "+"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE isTemp : DATA_STRUCTURE;begin Temp.Value1 := Data1.Value1 + Data2.Value1; Temp.Value2 := Data1.Value2 + Data2.Value2; Temp.Value3 := Data1.Value3 + Data2.Value3; return Temp;end "+";

function "-"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE isTemp : DATA_STRUCTURE;begin Temp.Value1 := Data1.Value1 - Data2.Value1; Temp.Value2 := Data1.Value2 - Data2.Value2; Temp.Value3 := Data1.Value3 - Data2.Value3; return Temp;end "-";

function Build_Structure(Val1, Val2, Val3 : INTEGER) return DATA_STRUCTURE isTemp : DATA_STRUCTURE;begin Temp.Value1 := Val1; Temp.Value2 := Val2; Temp.Value3 := Val3; return Temp;end Build_Structure;

procedure Decompose(Data1 : DATA_STRUCTURE; Val1, Val2, Val3 : out INTEGER) isbegin Val1 := Data1.Value1; Val2 := Data1.Value2; Val3 := Data1.Value3;end Decompose;

function Compare_Sum(Data1, Data2 : DATA_STRUCTURE) return BOOLEAN isbegin return (Data1.Value1 + Data1.Value2 + Data1.Value3) = (Data2.Value1 + Data2.Value2 + Data2.Value3);end Compare_Sum;

end Three;

-- This program exercises the package Three as an illustration.

with Ada.Text_IO; use Ada.Text_IO;with Three; use Three;

Page 75: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

procedure CH21_1 is

My_Data, Extra_Data : DATA_STRUCTURE; Temp : DATA_STRUCTURE;

begin

My_Data := Build_Structure(3,7,13); Extra_Data := Build_Structure(-4,77,0); My_Data := My_Data + Extra_Data;

if My_Data /= Extra_Data then Put_Line("The two structures are not equal."); end if;

My_Data := Extra_Data;

if My_Data = Extra_Data then Put_Line("The two structures are equal now."); end if;

-- The following line is illegal with the private type.-- My_Data.Value1 := My_Data.Value1 + 13;

Temp := Build_Structure(13,0,0); My_Data := My_Data + Temp;

if not Compare_Sum(My_Data, Temp) then Put_Line("My_Data and Temp are not equal."); end if;

My_Data := Build_Structure(1,9,3); if Compare_Sum(My_Data, Temp) then Put_Line("My_Data and Temp are equal."); end if;

end CH21_1;

-- Result of execution

-- The two structures are not equal.-- The two structures are equal now.-- My_Data and Temp are not equal.-- My_Data and Temp are equal.

2. Add the same function to PRIVATE2.ADA, and add a few statements to the main program to test it.(Solution)

-- Chapter 21 - Programming exercise 2

-- This package uses a data structure composed of three INTEGER-- variables. It allow the user to add two structures, component-- by component, or subtract component by component. Provision is-- also made to build a structure from three numbers, or decompose-- a structure into its components.

Page 76: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

package Three istype DATA_STRUCTURE is private;function "+"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE;function "-"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE;function Build_Structure(Val1, Val2, Val3 : INTEGER) return DATA_STRUCTURE;procedure Decompose(Data1 : DATA_STRUCTURE; Val1, Val2, Val3 : out INTEGER);function Compare_Sum(Data1, Data2 : DATA_STRUCTURE) return BOOLEAN;

private type DATA_STRUCTURE is array(1..3) of INTEGER;end Three;

package body Three is

function "+"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE isTemp : DATA_STRUCTURE;begin Temp(1) := Data1(1) + Data2(1); Temp(2) := Data1(2) + Data2(2); Temp(3) := Data1(3) + Data2(3); return Temp;end "+";

function "-"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE isTemp : DATA_STRUCTURE;begin Temp(1) := Data1(1) - Data2(1); Temp(2) := Data1(2) - Data2(2); Temp(3) := Data1(3) - Data2(3); return Temp;end "-";

function Build_Structure(Val1, Val2, Val3 : INTEGER) return DATA_STRUCTURE isTemp : DATA_STRUCTURE;begin Temp(1) := Val1; Temp(2) := Val2; Temp(3) := Val3; return Temp;end Build_Structure;

procedure Decompose(Data1 : DATA_STRUCTURE; Val1, Val2, Val3 : out INTEGER) isbegin Val1 := Data1(1); Val2 := Data1(2); Val3 := Data1(3);end Decompose;

function Compare_Sum(Data1, Data2 : DATA_STRUCTURE) return BOOLEAN isbegin return (Data1(1) + Data1(2) + Data1(3)) =

Page 77: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

(Data2(1) + Data2(2) + Data2(3));end Compare_Sum;

end Three;

-- This program exercises the package Three as an illustration.

with Ada.Text_IO; use Ada.Text_IO;with Three; use Three;

procedure CH21_2 is

My_Data, Extra_Data : DATA_STRUCTURE; Temp : DATA_STRUCTURE;

begin

My_Data := Build_Structure(3,7,13); Extra_Data := Build_Structure(-4,77,0); My_Data := My_Data + Extra_Data;

if My_Data /= Extra_Data then Put_Line("The two structures are not equal."); end if;

My_Data := Extra_Data;

if My_Data = Extra_Data then Put_Line("The two structures are equal now."); end if;

-- The following line is illegal with the private type.-- My_Data.Value1 := My_Data.Value1 + 13;

Temp := Build_Structure(13,0,0); My_Data := My_Data + Temp;

if not Compare_Sum(My_Data, Temp) then Put_Line("My_Data and Temp are not equal."); end if;

My_Data := Build_Structure(1,9,3); if Compare_Sum(My_Data, Temp) then Put_Line("My_Data and Temp are equal."); end if;

end CH21_2;

-- Result of execution

-- The two structures are not equal.-- The two structures are equal now.-- My_Data and Temp are not equal.-- My_Data and Temp are equal.

Page 78: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

3. Write a package named Stuff that contains only a package specification. The package specification should make the value of Pi (3.14159) and Two_Pi available as well as a DATE record type. Include a short program to use the package.(Solution)

-- Chapter 21 - Programming exercise 3package Stuff is

type MONTH_NAMES is (JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC); type DATE is record Month : MONTH_NAMES; Day : INTEGER range 1..31; Year : INTEGER range 1800..2100; end record;

Pi : constant := 3.14159; Two_Pi : constant := 2 * Pi;

end Stuff;

with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO, Stuff;use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO, Stuff;

procedure CH21_3 is

package Enum_IO is new Ada.Text_IO.Enumeration_IO(MONTH_NAMES);use Enum_IO;

Birth_Day : DATE := (OCT, 18, 1938);

begin Put("My birthday is "); Put(Birth_Day.Month); Put(Birth_Day.Day, 3); Put(","); Put(Birth_Day.Year, 5); New_Line(2);

Put("The value of Pi is"); Put(Pi, 3, 6, 0); New_Line;end CH21_3;

-- Result of execution

-- My birthday is OCT 18, 1938---- The value of Pi is 3.141590

Page 79: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Ada Tutorial - Chapter 22

OBJECT ORIENTED PROGRAMMING

In recent years, object oriented programming has seen a tremendous rise in popularity. It seems like the term has been applied to every programming language and marketing scheme regardless of whether it is actually a part of the product. There are probably very few persons that really understand what it is and know how to properly use object oriented programming, but that will not prevent us from digging in and learning how to use some of the technique in our programs. We will devote the next two chapters to this subject.

It is beyond the scope of this tutorial to define and illustrate such topics as object oriented analysis and object oriented design. There are lots of books available that cover those topics quite well, so we will spend our time showing how the various constructs available in Ada 95 help us to build a more reliable program.

The three terms that are usually applied to object oriented programming are encapsulation, inheritance, and polymorphism or dynamic binding. We have already covered a good bit of encapsulation, which can properly be called information hiding, in chapter 21 of this tutorial, and we will defer our discussion of polymorphism until chapter 23. This leaves us with inheritance which we will cover at this time.

INHERITANCE AND EXTENSION

Inheritance involves making a copy of some existing entity and adding to it to define an entity with all of the properties of the original, but with added properties. Most other programming languages approach this topic with the emphasis on the inheritance portion of the previous statement, with little emphasis on the extension operation. Ada writers, however, place the emphasis on the extension portion of that statement, so we will too. In this chapter we will illustrate how to begin with an entity and extend it such that it has additional capability. As usual we will start with a simple example program.

THE SIMPLEST INHERITANCE

Example program ------> e_c22_p1.ada

-- Chapter 22 - Program 1package Starter is

type MY_INTEGER is new INTEGER range 5..150;

type MY_FLOAT is new FLOAT;

type WOODEN_BOX is record Length : FLOAT; Width : FLOAT; Height : FLOAT; end record;

function "+" (Left, Right : WOODEN_BOX) return WOODEN_BOX;

type STEEL_BOX is new WOODEN_BOX;

function "-" (Left, Right : STEEL_BOX) return STEEL_BOX;

end Starter;

-- There is no implementation for this example program.

Page 80: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Result of execution---- (This fragment cannot be executed)

This program has nothing to do with object oriented programming as usually applied to programming languages, but it illustrates simple extension of a few Ada types. Line 4 declares that the type MY_INTEGER will have all of the properties of the type INTEGER but with a more limited range, so it is actually inheriting all of its properties from the parent type. The same statement can be said about the type MY_FLOAT in line 6. This inheritance is not very interesting however, so we will go on to a more complex type.

We define a type in lines 8 through 13 named WOODEN_BOX which is composed of three simple components and has several predefined operations which all records have, such as assignment, compare for equality, and compare for inequality. We add another primitive operation, the ability to add two objects of this type by overloading the + operator in line 15. In line 17, we derive a new type named STEEL_BOX which will have all of the components and operations that its parent type has, including the overloading of the + operator. The interesting part is in line 19 where we extend the STEEL_BOX type by overloading the - operator. It should be clear that we have "inherited" all of the functionality of the WOODEN_BOX type, and extended the operation of the STEEL_BOX by the additional operator.

This is inheritance and extension, but it is very limited because we cannot add components to the inherited capability, only operations. We will add components in example programs later in this chapter, but this program fragment was given to illustrate a very basic form of inheritance.

MORE SIMPLE TYPE EXTENTION

Example program ------> e_c22_p2.ada

-- Chapter 22 - Program 2package Conveyance1 is

-- This is a very simple transportation type. type TRANSPORT is record Wheels : INTEGER; Weight : FLOAT; end record;

procedure Set_Values(Vehicle_In : in out TRANSPORT; Wheels_In : INTEGER; Weight_In : FLOAT); function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER; function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT;

-- This CAR type extends the functionality of the TRANSPORT type. type CAR is new TRANSPORT;

function Tire_Loading(Vehicle_In : CAR) return FLOAT;

end Conveyance1;

package body Conveyance1 is

-- Subprograms for the TRANSPORT record type.procedure Set_Values(Vehicle_In : in out TRANSPORT;

Page 81: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Wheels_In : INTEGER; Weight_In : FLOAT) isbegin Vehicle_In.Wheels := Wheels_In; Vehicle_In.Weight := Weight_In;end Set_Values;

function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER isbegin return Vehicle_In.Wheels;end Get_Wheels;

function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT isbegin return Vehicle_In.Weight;end Get_Weight;

-- Subprogram for the CAR record type.function Tire_Loading(Vehicle_In : CAR) return FLOAT isbegin return Vehicle_In.Weight / FLOAT(Vehicle_In.Wheels);end Tire_Loading;

end Conveyance1;

-- Results of execution---- (This package cannot be executed alone.)

The example program e_c22_p2.ada illustrates a little more extension. It begins with a definition of a very simple record named TRANSPORT, with two components and three explicitly declared subprograms in addition to the operators that are generated automatically by the system such as assignment and compare for equality. There is nothing magic or unusual about this record. It can be used to define data and used according to the rules of Ada just like any other record we have studied in this tutorial. In fact we will use it in the next example program.

The next record, named CAR, is a little more interesting because it is declared as a derived type of the TRANSPORT record. This means that it has all of the components and functionality of its parent. In fact, we can use this new type as if it has the three functions declared in lines 11 through 15 redefined with the first parameter of type CAR. All three of these subprograms can be called directly with a variable of type CAR as the first parameter, without doing any type conversion on the variable. We extend CAR by adding a function named Tire_Loading to give it more capability than its parent type. This truly is inheritance and extension in the fullest sense of the terms, but we still do not have the ability to add components to the new type, only functionality. We will be able to extend components shortly.

The implementation in the package body is very simple and you should have no difficulty understanding it with the knowledge of Ada that you have gained already. Note that the three subprograms for the TRANSPORT type are available for the CAR type just as if they were redefined here, but they are not, because they are inherited automatically from the parent type. All operators and primitive subprograms are automatically inherited when we derive a new record from an old record.

WHAT ARE THE PRIMITIVE OPERATIONS?

The primitive operations of a type are;

Page 82: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

• all intrinsic operations predefined by the compiler such as assignment, compare for equality, and attributes

• all primitive operations inherited from a parent, when using inheritance or derivation • subprograms with one or more parameters and/or a return value of the type, which are

declared in the package specification along with the type declaration itself

The CAR type in the present example has some of each category. It has a few intrinsic operators such as assignment. It inherits three operations from its parent class which are declared in lines 11 through 15. Finally, it has an operation that is unique to itself declared in line 21, the Tire_Loading function. If the CAR type were inherited into another type, such as a SPORTS_CAR type, it would inherit all four of the functions declared in lines 11 through 21 since they are all within its ancestor types.

USING THE TYPE EXTENSION PACKAGE

Example program ------> e_c22_p3.ada

-- Chapter 22 - Program 3

with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO, Conveyance1;use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO, Conveyance1;

procedure Vehicle1 is

Hummer : TRANSPORT; Ford : CAR;

begin Set_Values(Hummer, 4, 5760.0);

Put("The Hummer has"); Put(Get_Wheels(Hummer), 2); Put(" wheels."); New_Line; Put("The Hummer has"); Put(Hummer.Wheels, 2); Put(" wheels."); New_Line;

Set_Values(Ford, 4, 3204.0); Put("The Ford has"); Put(Get_Wheels(Ford), 2); Put(" wheels. The tire load is "); Put(Tire_Loading(Ford), 3, 1, 0); Put(" pounds per tire."); New_Line; end Vehicle1;

-- Result of execution---- The Hummer has 4 wheels.-- The Hummer has 4 wheels.-- The Ford has 4 wheels. The tire load is 801.0 pounds per tire.

Page 83: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Examine the file named e_c22_p3.ada for an example of using the two types we defined in the last example program. You will notice that the package named Conveyance1 is with'ed and use'ed in lines 3 and 4 to make them available for use in the program. We define two variables in lines 8 and 9, one each of the two types which we will use in the program.

Lines 13 through 18 are rather simple, with nothing new, so you can study those on your own. The same is true of lines 20 through 23, except that we will return to this group of statements shortly.

The first really interesting statement is given in line 25 where we call Set_Values with the first parameter being of type CAR, even though we have no explicitly defined function with that type. This proves that the system truly generated a function with the type CAR as the first parameter that is identical to the same function with the type TRANSPORT as the first parameter. Since we didn't write the code twice, we only have a single function to maintain that operates on both of the types, but if we wanted to have them do different things, we could write a specification and a body for CAR's own Set_Values function.

Type extension is therefore illustrated in this example program in line 25 as well as line 28, where the function Get_Wheels is called, which was inherited from the parent type. Line 30 however, illustrates type extension since we wrote a completely new function to retrieve the Tire_Loading from CAR type variables. You will notice that because of the clear definition of Ada, it is impossible to tell in this program which functions were inherited and which were extended. The TRANSPORT type has no Tire_Loading function.

Object oriented programming includes the concept of information hiding which we are not practicing with this example program. If you refer back to line 21, you will see that we are accessing the number of Wheels on the Hummer directly. This is bad practice, because one of the most error prone operations in computer programming is providing direct access to data over a large range. The principle of information hiding requires that the data for an object be hidden within the object and unavailable to the outside world as discussed in the previous chapter of this tutorial. We will completely hide the data within the object in the next example program.

Be sure to compile and execute this program to be sure you understand what it does before going on to the next example program.

TYPE EXTENSION WITH A PRIVATE SECTION

Example program ------> e_c22_p4.ada

-- Chapter 22 - Program 4package Conveyance2 is

type TRANSPORT is private;

procedure Set_Values(Vehicle_In : in out TRANSPORT; Wheels_In : INTEGER; Weight_In : FLOAT); function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER; function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT;

type CAR is private;

procedure Set_Values(Vehicle_In : in out CAR; Wheels_In : INTEGER; Weight_In : FLOAT); function Get_Wheels(Vehicle_In : CAR) return INTEGER; function Tire_Loading(Vehicle_In : CAR) return FLOAT;

private -- private part of the specification

Page 84: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

type TRANSPORT is record Wheels : INTEGER; Weight : FLOAT; end record;

type CAR is new TRANSPORT;

end Conveyance2;

package body Conveyance2 is

-- Subprograms for the TRANSPORT record type.procedure Set_Values(Vehicle_In : in out TRANSPORT; Wheels_In : INTEGER; Weight_In : FLOAT) isbegin Vehicle_In.Wheels := Wheels_In; Vehicle_In.Weight := Weight_In;end Set_Values;

function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER isbegin return Vehicle_In.Wheels;end Get_Wheels;

function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT isbegin return Vehicle_In.Weight;end Get_Weight;

-- Subprogram for the CAR record type.procedure Set_Values(Vehicle_In : in out CAR; Wheels_In : INTEGER; Weight_In : FLOAT) isbegin Vehicle_In.Wheels := Wheels_In; Vehicle_In.Weight := Weight_In;end Set_Values;

function Get_Wheels(Vehicle_In : CAR) return INTEGER isbegin return Vehicle_In.Wheels;end Get_Wheels;

function Tire_Loading(Vehicle_In : CAR) return FLOAT isbegin return Vehicle_In.Weight / FLOAT(Vehicle_In.Wheels);end Tire_Loading;

end Conveyance2;

The example program named e_c22_p4.ada is very similar to the previous program with the exception that the two record types are declared to be private. This was done to prevent the user from seeing into the objects and directly manipulating the contained data. However, with each improvement we add to our code, there is the possibility that something else will not work as conveniently, and there is a bit of a penalty to be paid here.

Page 85: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Because the TRANSPORT type is private, it does not provide full functionality to objects outside of itself. In fact, the only operations it permits are assignment and compare for equality or inequality. The three subprograms defined for the TRANSPORT type in lines 6 through 10 are not available to be inherited into the CAR type because there is no indication that CAR inherits from TRANSPORT at this point. It is necessary to declare them explicitly for the CAR type as is done in lines 14 through 18. It is also necessary to provide a full implementation for these three functions as is illustrated in lines 58 through 74. It is clear that this form of inheritance is lacking in elegance.

As with all private types, the actual definitions are given within the private section in lines 21 through 29 where it is clear that CAR inherits all of TRANSPORT. This example is very similar to the last example, so you will be left to study the package body on your own.

USING THE TYPE EXTENSION

Example program ------> e_c22_p5.ada

-- Chapter 22 - Program 5

with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO, Conveyance2;use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO, Conveyance2;

procedure Vehicle2 is

Hummer : TRANSPORT; Ford : CAR;

begin Set_Values(Hummer, 4, 5760.0);

Put("The Hummer has"); Put(Get_Wheels(Hummer), 2); Put(" wheels."); New_Line;

Set_Values(Ford, 4, 3204.0); Put("The Ford has"); Put(Get_Wheels(Ford), 2); Put(" wheels. The tire load is "); Put(Tire_Loading(Ford), 3, 1, 0); Put(" pounds per tire."); New_Line; end Vehicle2;

-- Result of execution---- The Hummer has 4 wheels.-- The Ford has 4 wheels. The tire load is 801.0 pounds per tire.

The example program named Vehicle2 is in the file named e_c22_p5.ada and gives a very limited example of using the TRANSPORT and the CAR class. There is nothing new here except for the fact that the direct access of the Wheels component is not used here since it is now hidden from view.

The next example program will finally provide full inheritance and extension of both components

Page 86: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

and operations. This is what is usually meant when inheritance is referred to in articles on object oriented programming.

REAL TYPE EXTENSION

Example program ------> e_c22_p6.ada

-- Chapter 22 - Program 6package Conveyance3 is

-- This is a very simple transportaion type. type TRANSPORT is tagged private;

procedure Set_Values(Vehicle_In : in out TRANSPORT; Wheels_In : INTEGER; Weight_In : FLOAT); function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER; function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT;

type CAR is new TRANSPORT with private;

procedure Set_Values(Vehicle_In : in out CAR; Passenger_Count_In : INTEGER); function Get_Passenger_Count(Vehicle_In : CAR) return INTEGER;

type TRUCK is new TRANSPORT with private;

procedure Set_Values(Vehicle_In : in out TRUCK; Wheels_In : INTEGER; Weight_In : FLOAT; Passenger_Count_In : INTEGER; Payload_In : FLOAT); function Get_Passenger_Count(Vehicle_In : TRUCK) return INTEGER;

type BICYCLE is new TRANSPORT with private;

private

type TRANSPORT is tagged record Wheels : INTEGER; Weight : FLOAT; end record;

type CAR is new TRANSPORT with record Passenger_Count : INTEGER; end record;

type TRUCK is new TRANSPORT with record Passenger_Count : INTEGER; Payload : FLOAT; end record;

type BICYCLE is new TRANSPORT with null record;

end Conveyance3;

Page 87: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

package body Conveyance3 is

-- Subprograms for the TRANSPORT3 recordprocedure Set_Values(Vehicle_In : in out TRANSPORT; Wheels_In : INTEGER; Weight_In : FLOAT) isbegin Vehicle_In.Wheels := Wheels_In; Vehicle_In.Weight := Weight_In;end Set_Values;

function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER isbegin return Vehicle_In.Wheels;end Get_Wheels;

function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT isbegin return Vehicle_In.Weight;end Get_Weight;

-- Subprograms for the CAR recordprocedure Set_Values(Vehicle_In : in out CAR; Passenger_Count_In : INTEGER) isbegin Vehicle_In.Passenger_Count := Passenger_Count_In;end Set_Values;

function Get_Passenger_Count(Vehicle_In : CAR) return INTEGER isbegin return Vehicle_In.Passenger_Count;end Get_Passenger_Count;

-- Subprograms for the TRUCK recordprocedure Set_Values(Vehicle_In : in out TRUCK; Wheels_In : INTEGER; Weight_In : FLOAT; Passenger_Count_In : INTEGER; Payload_In : FLOAT) isbegin -- This is one way to set the values in the base class Vehicle_In.Wheels := Wheels_In; Vehicle_In.Weight := Weight_In;

-- This is another way to set the values in the base class Set_Values(TRANSPORT(Vehicle_In), Wheels_In, Weight_In);

-- This sets the values in this class Vehicle_In.Passenger_Count := Passenger_Count_In; Vehicle_In.Payload := Payload_In;end Set_Values;

function Get_Passenger_Count(Vehicle_In : TRUCK) return INTEGER isbegin return Vehicle_In.Passenger_Count;

Page 88: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

end Get_Passenger_Count;

end Conveyance3;

-- Result of execution---- (This program cannot be executed alone.)

The example program named e_c22_p6.ada contains our first example of real inheritance and extension and is a good example of the way it is actually used in practice. The last few example programs, even though they are valid Ada programs, do not illustrate real inheritance, but were intended to show that there are more subtle forms of inheritance built into the Ada language.

We begin by declaring a private TRANSPORT type in line 5, but with an extra reserved word added, namely tagged. The word tagged indicates to the Ada compiler that this type may be extended with additional components and/or operations to define a completely new type. The actual definition is given in lines 35 through 39 and should look very familiar by now except for the reserved word tagged which is added here also. Three subprograms are declared in lines 7 through 11 in much the same manner that they have been added in previous programs in this chapter. Except for the addition of the word tagged, this package is identical to the previous example package.

NOW FOR THE REAL DIFFERENCE

When we define the CAR type, things look a lot different than they did in the last program. This time the parent type is mentioned in the declaration along with a new use of the reserved word with. We have been using this word to with packages into our various programs but now it is used to indicate that something new is going to be added to the parent type to generate the new type. Lines 41 through 44 contain the full declaration of the new type CAR. It says to create a new derived type of TRANSPORT with some additions because of the reserved word with being used here. The component named Passenger_Count is included in the CAR type along with the two components from TRANSPORT, giving the CAR type three variables. We have finally found a way to extend the number of components in an inherited type. Note that you can only add components. It is not possible to remove components or to override components.

The CAR type also inherits the three subprograms from TRANSPORT, but it provides its own Set_Values procedure which only sets the value of the Passenger_Count component. Presumably, objects of the CAR type will be required to use the Set_Values procedure provided by the parent class in order to set the values of the two inherited components. This is what will actually be done in the next example program. The CAR type also provides a new subprogram named Get_Passenger_Count to return the number of passengers. Overall, the CAR type consists of three components and provides five subprograms which can be used to manipulate objects of this type.

It is possible to extend a type by adding subprograms, and it is possible to modify the functionality of a type by overriding one or more subprograms, but it is not possible to eliminate subprograms from the new type which are part of the parent type. Even though the TRANSPORT type is private, the CAR inherits all of the primitive operations of the TRANSPORT type because the TRANSPORT type is tagged. This is true inheritance because all of the entities are inherited.

WHAT DOES IT MEAN TO FREEZE A TYPE?

We can add all of the functionality we desire to a type until we use that type to define a variable or inherit it into a new type. After it is used, we are not allowed to add functionality to the type because we would have two variants of the type, the one where it was used and the newer changed version. The two would be incompatible even though they had the same type name. For this reason, the type is "frozen", which means you cannot add more primitive operations to it.

Page 89: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

ANOTHER NEW TYPE

Since the TRUCK type is very similar to the CAR type, little needs to be said except that it adds two components to those inherited, and it adds two subprograms to those inherited from the parent. The TRUCK type is a little different because it provides initialization data for all four variables in its procedure named Set_Values, so it has no need for the procedure of the same name in the parent type.

The BICYCLE type is declared in line 31 in exactly the same manner as the CAR and the TRUCK. When we get to line 52 where the private type is actually defined, it looks a little different than the other two new types. The with null record at the end of the line tells the system that the BICYCLE type will have all of the components of the parent type and no more. It is necessary to explicitly tell the system that none will be added.

We now have the ability to add components and subprograms to an inherited type. We can add subprograms without adding any components. We can also add components without adding any additional subprograms, but that would not be very useful because we would have no way to use the new components since they would be hidden in the object.

WHAT IS A CLASS IN ADA?

A class in Ada 95 is a group of types with a common ancestor, and the name of that class is the type name of the highest ancestor. The present example program contains the class named TRANSPORT which consists of the types TRANSPORT, CAR, TRUCK, and BICYCLE. It would also be correct to say that it contains the class named CAR that consists of only the type CAR. Any hierarchy of types compose a class with the class name being the name of the highest class in the hierarchy. The class concept is not very important at this time, but it will become important when we study dynamic dispatching in the next chapter.

THE PACKAGE BODY

The package body, given in lines 58 through 121, is nothing more than the same implementations we have been using in this chapter. All of the inheritance and extension constructs are in the specification part of the code.

There is one small detail that the student should take note of in line 104 and 105, where the components inherited from the parent class are initialized. Nothing new there, but in line 108 the procedure named Set_Values from the parent class is used to initialize the same two variables. Since the types are different, it is necessary to perform a type transformation from the TRUCK type to the TRANSPORT type to get the compiler to accept it. Note that either method of initialization can be used. The two methods were given here only as an example.

USING THE REAL TYPE EXTENSION

Example program ------> e_c22_p7.ada

-- Chapter 22 - Program 7

with Ada.Text_IO, Ada.Integer_Text_IO, Conveyance3;use Ada.Text_IO, Ada.Integer_Text_IO, Conveyance3;

procedure Vehicle3 is

Hummer : TRANSPORT; Limo : CAR; Chevy : CAR; Dodge : TRUCK; Ford : TRUCK;

begin

Page 90: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Set_Values(Hummer, 4, 5760.0);

Set_Values(Limo, 8); Set_Values(TRANSPORT(Limo), 4, 3750.0); Set_Values(Chevy, 5); Set_Values(TRANSPORT(Chevy), 4, 2560.0);

Set_Values(Dodge, 6, 4200.0, 3, 1800.0); Set_Values(Ford, 4, 2800.0, 3, 1100.0);

Put("The Ford truck has"); Put(Get_Wheels(Ford), 2); Put(" wheels, and can carry"); Put(Get_Passenger_Count(Ford), 2); Put(" passengers."); New_Line; end Vehicle3;

-- Result of execution---- The Ford truck has 4 wheels, and can carry 3 passengers.

Examine the file named e_c22_p7.ada which serves as an illustration of how to use these new types we have just generated. As expected, the new package is both with'ed and use'ed into this program, and the four new types are available for use as needed. There is nothing special about the new types, except for the fact that three are descendants of the fourth. They are used just like any other types would be used as can be seen by inspecting this example program. You will notice that two procedure calls are necessary to set all of the values in the CAR type objects, but only one call is necessary to set all of the values in the TRUCK type objects. This is only because of the way we declared them in the package. In a real program it would be best to declare them both in a similar manner to make the code easier to understand.

Be sure to compile and execute this program.

INITIALIZERS AND FINALIZERS - CONTROLLED

Example program ------> e_c22_p8.ada

-- Chapter 22 - Program 8

with Ada.Text_IO, Ada.Finalization;use Ada.Text_IO, Ada.Finalization;

package Component is

type WIDGET is new CONTROLLED with record Size : INTEGER; Number : INTEGER; end record;

procedure Initialize(Item : in out WIDGET); procedure Adjust(Item : in out WIDGET); procedure Finalize(Item : in out WIDGET);

end Component;

Page 91: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

package body Component is

procedure Initialize(Item : in out WIDGET) isbegin Put_Line(" This is from Initialize");end Initialize;

procedure Adjust(Item : in out WIDGET) isbegin Put_Line(" This is from Adjust");end Adjust;

procedure Finalize(Item : in out WIDGET) isbegin Put_Line(" This is from Finalize");end Finalize;

end Component;

with Ada.Text_IO, Component;use Ada.Text_IO, Component;

procedure Control is

Paper : WIDGET; Pencil : WIDGET;

begin

Put_Line("Beginning this simple program"); Paper.Size := 11; Paper.Number := 25; Put_Line("Paper record filled with data"); Pencil := Paper; Put_Line("Paper copied to Pencil"); Paper := Pencil; Put_Line("Pencil copied to Paper");

end Control;

-- Result of execution---- This is from Initialize-- This is from Initialize-- Beginning this simple program-- Paper record filled with data-- This is from Finalize-- This is from Adjust-- Paper copied to Pencil-- This is from Finalize-- This is from Adjust-- Pencil copied to Paper-- This is from Finalize-- This is from Finalize

The example program named e_c22_p8.ada illustrates automatic object initialization and automatic

Page 92: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

cleaning up when the object is about to be destroyed. We include the Ada.Finalization package for use here, and declare a type named WIDGET which inherits from the type named CONTROLLED. We override three procedures from CONTROLLED with the predefined names Initialize, Adjust, and Finalize, each with one parameter of the type of record we just declared. The package body contains the definitions of the three procedures which are trivial because they each output only a single line of text to the monitor.

In the main program, we include the Component package in the with list, and we write a trivial program that does nothing but output a few lines of text. When we execute this program, we get a bit of a surprise because the three procedures in the Component package are executed even though we never called them, the system did. They are very special because they each have a special job to do.

Initialize - When any object of this type is defined, this procedure is run automatically, without the programmer calling it explicitly. This is the place to initialize the components of the class or include any other initialization code that needs to be executed for each object of this type.

Adjust - Following an assignment, you may have some cleaning up of the data within the new copy. This procedure is called automatically for the new object after the new data has been assigned to it.

Finalize - When you assign something to a variable, the data contained in that variable is overwritten and lost forever. You may wish to do something special with the data before it is overwritten, and that is one of the jobs for this procedure. When you are finished with an object and you wish to destroy it, or it goes out of scope, you will need to do some cleaning up if it has an access variable to something on the heap that you are finished with, or some other job that must be done prior to destroying the object. This procedure is called automatically by the system.

You will notice that the Initialize procedure is executed twice prior to the beginning of the program, because there are two variables. The Finalize and the Adjust procedures are both executed for each of the two assignment operations as is evident from the listing. The Finalize procedure is called once for each of the two objects when they go out of scope at the end of the program.

These three procedures can be a great aid when you are using types that are rather complex.

PROGRAMMING EXERCISES

1. Add a BICYCLE object to e_c22_p7.ada and exercise it to prove to yourself that you can use the inherited objects correctly.(Solution)

-- Chapter 22 - Exercise 1

with Ada.Text_IO, Ada.Integer_Text_IO, Conveyance3;use Ada.Text_IO, Ada.Integer_Text_IO, Conveyance3;

procedure CH22_1 is

Hummer : TRANSPORT; Limo : CAR; Chevy : CAR; Dodge : TRUCK; Ford : TRUCK; Schwinn : BICYCLE;

begin Set_Values(Hummer, 4, 5760.0);

Set_Values(Limo, 8); Set_Values(TRANSPORT(Limo), 4, 3750.0); Set_Values(Chevy, 5);

Page 93: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Set_Values(TRANSPORT(Chevy), 4, 2560.0);

Set_Values(Dodge, 6, 4200.0, 3, 1800.0); Set_Values(Ford, 4, 2800.0, 3, 1100.0);

Set_Values(Schwinn, 1, 75.0);

Put("The Ford truck has"); Put(Get_Wheels(Ford), 2); Put(" wheels, and can carry"); Put(Get_Passenger_Count(Ford), 2); Put(" passengers."); New_Line; Put("The Schwinn has"); Put(Get_Wheels(Schwinn), 2); Put(" wheel."); New_Line;

end CH22_1;

-- Result of execution---- The Ford truck has 4 wheels, and can carry 3 passengers.-- The Schwinn has one wheel.

2. Add a new function to the BICYCLE class in e_c22_p6.ada to retrieve the weight in ounces. Name the new function Number_Of_Ounces. One pound is 16 ounces.(Solution)

-- Chapter 22 - Exercise 2package CH22_2 is

-- This is a very simple transportaion type. type TRANSPORT is tagged private;

procedure Set_Values(Vehicle_In : in out TRANSPORT; Wheels_In : INTEGER; Weight_In : FLOAT); function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER; function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT;

type CAR is new TRANSPORT with private;

procedure Set_Values(Vehicle_In : in out CAR; Passenger_Count_In : INTEGER); function Get_Passenger_Count(Vehicle_In : CAR) return INTEGER;

type TRUCK is new TRANSPORT with private;

procedure Set_Values(Vehicle_In : in out TRUCK; Wheels_In : INTEGER; Weight_In : FLOAT; Passenger_Count_In : INTEGER; Payload_In : FLOAT); function Get_Passenger_Count(Vehicle_In : TRUCK) return INTEGER;

Page 94: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

type BICYCLE is new TRANSPORT with private;

function Get_Weight_In_Ounces(Vehicle_In : BICYCLE) return FLOAT;

private

type TRANSPORT is tagged record Wheels : INTEGER; Weight : FLOAT; end record;

type CAR is new TRANSPORT with record Passenger_Count : INTEGER; end record;

type TRUCK is new TRANSPORT with record Passenger_Count : INTEGER; Payload : FLOAT; end record;

type BICYCLE is new TRANSPORT with null record;

end CH22_2;

package body CH22_2 is

-- Subprograms for the TRANSPORT3 recordprocedure Set_Values(Vehicle_In : in out TRANSPORT; Wheels_In : INTEGER; Weight_In : FLOAT) isbegin Vehicle_In.Wheels := Wheels_In; Vehicle_In.Weight := Weight_In;end Set_Values;

function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER isbegin return Vehicle_In.Wheels;end Get_Wheels;

function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT isbegin return Vehicle_In.Weight;end Get_Weight;

-- Subprograms for the CAR recordprocedure Set_Values(Vehicle_In : in out CAR; Passenger_Count_In : INTEGER) isbegin Vehicle_In.Passenger_Count := Passenger_Count_In;end Set_Values;

Page 95: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

function Get_Passenger_Count(Vehicle_In : CAR) return INTEGER isbegin return Vehicle_In.Passenger_Count;end Get_Passenger_Count;

-- Subprograms for the TRUCK recordprocedure Set_Values(Vehicle_In : in out TRUCK; Wheels_In : INTEGER; Weight_In : FLOAT; Passenger_Count_In : INTEGER; Payload_In : FLOAT) isbegin -- This is one way to set the values in the base class Vehicle_In.Wheels := Wheels_In; Vehicle_In.Weight := Weight_In;

-- This is another way to set the values in the base class Set_Values(TRANSPORT(Vehicle_In), Wheels_In, Weight_In);

-- This sets the values in this class Vehicle_In.Passenger_Count := Passenger_Count_In; Vehicle_In.Payload := Payload_In;end Set_Values;

function Get_Passenger_Count(Vehicle_In : TRUCK) return INTEGER isbegin return Vehicle_In.Passenger_Count;end Get_Passenger_Count;

-- Subprograms for the TRUCK recordfunction Get_Weight_In_Ounces(Vehicle_In : BICYCLE) return FLOAT isbegin return 16.0 * Vehicle_In.Weight;end Get_Weight_In_Ounces;

end CH22_2;

-- Result of execution---- (This program cannot be executed alone.)

Page 96: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Ada Tutorial - Chapter 23

MORE OBJECT ORIENTED PROGRAMMING

A CLASS WIDE PROCEDURE

Example program ------> e_c23_p1.ada

-- Chapter 23 - Program 1with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;

package Conveyance4 is

-- A very simple transportation record. type TRANSPORT is tagged private;

procedure Set_Values(Vehicle_In : in out TRANSPORT; Wheels_In : INTEGER; Weight_In : FLOAT); function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER; function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT;

-- Extend TRANSPORT to a CAR type. type CAR is new TRANSPORT with private;

procedure Set_Values(Vehicle_In : in out CAR; Passenger_Count_In : INTEGER); function Get_Passenger_Count(Vehicle_In : CAR) return INTEGER;

-- Extend TRANSPORT to a TRUCK type. type TRUCK is new TRANSPORT with private;

procedure Set_Values(Vehicle_In : in out TRUCK; Wheels_In : INTEGER; Weight_In : FLOAT; Passenger_Count_In : INTEGER; Payload_In : FLOAT);

function Get_Passenger_Count(Vehicle_In : TRUCK) return INTEGER;

-- Derive an identical type for BICYCLE. type BICYCLE is new TRANSPORT with private;

-- Print_Values is a class-wide operation. It can accept objects -- of any type within the TRANSPORT heirarchy. procedure Print_Values(Any_Vehicle : TRANSPORT'Class);

private

type TRANSPORT is tagged record Wheels : INTEGER; Weight : FLOAT; end record;

type CAR is new TRANSPORT with record Passenger_Count : INTEGER;

Page 97: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

end record;

type TRUCK is new TRANSPORT with record Passenger_Count : INTEGER; Payload : FLOAT; end record;

type BICYCLE is new TRANSPORT with null record;

end Conveyance4;

package body Conveyance4 is

-- Subprograms for the TRANSPORT recordprocedure Set_Values(Vehicle_In : in out TRANSPORT; Wheels_In : INTEGER; Weight_In : FLOAT) isbegin Vehicle_In.Wheels := Wheels_In; Vehicle_In.Weight := Weight_In;end Set_Values;

function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER isbegin return Vehicle_In.Wheels;end Get_Wheels;

function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT isbegin return Vehicle_In.Weight;end Get_Weight;

-- Subprograms for the CAR recordprocedure Set_Values(Vehicle_In : in out CAR; Passenger_Count_In : INTEGER) isbegin Vehicle_In.Passenger_Count := Passenger_Count_In;end Set_Values;

function Get_Passenger_Count(Vehicle_In : CAR) return INTEGER isbegin return Vehicle_In.Passenger_Count;end Get_Passenger_Count;

-- Subprograms for the TRUCK recordprocedure Set_Values(Vehicle_In : in out TRUCK; Wheels_In : INTEGER; Weight_In : FLOAT; Passenger_Count_In : INTEGER; Payload_In : FLOAT) isbegin -- This is one way to set the values in the base class Vehicle_In.Wheels := Wheels_In; Vehicle_In.Weight := Weight_In;

Page 98: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- This is another way to set the values in the base class Set_Values(TRANSPORT(Vehicle_In), Wheels_In, Weight_In);

-- This sets the values in this class Vehicle_In.Passenger_Count := Passenger_Count_In; Vehicle_In.Payload := Payload_In;end Set_Values;

function Get_Passenger_Count(Vehicle_In : TRUCK) return INTEGER isbegin return Vehicle_In.Passenger_Count;end Get_Passenger_Count;

-- Print_Values is a class-wide operation. It can accept objects-- of any type within the TRANSPORTheirarchy.procedure Print_Values(Any_Vehicle : TRANSPORT'Class) isbegin Put("This vehicle has"); Put(Any_Vehicle.Wheels, 2); Put(" wheels, and weighs"); Put(Any_Vehicle.Weight, 5, 1, 0); Put(" pounds."); New_Line;

-- The following line of code will produce an error because TRANSPORT-- and BICYCLE do not contain this variable.-- Put(Any_Vehicle.Passenger_Count, 5);

end Print_Values;

end Conveyance4;

-- Result of execution---- (This program cannot be executed alone.)

Examine the example program named e_c23_p1.ada for yet another new construct available in Ada 95, the class wide procedure. By this time you should be very familiar with the record definitions in the package specification, where we declare 4 records in lines 8, 18, 26, and 38, three of which are descended from the parent, which is of type TRANSPORT. Ada calls the combination of all four of these records a class because they all descend from a common parent. In fact, they are all descended from the type TRANSPORT so the combination of all four are referred to as the TRANSPORT class.

The procedure named Print_Values is declared in line 43, and it uses a single parameter with a very odd looking type, namely the type TRANSPORT'Class. This says that an actual parameter of any type of the TRANSPORT class can be passed into this procedure, which means that any type that inherits TRANSPORT either directly or indirectly is a candidate for this parameter. The implementation for the Print_Values procedure is given in lines 136 through 149 where some of the components of the parameter named Any_Vehicle are displayed on the monitor.

There are several facts that must be considered when studying this procedure. The first one is that it is not possible to remove any data components when one type is inherited into another type. Elements can be added, but they cannot be removed. Next, a variable of any of the four types within the TRANSPORT class can be passed in as the actual parameter, and the only components that will

Page 99: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

exist in all four are those in the parent type itself. The CAR type for example, may have additional components that are not a part of the parent type, so those are not available in all four types. Therefore, it is not legal to use any components within this procedure unless they are a part of the parent type, because only those are guaranteed to be a part of all types of the class. Wheels and Weight are therefore the only variables that can be used in this procedure. A very brief message is displayed on the monitor containing both of these values, as a trivial example. You will notice that line 147 is commented out because the component named Passenger_Count is not available in some of the types, and can not be used in this procedure which is designed to accommodate any variable of any one of these four types.

The rest of this package is very similar to those studied in the previous chapter of this tutorial. The diligent student should understand it thoroughly by now.

USING THE CLASS WIDE PROCEDURE

Example program ------> e_c23_p2.ada

-- Chapter 23 - Program 2

with Ada.Text_IO, Ada.Integer_Text_IO, Conveyance4;use Ada.Text_IO, Ada.Integer_Text_IO, Conveyance4;

procedure Vehicle4 is

Hummer : TRANSPORT; Limo : CAR; Chevy : CAR; Dodge : TRUCK; Ford : TRUCK;

begin Set_Values(Hummer, 4, 5760.0);

Set_Values(Limo, 8); Set_Values(TRANSPORT(Limo), 4, 3750.0); Set_Values(Chevy, 5); Set_Values(TRANSPORT(Chevy), 4, 2560.0);

Set_Values(Dodge, 6, 4200.0, 3, 1800.0); Set_Values(Ford, 4, 2800.0, 3, 1100.0);

-- Print out the data for the Ford truck just to pick on one -- of the objects for demonstration purposes. Put("The Ford truck has"); Put(Get_Wheels(Ford), 2); Put(" wheels, and can carry"); Put(Get_Passenger_Count(Ford), 2); Put(" passengers."); New_Line; -- Now, let's call the class-wide procedure 5 times. Print_Values(Hummer); Print_Values(Limo); Print_Values(Chevy); Print_Values(Dodge); Print_Values(Ford);

end Vehicle4;

Page 100: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Result of execution---- The Ford truck has 4 wheels and can carry 3 passengers.-- This vehicle has 4 wheels, and weighs 5760.0 pounds.-- This vehicle has 4 wheels, and weighs 3750.0 pounds.-- This vehicle has 4 wheels, and weighs 2560.0 pounds.-- This vehicle has 6 wheels, and weighs 4200.0 pounds.-- This vehicle has 4 wheels, and weighs 2800.0 pounds.

The example program named e_c23_p2.ada should be very familiar to you if you just completed a study of the previous chapter. The only real difference from e_c22_p7.ada is given in lines 36 through 40 where the same procedure is called with objects of three different types. The class wide procedure named Print_Values is capable of accepting a parameter of any type within the TRANSPORT class, and all five variables qualify for this.

When the program is executed, you will see that it does print the data values for each of the variables correctly even though they are of different types. It is therefore legal to pass a variable of any type into this procedure, provided that the type is a descendant of the TRANSPORT class no matter how far down the line it is.

DYNAMIC DISPATCHING?

Example program ------> e_c23_p3.ada

-- Chapter 23 - Program 3

with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;

package Conveyance5 is

-- Begin with a basic transportation type. type TRANSPORT is tagged private;

procedure Set_Values(Vehicle_In : in out TRANSPORT; Wheels_In : INTEGER); function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER; procedure Describe(Vehicle_In : TRANSPORT);

-- Extend the basic type to a CAR type. type CAR is new TRANSPORT with private;

procedure Set_Values(Vehicle_In : in out CAR; Passenger_Count_In : INTEGER); function Get_Passenger_Count(Vehicle_In : CAR) return INTEGER; procedure Describe(Vehicle_In : CAR);

-- Extend the basic type to a TRUCK type. type TRUCK is new TRANSPORT with private;

procedure Set_Values(Vehicle_In : in out TRUCK; Wheels_In : INTEGER; Passenger_Count_In : INTEGER); function Get_Passenger_Count(Vehicle_In : TRUCK) return INTEGER; procedure Describe(Vehicle_In : TRUCK);

-- Extend the basic type to the BICYCLE type. type BICYCLE is new TRANSPORT with private;

Page 101: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

procedure Describe(Vehicle_In : BICYCLE);

-- Print_Values is a class-wide operation. It can accept objects -- of any type within the TRANSPORT heirarchy. procedure Print_Values(Any_Vehicle : TRANSPORT'Class);

private

type TRANSPORT is tagged record Wheels : INTEGER; end record;

type CAR is new TRANSPORT with record Passenger_Count : INTEGER; end record;

type TRUCK is new TRANSPORT with record Passenger_Count : INTEGER; end record;

type BICYCLE is new TRANSPORT with null record;

end Conveyance5;

package body Conveyance5 is

-- Subprograms for the TRANSPORT recordprocedure Set_Values(Vehicle_In : in out TRANSPORT; Wheels_In : INTEGER) isbegin Vehicle_In.Wheels := Wheels_In;end Set_Values;

function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER isbegin return Vehicle_In.Wheels;end Get_Wheels;

procedure Describe(Vehicle_In : TRANSPORT) isbegin Put("We are in the TRANSPORT procedure."); new_Line;end Describe;

-- Subprograms for the CAR recordprocedure Set_Values(Vehicle_In : in out CAR; Passenger_Count_In : INTEGER) isbegin Vehicle_In.Passenger_Count := Passenger_Count_In;end Set_Values;

Page 102: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

function Get_Passenger_Count(Vehicle_In : CAR) return INTEGER isbegin return Vehicle_In.Passenger_Count;end Get_Passenger_Count;

procedure Describe(Vehicle_In : CAR) isbegin Put("We are in the CAR procedure."); new_Line;end Describe;

-- Subprograms for the TRUCK recordprocedure Set_Values(Vehicle_In : in out TRUCK; Wheels_In : INTEGER; Passenger_Count_In : INTEGER) isbegin -- This is one way to set the values in the base class Vehicle_In.Wheels := Wheels_In;

-- This is another way to set the values in the base class Set_Values(TRANSPORT(Vehicle_In), Wheels_In);

-- This sets the values in this class Vehicle_In.Passenger_Count := Passenger_Count_In;end Set_Values;

function Get_Passenger_Count(Vehicle_In : TRUCK) return INTEGER isbegin return Vehicle_In.Passenger_Count;end Get_Passenger_Count;

procedure Describe(Vehicle_In : TRUCK) isbegin Put("We are in the TRUCK procedure."); new_Line;end Describe;

-- Subprograms for the BICYCLE recordprocedure Describe(Vehicle_In : BICYCLE) isbegin Put("We are in the BICYCLE procedure."); New_Line;end Describe;

-- Print_Values is a class-wide operation. It can accept objects-- of any type within the TRANSPORT heirarchy.procedure Print_Values(Any_Vehicle : TRANSPORT'Class) isbegin-- Describe(Any_Vehicle);

Put("This vehicle has"); Put(Any_Vehicle.Wheels, 2); Put(" wheels."); New_Line;

Page 103: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- The following line of code will produce an error because TRANSPORT-- and BICYCLE do not contain this variable.-- Put(Any_Vehicle.Passenger_Count, 5);

end Print_Values;

end Conveyance5;

-- Result of execution---- (This program cannot be executed alone.)

The example program named e_c23_p3.ada introduces yet another Ada construct into our repertoire, the concept of dynamic binding, or polymorphism. In the last program we called one procedure with several different types, but in this program, we will call several different procedures with one call, by letting the system figure out which procedure to call for us.

This example is identical to e_c23_p1.ada except for the addition of four new procedures, one for each type. The specifications for these procedures are given in lines 14, 23, 33, and 39. You will notice that they are all identical except for the fact that each uses a different type for the single parameter to be passed in. Notice also that all of the parameter types are in the TRANSPORT class. The implementation for one of these four procedures is given in lines 85 through 89, and you can easily find the three other implementations. You will notice that the implementation for each of the procedures is different because of the difference in the string to be printed. It is important to note that the code within the body is different in each case, but the interface is identical.

USING THE DYNAMIC DISPATCHING

Example program ------> e_c23_p4.ada

-- Chapter 23 - Program 4

with Ada.Text_IO, Ada.Integer_Text_IO, Conveyance5;use Ada.Text_IO, Ada.Integer_Text_IO, Conveyance5;

procedure Vehicle5 is

Hummer : aliased TRANSPORT; Limo : aliased CAR; Chevy : aliased CAR; Dodge : aliased TRUCK; Ford : aliased TRUCK; Schwinn : aliased BICYCLE;

type TRANSPORT_ACCESS is access all TRANSPORT'Class; Any_Pt : TRANSPORT_ACCESS;

begin Set_Values(Hummer, 4);

Set_Values(Limo, 8); Set_Values(TRANSPORT(Limo), 4); Set_Values(Chevy, 5); Set_Values(TRANSPORT(Chevy), 4);

Set_Values(Dodge, 6, 3); Set_Values(Ford, 4, 3);

Set_Values(Schwinn, 2);

Page 104: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Print out the data for the Ford truck just to pick on one. Put("The Ford truck has"); Put(Get_Wheels(Ford), 2); Put(" wheels, and can carry"); Put(Get_Passenger_Count(Ford), 2); Put(" passengers."); New_Line; -- Now, let's call the class-wide procedure 6 times. Print_Values(Hummer); Print_Values(Limo); Print_Values(Chevy); Print_Values(Dodge); Print_Values(Ford); Print_Values(Schwinn);

Any_Pt := Hummer'Access; Describe(Any_Pt.all); Any_Pt := Limo'Access; Describe(Any_Pt.all); Any_Pt := Chevy'Access; Describe(Any_Pt.all); Any_Pt := Dodge'Access; Describe(Any_Pt.all); Any_Pt := Ford'Access; Describe(Any_Pt.all); Any_Pt := Schwinn'Access; Describe(Any_Pt.all);

end Vehicle5;

-- Result of execution---- The Ford truck has 4 wheels and can carry 3 passengers.-- This vehicle has 4 wheels.-- This vehicle has 4 wheels.-- This vehicle has 4 wheels.-- This vehicle has 6 wheels.-- This vehicle has 4 wheels.-- This vehicle has 2 wheels.-- We are in the TRANSPORT procedure.-- We are in the CAR procedure.-- We are in the CAR procedure.-- We are in the TRUCK procedure.-- We are in the TRUCK procedure.-- We are in the BICYCLE procedure.

The example program named e_c23_p4.ada will require a good bit of explanation, because there is a lot of new material here. In lines 8 through 13 we define 6 variables using the four types that are part of the TRANSPORT class. We also state explicitly that each of the variables is aliased, a reserved word that we studied when we covered the access type in this tutorial. You will recall that this permits access type variables to access these variables indirectly.

We define a new access type in line 15 that can access any variable of the right type, provided it is aliased, including those allocated on the heap, on the stack, or globally. In addition, it accesses the TRANSPORT'Class which means that it can access any variable of any type within that class,

Page 105: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

provided that variable has been defined to be aliased. Finally, to complete our data definitions, we define an access variable of the new type for use in the program.

The example program is the same as e_c23_p2.ada until we get to line 47 where interesting things begin to happen. The code in line 47 assigns Any_Pt the address of the Hummer, and line 48 uses that address to make a call to the procedure named Describe. You will recall that we have four different procedures named Describe, but only one that has a TRANSPORT type for its parameter. Since the Hummer is of that type, that is the procedure that will be called here, and the procedure is dynamically selected to be executed. In line 49 we assign Any_Pt the address of the Limo and use it as the parameter for the Describe call. This time the procedure associated with the CAR type is called. This continues with all six variables and the program is complete.

WHAT REALLY HAPPENED?

You will notice that lines 48, 50, 52, 54, 56, and 58 are identical. The only difference in these six lines of code is the type of the pointer when the call is made, and the type of the pointer is used to select the procedure to be executed. This selection is done at run-time and is usually called run-time binding or polymorphism. It seems like we went to a lot of trouble to do this, and we did, but we will find that this is a very valuable technique for some programming situations.

A little repetition is in order here since this is new material. In lines 40 through 45 we made many different calls to one procedure, but in lines 48 through 58 we made several repeats of one call, each of which was dispatched to several different procedures. This is sometimes called dynamic selection because the selection is made at run time, as opposed to static selection where the selection is made at compile time. It is often referred to as polymorphism which means many forms of similar entities.

A DYNAMIC BASE CLASS

Example program ------> e_c23_p5.ada

-- Chapter 23 - Program 5

with Ada.Text_IO;use Ada.Text_IO;

package Person is

type EMPLOYEE is tagged private;

procedure Display(Person_In : EMPLOYEE);

private

type EMPLOYEE is tagged record Name : STRING(1..25); Name_Length : INTEGER; Salary : INTEGER; end record;

end Person;

package body Person is

procedure Display(Person_In : EMPLOYEE) isbegin Put("This message should never be displayed!");end Display;

Page 106: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

end Person;

-- Result of execution---- (This package cannot be executed alone.)

Examine the file named e_c23_p5.ada to see the beginning package for a final examination of dynamic binding, at least for the present time. You will notice that we declared a simple type named EMPLOYEE with three components and a single procedure. It is declared to be tagged which will permit us to inherit this type into other types to build a class of types. This will be used to illustrate dynamic binding once more. The message printed by the procedure states that it should never be displayed. We will return to this later and illustrate a method by which the compiler will prevent that from happening.

TYPE EXTENSION

Example program ------> e_c23_p6.ada

-- Chapter 23 - Program 6

with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

package Person.Positions is

-- The SUPERVISOR type has a title. type SUPERVISOR is new EMPLOYEE with private;

procedure Init_Data(In_Person : in out SUPERVISOR; In_Name : STRING; In_Salary : INTEGER; In_Title : STRING); procedure Display(In_Person : SUPERVISOR);

-- The PROGRAMMER type has a language preference. type PROGRAMMER is new EMPLOYEE with private;

procedure Init_Data(In_Person : in out PROGRAMMER; In_Name : STRING; In_Salary : INTEGER; In_Title : STRING; In_Language : STRING); procedure Display(In_Person : PROGRAMMER);

-- The SECRETARY type has a typing speed. type SECRETARY is new EMPLOYEE with private;

procedure Init_Data(In_Person : in out SECRETARY; In_Name : STRING; In_Salary : INTEGER; In_ShortHand : BOOLEAN; In_Speed : INTEGER); procedure Display(In_Person : SECRETARY);

private

type SUPERVISOR is new EMPLOYEE with

Page 107: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

record Title : STRING(1..25); Title_Length : INTEGER; end record;

type PROGRAMMER is new EMPLOYEE with record Title : STRING(1..25); Title_Length : INTEGER; Language : STRING(1..25); Language_Length : INTEGER; end record;

type SECRETARY is new EMPLOYEE with record Shorthand : BOOLEAN; Typing_Speed : INTEGER; end record; end Person.Positions;

package body Person.Positions is

-- Subprograms for the SUPERVISOR type.procedure Init_Data(In_Person : in out SUPERVISOR; In_Name : STRING; In_Salary : INTEGER; In_Title : STRING) isbegin

In_Person.Name_Length := In_Name'Length; for Index in In_Name'Range loop In_Person.Name(Index) := In_Name(Index); end loop;

In_Person.Salary := In_Salary;

In_Person.Title_Length := In_Title'Length; for Index in In_Title'Range loop In_Person.Title(Index) := In_Title(Index); end loop;

end Init_Data;

procedure Display(In_Person : SUPERVISOR) isbegin

for Index in 1..In_Person.Name_Length loop Put(In_Person.Name(Index)); end loop;

Put(" is a supervisor, and is the ");

for Index in 1..In_Person.Title_Length loop Put(In_Person.Title(Index)); end loop;

Put(" of the company"); New_Line;

Page 108: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

end Display;

-- Subprograms for the PROGRAMMER type.procedure Init_Data(In_Person : in out PROGRAMMER; In_Name : STRING; In_Salary : INTEGER; In_Title : STRING; In_Language : STRING) isbegin

In_Person.Name_Length := In_Name'Length; for Index in In_Name'Range loop In_Person.Name(Index) := In_Name(Index); end loop;

In_Person.Salary := In_Salary;

In_Person.Title_Length := In_Title'Length; for Index in In_Title'Range loop In_Person.Title(Index) := In_Title(Index); end loop;

In_Person.Language_Length := In_Language'Length; for Index in In_Language'Range loop In_Person.Language(Index) := In_Language(Index); end loop;

end Init_Data;

procedure Display(In_Person : PROGRAMMER) isbegin for Index in 1..In_Person.Name_Length loop Put(In_Person.Name(Index)); end loop;

Put(" is a programmer specializing in ");

for Index in 1..In_Person.Language_Length loop Put(In_Person.Language(Index)); end loop;

Put(". He makes "); Put(In_Person.Salary, 6); Put(" dollars per year."); New_Line;end Display;

-- Subprograms for the SECRETARY type.procedure Init_Data(In_Person : in out SECRETARY; In_Name : STRING; In_Salary : INTEGER; In_ShortHand : BOOLEAN; In_Speed : INTEGER) isbegin

Page 109: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

In_Person.Name_Length := In_Name'Length; for Index in In_Name'Range loop In_Person.Name(Index) := In_Name(Index); end loop;

In_Person.Salary := In_Salary; In_Person.Shorthand := In_Shorthand; In_Person.Typing_Speed := In_Speed;

end Init_Data;

procedure Display(In_Person : SECRETARY) isbegin

for Index in 1.. In_Person.Name_Length loop Put(In_Person.Name(Index)); end loop;

Put(" is a secretary that does "); if not In_Person.Shorthand then Put("not "); end if; Put("take shorthand."); New_Line;

Put(" "); for Index in 1..In_Person.Name_Length loop Put(In_Person.Name(Index)); end loop;

Put(" is paid "); Put(In_Person.Salary, 6); Put(" dollars per year."); New_Line;

end Display;

end Person.Positions;

-- Result of execution---- (This package cannot be executed alone.)

The EMPLOYEE type mentioned above is located in the Person package, and the three types in the file named e_c23_p6.ada are located in the package named Person.Positions as indicated by line 6. We are using the hierarchical libraries available with Ada 95 which could be used to prevent any possibility of name clashes. The package specification for Person.Positions is very straightforward and you should be able to understand this package on your own. Notice that there are three procedures named Display, each with a different type for the formal parameter but otherwise the interfaces are identical.

The body for the Person.Positions package is not so trivial because of the way strings are defined in Ada. We mentioned earlier in this tutorial that strings could not be assigned to variables unless they are all of the same length, or the Ada compiler will issue a type incompatibility error. If we were building an address list, it would not be very expedient to require everybody to have the same number of letters in their first name and the same number of letters in their last name. The STRING

Page 110: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

type available with Ada must be improved upon in order to make it convenient to use strings in a meaningful Ada program. We will see how to do that shortly, but in the meantime, we will suffer through this example program with the current STRING type implementation to show you how difficult they are to work with. Since we wish to use variable length strings, we will define each string a little longer than they need to be and associate a counter with each string variable to store the current length of the string.

Line 43 defines a string variable named Title that can store up to 25 characters and line 44 contains a variable named Title_Length that will store the current length of the string. If we wanted to store the name "John", we would put those four characters in the first four locations of Title and store a value of 4 in the Title_Length variable. We must then diligently define a length variable to be associated with each string used in this program, and that is exactly what we do. In line 75, we use the Length attribute to determine the number of characters passed in from the calling program, and execute a loop to copy the characters one by one from the input string to the internal storage string. You will notice in line 80, that it is trivial to store away the value of the Salary.

When we get to the Display procedure, we are required to copy the characters to the monitor one at a time as is illustrated in lines 92 through 94. This is not very convenient, and it is somewhat error prone because it would be very easy to use a wrong count with some particular string. Even though the procedures are longer than they really should be, they are very simple, so you should have no trouble understanding what the package body does.

A SIMPLE TEST PROGRAM FOR THE CLASS

Example program ------> e_c23_p7.ada

-- Chapter 23 - Program 7

with Ada.Text_IO, Person, Person.Positions;use Ada.Text_IO, Person, Person.Positions;

procedure Busines1 is

Big_John : SUPERVISOR; Jessica : SUPERVISOR; Steve : PROGRAMMER; Patrick : PROGRAMMER; Gwen : SECRETARY;

begin

Init_Data(Big_John, "John", 54000, "President"); Init_Data(Jessica, "Jessica", 47500, "CEO"); Init_Data(Steve, "Steve", 52000, "Chief Programmer", "Ada"); Init_Data(Patrick, "Patrick", 33000, "Assistant Debugger","C++"); Init_Data(Gwen, "Gwendolyn", 27000, TRUE, 85);

Display(Big_John); Display(Jessica); Display(Steve); Display(Patrick); Display(Gwen);

end Busines1;

-- Result of execution---- John is a supervisor, and is the President of the company

Page 111: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Jessica is a supervisor, and is the CEO of the company-- Steve is a programmer specializing in Ada. He makes 52000 dollars per year.-- Patrick is a programmer specializing in C++. He makes 33000 dollars per year.-- Gwendolyn is a secretary that does take shorthand.-- Gwendolyn is paid 27000 dollars per year.

The file named e_c23_p7.ada is a very simple program that exercises the EMPLOYEE class that we have just defined, but it really doesn't exercise it very much. It defines a few variables in lines 8 through 12, fills them with data in lines 16 through 20, and displays the data on the monitor in lines 22 through 26. The calls to Display are nothing special, in fact they only illustrate subprogram name overloading. There is no polymorphism being used here.

You should compile these three files (e_c23_p5.ada, e_c23_p6.ada, and BUSINESS1.ADA) and link them together, because they are the basis for some additional operations which we will study in the remainder of this chapter.

USING DYNAMIC DISPATCHING

Example program ------> e_c23_p8.ada

-- Chapter 23 - Program 8

with Ada.Text_IO, Person, Person.Positions;use Ada.Text_IO, Person, Person.Positions;

procedure Busines2 is

Big_John : aliased SUPERVISOR; Jessica : aliased SUPERVISOR; Steve : aliased PROGRAMMER; Patrick : aliased PROGRAMMER; Gwen : aliased SECRETARY;

type EMPLOYEE_ACCESS is access all EMPLOYEE'Class; Employee_Point : EMPLOYEE_ACCESS;

begin

Init_Data(Big_John, "John", 54000, "President"); Init_Data(Jessica, "Jessica", 47500, "CEO"); Init_Data(Steve, "Steve", 52000, "Chief Programmer", "Ada"); Init_Data(Patrick, "Patrick", 33000, "Assistant Debugger", "C++"); Init_Data(Gwen, "Gwendolyn", 27000, TRUE, 85);

Employee_Point := Big_John'Access; Display(Employee_Point.all); Employee_Point := Jessica'Access; Display(Employee_Point.all); Employee_Point := Steve'Access; Display(Employee_Point.all); Employee_Point := Patrick'Access; Display(Employee_Point.all); Employee_Point := Gwen'Access; Display(Employee_Point.all);

end Busines2;

Page 112: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Result of execution---- John is a supervisor, and is the President of the company-- Jessica is a supervisor, and is the CEO of the company-- Steve is a programmer specializing in Ada. He makes 52000 dollars per year.-- Patrick is a programmer specializing in C++. He makes 33000 dollars per year.-- Gwendolyn is a secretary that does take shorthand.-- Gwendolyn is paid 27000 dollars per year.

Examine the file named BUSINESS2.ADA for another example of dynamic dispatching. We did nothing special in the last three example programs to prepare for dynamic dispatching but we will use the first two files unchanged to illustrate the use of dynamic dispatching. The file named e_c23_p8.ada begins by declaring all of the variables as being aliased so that an Ada access variable can access them within the program. We define an access type for the EMPLOYEE class in line 14, and use that type to define a variable named Employee_Point in line 15. We initialize all of the variables as before and we are ready to display the data.

We use the same access variable to access each variable in succession and display the data in each variable. You will notice that the exact same line of code in used in lines 26, 28, 30, 32, and 34, but it does not call the same actual procedure each time one of those lines are executed. This is, of course, because of the way dynamic dispatching works. The procedure that matches the type of the current value of the access variable is the procedure that will get called.

We used the same two files for the type definitions for each of the last two main programs, but one used dynamic dispatching, and the other did not. This program is meant to illustrate that there is nothing magic about the elements of the class that will be used for dynamic dispatching, the critical portions are in the calling program.

DON'T USE THIS PROCEDURE

Example program ------> e_c23_p9.ada

-- Chapter 23 - Program 9

package Person is

type EMPLOYEE is abstract tagged private;

procedure Display(Person_In : EMPLOYEE) is abstract;

private

type EMPLOYEE is abstract tagged record Name : STRING(1..25); Name_Length : INTEGER; Salary : INTEGER; end record;

end Person;

-- Result of execution---- (This package cannot be executed alone.)

Return to the file named e_c23_p5.ada to discuss a little problem that we completely overlooked

Page 113: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

when we discussed it. Line 29 contains a string indicating that we don't expect anyone to call this procedure and we would construe it as an error if they did. In fact, we don't expect anyone to ever create an object of this type, because we don't care to work with EMPLOYEE types, only the more specific types defined in e_c23_p6.ada.

The example program named e_c23_p9.ada contains a new reserved word abstract that is used in lines 5 and 11 to indicate that this is an abstract record. Since it is abstract, it is not permissible to create a variable of this type, and attempting to do so will result in a compile error. The word abstract is also used in line 7 where it states that the procedure is abstract and can never be called. Since it will never be called, the procedure does not even need to have an implementation. With the body of the procedure not being needed, the package body is empty, so it is completely eliminated. There is then no need for the context clauses for the Ada.Text_IO package, so they are removed.

Any type that inherits the EMPLOYEE type must provide an implementation for Display or contain an abstract definition for it. Including an abstract definition for Display will make the new type an abstract record also, and we cannot create an object of the new type. However, including an implementation for Display, will make the new type a normal type which can be used to define one or more objects. The compiler will prevent you from ignoring the Display subprogram, and will require that you include it in every child record of EMPLOYEE.

The most surprising part of this modification is that it can still be used with the files named e_c23_p6.ada and e_c23_p7.ada or e_c23_p8.ada, with no changes to any of them. Since we never tried to create a variable of the EMPLOYEE type, there is nothing to change in those files. You should compile, link, and execute both groups of files to prove to yourself that it actually works as stated.

THIS ENDS THE OBJECT ORIENTED PROGRAMMING LESSONS

This completes our study of object oriented programming in this tutorial. There is a lot more to learn about object oriented programming and how to use it, but it is beyond the scope of this tutorial, and there is a plethora of information about the topic in other publications. The remainder of this chapter will be used to illustrate a better method of using strings in Ada. It is included here because these example programs lend themselves well to illustrating the particular construct we wish to consider.

RETURNING TO THE STRING PROBLEM

Even though it may seem like we have a STRING problem, we really don't, because the STRING type is working exactly as it was designed to work. The mark of a well designed language is not that it has the ability to do everything, but that it has the ability to be extended in an efficient manner to do anything that needs to be done. Ada was designed to be easily and robustly extended, and the area of strings is a very good example of this.

There are several string packages available with all Ada 95 compilers. They are all well defined in the ARM, and should be well defined in your compiler documentation. We will look at one of these packages to illustrate how it can be used to simplify the use of strings in an Ada program. The package named Ada.Strings.Bounded contains a generic package named Generic_Bounded_Length which implements a string package that is much more flexible than the STRING type. This string type stores an internal count of the number of characters that are currently significant, much like we did in the e_c23_p5.ada and e_c23_p6.ada files. it also provides a plethora of subprograms to load, extract, concatenate, and many other operations which are commonly needed when working with strings.

Example program ------> e_c23_p10.ada

-- Chapter 23 - Program 10with Ada.Strings.Bounded;

package Person is

Page 114: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

type EMPLOYEE is abstract tagged private;

procedure Display(Person_In : EMPLOYEE) is abstract; package My_Strings is new Ada.Strings.Bounded.Generic_Bounded_Length(25); use My_Strings;

private

type EMPLOYEE is abstract tagged record Name : BOUNDED_STRING; Salary : INTEGER; end record;

end Person;

-- Result of execution---- (This package cannot be executed alone.)

The file named e_c23_p10.ada uses the Generic_Bounded_Length package by instantiating a copy of it in line 10 which provides an upper limit of 25 characters for a string of this type. The package provides a type named BOUNDED_STRING which defines the strings we wish to use. Each string can be from 0 to 25 characters long, and the string object will remember how many characters are currently stored there. The variable named Name is defined as type BOUNDED_STRING in line 18. The full name of the type is My_Strings.BOUNDED_STRING.You will notice that the generic package is in the Ada.Strings.Bounded library, so that library is mentioned in the context clause in line 2. Instead of a use clause, the fully qualified name is used in lines 10 and 11. This is a little different than the way it is done in most of the tutorial, but either method is completely acceptable.

MORE DYNAMIC STRINGS

Example program ------> e_c23_p11.ada

-- Chapter 23 - Program 11

with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

package Person.Positions is

-- The SUPERVISOR type has a title. type SUPERVISOR is new EMPLOYEE with private;

procedure Init_Data(In_Person : in out SUPERVISOR; In_Name : BOUNDED_STRING; In_Salary : INTEGER; In_Title : BOUNDED_STRING); procedure Display(In_Person : SUPERVISOR);

-- The PROGRAMMER type has a language preference. type PROGRAMMER is new EMPLOYEE with private;

procedure Init_Data(In_Person : in out PROGRAMMER; In_Name : BOUNDED_STRING;

Page 115: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

In_Salary : INTEGER; In_Title : BOUNDED_STRING; In_Language : BOUNDED_STRING); procedure Display(In_Person : PROGRAMMER);

-- The SECRETARY type has a typing speed. type SECRETARY is new EMPLOYEE with private;

procedure Init_Data(In_Person : in out SECRETARY; In_Name : BOUNDED_STRING; In_Salary : INTEGER; In_ShortHand : BOOLEAN; In_Speed : INTEGER); procedure Display(In_Person : SECRETARY);

private

type SUPERVISOR is new EMPLOYEE with record Title : BOUNDED_STRING; end record;

type PROGRAMMER is new EMPLOYEE with record Title : BOUNDED_STRING; Language : BOUNDED_STRING; end record;

type SECRETARY is new EMPLOYEE with record Shorthand : BOOLEAN; Typing_Speed : INTEGER; end record; end Person.Positions;

package body Person.Positions is

-- Subprograms for the SUPERVISOR type.procedure Init_Data(In_Person : in out SUPERVISOR; In_Name : BOUNDED_STRING; In_Salary : INTEGER; In_Title : BOUNDED_STRING) isbegin In_Person.Name := In_Name; In_Person.Salary := In_Salary; In_Person.Title := In_Title;end Init_Data;

procedure Display(In_Person : SUPERVISOR) isbegin for Index in 1..Length(In_Person.Name) loop Put(Element(In_Person.Name, Index)); end loop;

Put(" is a supervisor, and is the ");

for Index in 1..Length(In_Person.Title) loop

Page 116: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Put(Element(In_Person.Title, Index)); end loop;

Put(" of the company"); New_Line;end Display;

-- Subprograms for the PROGRAMMER type.procedure Init_Data(In_Person : in out PROGRAMMER; In_Name : BOUNDED_STRING; In_Salary : INTEGER; In_Title : BOUNDED_STRING; In_Language : BOUNDED_STRING) isbegin In_Person.Name := In_Name; In_Person.Salary := In_Salary; In_Person.Title := In_Title; In_Person.Language := In_Language;end Init_Data;

procedure Display(In_Person : PROGRAMMER) isbegin for Index in 1..Length(In_Person.Name) loop Put(Element(In_Person.Name, Index)); end loop;

Put(" is a programmer specializing in ");

for Index in 1..Length(In_Person.Language) loop Put(Element(In_Person.Language, Index)); end loop;

Put(". He makes "); Put(In_Person.Salary, 6); Put(" dollars per year."); New_Line;end Display;

-- Subprograms for the SECRETARY type.procedure Init_Data(In_Person : in out SECRETARY; In_Name : BOUNDED_STRING; In_Salary : INTEGER; In_ShortHand : BOOLEAN; In_Speed : INTEGER) isbegin In_Person.name := In_Name; In_Person.Salary := In_Salary; In_Person.Shorthand := In_Shorthand; In_Person.Typing_Speed := In_Speed;

end Init_Data;

procedure Display(In_Person : SECRETARY) isbegin for Index in 1..Length(In_Person.Name) loop Put(Element(In_Person.Name, Index));

Page 117: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

end loop;

Put(" is a secretary that does "); if not In_Person.Shorthand then Put("not "); end if; Put("take shorthand."); New_Line;

Put(" "); for Index in 1..Length(In_Person.Name) loop Put(Element(In_Person.Name, Index)); end loop;

Put(" is paid "); Put(In_Person.Salary, 6); Put(" dollars per year."); New_Line;

end Display;

end Person.Positions;

-- Result of execution---- (This package cannot be executed alone.)

Examine the file named e_c23_p11.ada for the definition of the other three types, and you will notice that there are no STRING types in the package specification. They are replaced by the new type BOUNDED_STRING. Lines 71 through 73 of the package body illustrate the clean syntax that is possible with this new type because it has an assignment operator available that assigns not only the text to the new variable, but also the current length.

Since we have no way to output a variable of type BOUNDED_STRING to the monitor, we are forced to use a loop to output a single character at a time, but the loop uses attributes of the single variable rather than using two separate variables to control the output. This results in the Display procedures looking a little cleaner than they did in the last example program. The remainder of this package is simply more of the same that you can study at your leisure.

THE MAIN PROGRAM IS UGLY

Example program ------> e_c23_p12.ada

-- Chapter 23 - Program 12

with Ada.Text_IO, Person, Person.Positions;use Ada.Text_IO, Person, Person.Positions;

procedure Busines3 is

Big_John : SUPERVISOR; Jessica : SUPERVISOR; Steve : PROGRAMMER; Patrick : PROGRAMMER; Gwen : SECRETARY;

begin

Init_Data(Big_John, My_Strings.To_Bounded_String("John"),

Page 118: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

54000, My_Strings.To_Bounded_String("President"));

Init_Data(Jessica, My_Strings.To_Bounded_String("Jessica"), 47500, My_Strings.To_Bounded_String("CEO"));

Init_Data(Steve, My_Strings.To_Bounded_String("Steve"), 52000, My_Strings.To_Bounded_String("Chief Programmer"), My_Strings.To_Bounded_String("Ada"));

Init_Data(Patrick, My_Strings.To_Bounded_String("Patrick"), 33000, My_Strings.To_Bounded_String("Assistant Debugger"), My_Strings.To_Bounded_String("C++"));

Init_Data(Gwen, My_Strings.To_Bounded_String("Gwendolyn"), 27000, TRUE, 85);

Display(Big_John); Display(Jessica); Display(Steve); Display(Patrick); Display(Gwen);

end Busines3;

-- Result of execution---- John is a supervisor, and is the President of the company-- Jessica is a supervisor, and is the CEO of the company-- Steve is a programmer specializing in Ada. He makes 52000 dollars per year.-- Patrick is a programmer specializing in C++. He makes 33000 dollars per year.-- Gwendolyn is a secretary that does take shorthand.-- Gwendolyn is paid 27000 dollars per year.

Because of the need to maintain the correct types, it is necessary to convert the string constants in lines 16 through 31 into BOUNDED_STRING types. This is accomplished by calling the To_Bounded_String function for each of the string parameters prior to passing them into the Init_Data procedures. This looks ugly, and it seems to defeat the purpose of using the bounded string package. When writing a string intensive program, you will normally have a small input routine of some kind, and a small output routine, but there may be a massive amount of string processing going on within the program. A good example would be a spell checker where there is generally only a single word input, but an entire dictionary which must be searched.

In addition to the bounded string package, where you define a string type with some upper limit to the number of characters that can be stored within, there is an unbounded string package. The unbounded string can store any number of characters. It does this by growing longer automatically as you add more characters to its length. As you add characters, it reallocates a larger block each time it runs out of space in order for your string to fit. It will be left up to you to study this package if you wish to use it.

PROGRAMMING EXERCISES

1. Remove the comment from line 147 of e_c23_p1.ada to see what kind of an error the compiler issues.(Solution)

-- Chapter 23 - Exercise 1with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;

Page 119: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;

package CH23_1 is

-- A very simple transportation record. type TRANSPORT is tagged private;

procedure Set_Values(Vehicle_In : in out TRANSPORT; Wheels_In : INTEGER; Weight_In : FLOAT); function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER; function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT;

-- Extend TRANSPORT to a CAR type. type CAR is new TRANSPORT with private;

procedure Set_Values(Vehicle_In : in out CAR; Passenger_Count_In : INTEGER); function Get_Passenger_Count(Vehicle_In : CAR) return INTEGER;

-- Extend TRANSPORT to a TRUCK type. type TRUCK is new TRANSPORT with private;

procedure Set_Values(Vehicle_In : in out TRUCK; Wheels_In : INTEGER; Weight_In : FLOAT; Passenger_Count_In : INTEGER; Payload_In : FLOAT);

function Get_Passenger_Count(Vehicle_In : TRUCK) return INTEGER;

-- Derive an identical type for BICYCLE. type BICYCLE is new TRANSPORT with private;

-- Print_Values is a class-wide operation. It can accept objects -- of any type within the TRANSPORT heirarchy. procedure Print_Values(Any_Vehicle : TRANSPORT'Class);

private

type TRANSPORT is tagged record Wheels : INTEGER; Weight : FLOAT; end record;

type CAR is new TRANSPORT with record Passenger_Count : INTEGER; end record;

type TRUCK is new TRANSPORT with record Passenger_Count : INTEGER; Payload : FLOAT; end record;

type BICYCLE is new TRANSPORT with null record;

Page 120: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

end CH23_1;

package body CH23_1 is

-- Subprograms for the TRANSPORT recordprocedure Set_Values(Vehicle_In : in out TRANSPORT; Wheels_In : INTEGER; Weight_In : FLOAT) isbegin Vehicle_In.Wheels := Wheels_In; Vehicle_In.Weight := Weight_In;end Set_Values;

function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER isbegin return Vehicle_In.Wheels;end Get_Wheels;

function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT isbegin return Vehicle_In.Weight;end Get_Weight;

-- Subprograms for the CAR recordprocedure Set_Values(Vehicle_In : in out CAR; Passenger_Count_In : INTEGER) isbegin Vehicle_In.Passenger_Count := Passenger_Count_In;end Set_Values;

function Get_Passenger_Count(Vehicle_In : CAR) return INTEGER isbegin return Vehicle_In.Passenger_Count;end Get_Passenger_Count;

-- Subprograms for the TRUCK recordprocedure Set_Values(Vehicle_In : in out TRUCK; Wheels_In : INTEGER; Weight_In : FLOAT; Passenger_Count_In : INTEGER; Payload_In : FLOAT) isbegin -- This is one way to set the values in the base class Vehicle_In.Wheels := Wheels_In; Vehicle_In.Weight := Weight_In;

-- This is another way to set the values in the base class Set_Values(TRANSPORT(Vehicle_In), Wheels_In, Weight_In);

-- This sets the values in this class Vehicle_In.Passenger_Count := Passenger_Count_In; Vehicle_In.Payload := Payload_In;end Set_Values;

Page 121: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

function Get_Passenger_Count(Vehicle_In : TRUCK) return INTEGER isbegin return Vehicle_In.Passenger_Count;end Get_Passenger_Count;

-- Print_Values is a class-wide operation. It can accept objects-- of any type within the TRANSPORTheirarchy.procedure Print_Values(Any_Vehicle : TRANSPORT'Class) isbegin Put("This vehicle has"); Put(Any_Vehicle.Wheels, 2); Put(" wheels, and weighs"); Put(Any_Vehicle.Weight, 5, 1, 0); Put(" pounds."); New_Line;

-- The following line of code will produce an error because TRANSPORT-- and BICYCLE do not contain this variable. Put(Any_Vehicle.Passenger_Count, 5);

end Print_Values;

end CH23_1;

-- Result of execution---- (This program cannot be executed alone.)

2. Define a variable of the EMPLOYEE type in the BUSINESS2.ADA file when you are using the e_c23_p5.ada file as the definition of that type. Call the Display procedure to see that it can be used as well as the child types.(Solution)

-- Chapter 23 - Exercise 2

with Ada.Text_IO, Person, Person.Positions;use Ada.Text_IO, Person, Person.Positions;

procedure CH23_2 is

Big_John : aliased SUPERVISOR; Jessica : aliased SUPERVISOR; Steve : aliased PROGRAMMER; Patrick : aliased PROGRAMMER; Gwen : aliased SECRETARY; Willy : EMPLOYEE;

type EMPLOYEE_ACCESS is access all EMPLOYEE'Class; Employee_Point : EMPLOYEE_ACCESS;

begin

Init_Data(Big_John, "John", 54000, "President"); Init_Data(Jessica, "Jessica", 47500, "CEO"); Init_Data(Steve, "Steve", 52000, "Chief Programmer", "Ada"); Init_Data(Patrick, "Patrick", 33000, "Assistant Debugger", "C++"); Init_Data(Gwen, "Gwendolyn", 27000, TRUE, 85);

Page 122: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Display(Willy);

Employee_Point := Big_John'Access; Display(Employee_Point.all); Employee_Point := Jessica'Access; Display(Employee_Point.all); Employee_Point := Steve'Access; Display(Employee_Point.all); Employee_Point := Patrick'Access; Display(Employee_Point.all); Employee_Point := Gwen'Access; Display(Employee_Point.all);

end CH23_2;

-- Result of execution---- John is a supervisor, and is the President of the company-- Jessica is a supervisor, and is the CEO of the company-- Steve is a programmer specializing in Ada. He makes 52000 dollars per year.-- Patrick is a programmer specializing in C++. He makes 33000 dollars per year.-- Gwendolyn is a secretary that does take shorthand.-- Gwendolyn is paid 27000 dollars per year.

3. Define a variable of the EMPLOYEE type in the BUSINESS2.ADA file when you are using the e_c23_p9.ada file as the definition of that type. Does the error reported make sense to you?(Solution)

-- Chapter 23 - Exercise 3

with Ada.Text_IO, Person, Person.Positions;use Ada.Text_IO, Person, Person.Positions;

procedure CH23_3 is

Big_John : aliased SUPERVISOR; Jessica : aliased SUPERVISOR; Steve : aliased PROGRAMMER; Patrick : aliased PROGRAMMER; Gwen : aliased SECRETARY; Willy : EMPLOYEE;

type EMPLOYEE_ACCESS is access all EMPLOYEE'Class; Employee_Point : EMPLOYEE_ACCESS;

begin

Init_Data(Big_John, "John", 54000, "President"); Init_Data(Jessica, "Jessica", 47500, "CEO"); Init_Data(Steve, "Steve", 52000, "Chief Programmer", "Ada"); Init_Data(Patrick, "Patrick", 33000, "Assistant Debugger", "C++"); Init_Data(Gwen, "Gwendolyn", 27000, TRUE, 85);

Employee_Point := Big_John'Access; Display(Employee_Point.all); Employee_Point := Jessica'Access;

Page 123: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Display(Employee_Point.all); Employee_Point := Steve'Access; Display(Employee_Point.all); Employee_Point := Patrick'Access; Display(Employee_Point.all); Employee_Point := Gwen'Access; Display(Employee_Point.all);

end CH23_3;

-- Result of execution---- John is a supervisor, and is the President of the company-- Jessica is a supervisor, and is the CEO of the company-- Steve is a programmer specializing in Ada. He makes 52000 dollars per year.-- Patrick is a programmer specializing in C++. He makes 33000 dollars per year.-- Gwendolyn is a secretary that does take shorthand.-- Gwendolyn is paid 27000 dollars per year.

Page 124: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Ada Tutorial - Chapter 24

BINARY INPUT/OUTPUT

Any useful computer program must have a way to get data into it to operate on, and a way to get the results out to the user. In part 1 of this tutorial, we studied how to get text in and out of the computer, now we will see how to get binary data in and out of the computer. Most of the binary input and output will be to or from files because the data is always in a machine readable format, not one that a human reader can easily comprehend. A block of binary data could be transmitted to a hardware device that is designed to respond to the data, or a block of data could be input from a hardware device reporting on some process. A little more will be said about that later.

BINARY DATA OUTPUT

Example program ------> e_c24_p1.ada

-- Chapter 24 - Program 1with Ada.Text_IO;use Ada.Text_IO;with Ada.Sequential_IO;

procedure BiSeqOut is

type MY_REC is record Age : INTEGER; Sex : CHARACTER; Initial : CHARACTER; end record;

package Seq_IO is new Ada.Sequential_IO(MY_REC); use Seq_IO;

Myself : MY_REC; My_Out_File : Seq_IO.FILE_TYPE;

begin

Create(My_Out_File, Out_File, "NAMEFILE.TXT");

Myself.Sex := 'M'; Myself.Initial := 'X';

for Index in 1..100 loop Myself.Age := Index; Write(My_Out_File, Myself); end loop;

Close(My_Out_File);

end BiSeqOut;

-- Result of Execution

-- (The only output is a binary file named NAMEFILE.TXT)

Examine the program named e_c24_p1.ada for an example of outputting binary sequential data.

Page 125: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Ada gives you five data input/output library packages that are defined in the Ada 95 Reference Manual (ARM), and required to be available with every Ada compiler. They are listed by package name as follows;

• Ada.Text_IO - This is used for text Input/Output and is always in a sequential mode of operation. Any file can be used for input or output, but not both. (This package has been in use during most of this tutorial and was specifically described in chapter 14.)

• Ada.Sequential_IO - This is used for binary Input/Output and is always in a sequential mode of operation. Any file can be used for input or output, but not both.

• Ada.Direct_IO - This is used for binary Input/Output and can be used in either a sequential or random mode of operation. Any file can be used for input, output, or input and output.

• Ada.Streams.Stream_IO - This is used for either binary or text Input/Output but permits the use of heterogeneous data whereas the other three require all data to be of the same type. for any given file. In the case of Ada.Text_IO, the data must all be of the CHARACTER type, so it must be of a single type also.

• Ada.Text_IO.Text_Streams - This permits a text file to be used for stream Input/Output so it can be mixed with binary data in the same file.

You should refer to either your compiler documentation or the ARM for the definition of these packages. All definitions are found in Annex A. Spend a little time studying the procedures and functions available with each to become familiar with the capability of each package. The knowledge you have gained studying this tutorial to this point should enable you to understand most of the specifications of these five packages.

THE PACKAGE NAMED Low_Level_IO

There is another input/output package that may be available with your compiler named Ada.Low_Level_IO. This is not required by the ARM, but if it exists, it will be used for machine dependent input/output programming with your particular implementation. If it exists with your compiler, the description of how to use it will be included with your documentation, and since it will be completely different with each compiler, no attempt will be made to explain its use here.

BACK TO e_c24_p1.ada

Although this program does very little, there are several steps that must be performed to output to a binary file. They will be taken in order, so follow along closely. First we must tell the system that we wish to use the external package Ada.Sequential_IO, which we do using a with clause in line 4. Next we define a record type to illustrate one kind of data that can be output to the file, and declare a variable of the record type in line 18.

In order to use the sequential input/output package, we must instantiate a copy of it, because it is a generic package and cannot be used directly. We do this in line 15, using the data type we wish to output to the file, then add the use clause in line 16 to make the new package readily available. We need an internal file name, and we define it in line 19, but with a minor difficulty because we have an overloaded file type available. We have made Ada.Text_IO available to illustrate the problem, even though we don't use it in this program, because it contains a type named FILE_TYPE. The package named Seq_IO that we have instantiated also contains a type named FILE_TYPE, the one we wish to use. In order to tell the system which one we want, we must use the extended naming notation to differentiate between the two types. This is done in line 19 of the example program. With these steps, we are ready to begin the executable part of the program.

In a manner similar to that used for text files which we studied earlier, we create the file in line 23, in this case using the mode Out_File since we wish to write to the file. As you recall, this also ties the internal name for the file to the external name we have chosen, NAMEFILE.TXT, which must follow the conventions for our particular Ada compiler and operating system. If your operating system is substantially different, you may need to change this name. We are finally ready to actually

Page 126: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

use the output file, so we load some nonsense data into the declared record variable in lines 25 and 26, then enter a loop where we will change the Age field of the record in order to have some varying data to write to the file. This will make it more useful when we read the data from this file in another example program.

WRITING BINARY DATA TO THE FILE

We actually write data to the file in line 30, where we use the procedure Write which is part of the instantiated package we named Seq_IO earlier, but since we have defined Seq_IO in a use clause, we do not need the qualifier "dotted" to the procedure name. We mention the internal file name to tell the system which file we wish to write to, and the variable which we desire to output. The variable to be output must be of the type for which the package was instantiated, resulting in a rule that all binary records output to any given file must be of the same type. Even though they must be of the same type, they can be records of a variant record with different variants.

After writing 100 binary records, we close the file in the manner shown in line 33, and the program is complete.

We covered a lot of territory in the last few paragraphs, but it is useful information we will need in the next few example programs, and of course we will need it anytime we wish to actually use a binary file. It should be clear that we could open several files, each storing a different data type, and write to them in any order provided that we wrote the proper data type to each file.

The resulting file, named NAMEFILE.TXT, will be used to illustrate binary reading in the next two programs, so it is imperative that you compile and execute this program. After running it, you will find the file named NAMEFILE.TXT in the default directory of your system, and if you attempt to look at it with a text editor, it will have some very strange looking characters because it is a binary file. You will notice however, that much of it will be readable because of the nature of the data written to it.

READING A BINARY FILE

Example program ------> e_c24_p2.ada

-- Chapter 24 - Program 2with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;with Ada.Sequential_IO;

procedure BiSeqIn is

type MY_REC is record Age : INTEGER; Sex : CHARACTER; Initial : CHARACTER; end record;

package Seq_IO is new Ada.Sequential_IO(MY_REC); use Seq_IO;

Myself : MY_REC; My_In_File : Seq_IO.FILE_TYPE;

begin

Open(My_In_File, In_File, "NAMEFILE.TXT");

for Index in 1..100 loop Read(My_In_File, Myself); if Myself.Age >= 82 then

Page 127: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Put("Record number"); Put(Myself.Age, 4); Put(" "); Put(Myself.Sex); Put(" "); Put(Myself.Initial); New_Line; end if; end loop;

Close(My_In_File);

end BiSeqIn;

-- Result of Execution

-- Record number 82 M X-- Record number 83 M X-- Record number 84 M X-- Record number 85 M X-- Record number 86 M X-- Record number 87 M X-- Record number 88 M X-- Record number 89 M X-- Record number 90 M X-- Record number 91 M X-- Record number 92 M X-- Record number 93 M X-- Record number 94 M X-- Record number 95 M X-- Record number 96 M X-- Record number 97 M X-- Record number 98 M X-- Record number 99 M X-- Record number 100 M X

The example program named e_c24_p2.ada illustrates how to read from a binary file in a sequential mode. Everything in the declaration part of the program is identical to the last program except for including the Ada.Integer_Text_IO package in lines 2 and 3 for use later.

The only differences in the executable part is the use of the Open procedure from the Seq_IO package, which uses the In_File mode of file opening. We can now read from the file we wrote in the last program, but we cannot write to it from this program. We execute a loop 100 times where we read a record from the binary file each time through the loop. The record definition is identical to the record used to write the binary file. If the record is different in structure or in data types, you may get anything upon reading, and the data probably will not appear to have anything to do with the original data written, because the bytes will be mixed around.

You may wonder what will happen if the file that we requested the system to open is not available where we expect it to be. In the spirit of Ada, you may guess that an exception will be raised and you will be correct. If the file is not available for opening, the exception Name_Error will be raised as an indicator. There are eight exceptions defined in the package IO_Exceptions that are raised for various kinds of errors.

Assuming that the file did open properly, all 100 elements are read in and those with the Age field greater than or equal to 82 are displayed to illustrate that the data really did get written. Finally, the

Page 128: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

binary file is closed and the program terminated.

WHAT ABOUT PORTABILITY?

Suppose you wrote the binary file with one Ada compiler, and attempted to read it using a different compiler. It would be indeterminate whether or not it would work, because each implementor is free to define the internal bit patterns of the various types to fit his particular compiler. Using such simple fields as those in this illustration would lead to a very good chance of portability, but using more elaborate records or arrays, would almost certainly cause incompatibility problems.

The solution to this problem is to read a file with the same compiler that was used to write it. Of course a file written in a text format, using Ada.Text_IO, is portable and can be read with a different system.

Compile and execute this program and verify that the data really did get output as desired. If you did not compile and execute the last example program, you did not generate the file named "NAMEFILE.TXT", and you cannot successfully execute this program.

RANDOM INPUT AND OUTPUT

Example program ------> e_c24_p3.ada

-- Chapter 24 - Program 3with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;with Ada.Direct_IO;

procedure BiRandIO is

type MY_REC is record Age : INTEGER; Sex : CHARACTER; Initial : CHARACTER; end record;

package Ran_IO is new Ada.Direct_IO(MY_REC); use Ran_IO;

Myself : MY_REC; My_In_Out_File : Ran_IO.FILE_TYPE;

procedure Display_Record(In_Rec : MY_REC) is begin Put("Record number"); Put(In_Rec.Age, 4); Put(" "); Put(In_Rec.Sex); Put(" "); Put(In_Rec.Initial); New_Line; end Display_Record;

begin

Open(My_In_Out_File, InOut_File, "NAMEFILE.TXT");

Read(My_In_Out_File, Myself, 37); Display_Record(Myself); Read(My_In_Out_File, Myself, 25); Display_Record(Myself); Read(My_In_Out_File, Myself); Display_Record(Myself);

Page 129: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

New_Line;

Myself.Age := 33; Myself.Sex := 'F'; Myself.Initial := 'Z'; Write(My_In_Out_File, Myself, 91); Write(My_In_Out_File, Myself, 96); Write(My_In_Out_File, Myself);

Set_Index(My_In_Out_File, 88); while not End_Of_File(My_In_Out_File) loop Read(My_In_Out_File, Myself); Display_Record(Myself); end loop;

Close(My_In_Out_File);

end BiRandIO;

-- Result of Execution

-- Record number 37 M X-- Record number 25 M X-- Record number 26 M X

-- Record number 88 M X-- Record number 89 M X-- Record number 90 M X-- Record number 33 F Z-- Record number 92 M X-- Record number 93 M X-- Record number 94 M X-- Record number 95 M X-- Record number 33 F Z-- Record number 33 F Z-- Record number 98 M X-- Record number 99 M X-- Record number 100 M X

Examine the file named e_c24_p3.ada for an example of random input and output. Random file access means that we can output data to the file, or read data from the file just as if the file were an array. The elements of the file do not have to be accessed in order. We will illustrate all of this in this example program.

The declaration part of this program should look familiar to you since it is nearly identical to the last two example programs. The biggest difference is the use of the Ada.Direct_IO package instead of the Ada.Sequential_IO package. We instantiate a copy of this called Ran_IO, and use it to declare the internal filename, My_In_Out_File. Next, as part of the declaration part of the program, we declare a function that will be used to output some well formatted data.

READING FROM THE RANDOM FILE

Before we can do anything with the file, we must open it, and since we intend to read from and write to this file, we open it with the InOut_File mode. Of course we use the internal filename we defined in line 19, and the external filename we have already written to. In line 36, we use the procedure Read, reading from the file we have opened, and we read the data into the record variable named Myself. The thing that is really new here is the use of the number 37 as the third

Page 130: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

actual parameter of the procedure call. This tells the system that we wish to read the 37th record from the designated file. We use our Display_Record procedure to display the record read, then tell the system that we wish to read record number 25, and display it. In line 40, we don't tell the system which record we want to read explicitly, so it returns the next record, the 26th, which we display.

WRITING TO THE RANDOM FILE

We fill the three fields of the record variable with nonsense data in lines 44 through 46, and write the modified record to records 91, 96, and 97. We write to record 97 because we don't specify a record and the system will default to the next successive record number. In line 51 we call the procedure Set_Index with the value of 88, and as you may guess, it sets the record pointer to 88, which is the next record that will be read by default if no record number is stated. The loop in lines 52 through 55 read and display all records from 88 through the last, because we keep reading until we find an end of file. You will see when you compile and execute this program that we did modify records numbered 91, 96, and 97.

You will find binary Input/Output to be very useful for temporary storage of large amounts of data, but you must keep in mind that any data you write using this technique may or may not be readable by some other system. For this reason, binary output should not be used except for data that is meant to be read by another program compiled with the same Ada compiler as the data generator. Be sure to compile and execute this program.

USING HETEROGENEOUS DATA

Example program ------> e_c24_p4.ada

-- Chapter 24 - Program 4

with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Streams.Stream_IO;use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Streams.Stream_IO;

procedure Stream is

My_File : Ada.Streams.Stream_IO.FILE_TYPE; My_File_Access : Ada.Streams.Stream_IO.STREAM_ACCESS;

type RESULT is (WIN, LOSE, TIE, FORFEIT);

type BOX is record Length : INTEGER; Width : INTEGER; Height : INTEGER; end record;

Big_Box, Small_Box : BOX := (7, 8, 9); Football, Baseball : RESULT := TIE; Dogs, Pigs, Cats : INTEGER := 27; Animal : INTEGER := 100;

begin -- Create a stream to a file in the output mode to write to. Ada.Streams.Stream_IO.Create(My_File, Out_File, "funny.txt"); My_File_Access := Ada.Streams.Stream_IO.Stream(My_File); INTEGER'Write(My_File_Access, Dogs); BOX'Write(My_File_Access, Big_Box); BOX'Write(My_File_Access, Small_Box); RESULT'Write(My_File_Access, Baseball); INTEGER'Write(My_File_Access, Pigs); RESULT'Write(My_File_Access, Football);

Page 131: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

INTEGER'Write(My_File_Access, Cats);

Ada.Streams.Stream_IO.Close(My_File);

-- Open the stream in the input mode to read from it. Open(My_File, In_File, "funny.txt"); My_File_Access := Stream(My_File);

INTEGER'Read(My_File_Access, Animal); BOX'Read(My_File_Access, Small_Box); -- The others can be read here too

Put("Animal has a value of "); Put(Animal, 6); New_Line;

Close(My_File);

end Stream;

-- Result of execution

-- Animal has a value of 27

Examine the example program e_c24_p4.ada which contains an example of writing several types of data to a single file using the streams capability which was added to Ada 95. The package named Ada.Streams.Stream_IO contains the required entities, so it is included in context clauses in lines 3 and 4, and we declare a file object along with an access type for use with the file. Note that even though the package is included in a use clause, the extended naming notation is required in line 8 because there is also a type FILE_TYPE defined in Ada.Text_IO, and the system does not know which to select based on the available information. The extended naming notation is not required in line 9, but is given to indicate clearly that the two types work closely together.

In lines 11 through 23, we define several types and various variables for use within the executable portion of the program. They are all assigned initial values which have no significance, but provide known values for all entities.

In line 27, we create a stream with the name My_File which will allow us to write to the file named "funny.txt" in the default directory. In line 28 we cause the access variable named My_File_Access to access the file we just opened. In lines 30 through 36, we write seven different variables, which represent three different types, to the same file in an arbitrary order. The method of writing seems very strange, and it is, because it uses an attribute of the types to do the actual writing. The file is closed in line 38, and the same file is opened for reading in line 41.

The first two variables are read from the file in lines 44 and 45, being careful to read them in the proper order and using the proper type for each variable. To illustrate that the data did get read in properly, the value of Animal is displayed on the monitor for inspection. The file is closed a second time in line 52, and the program is complete.

This very limited program illustrates only one use for the streams capability provided with Ada 95. This package also provides for random input/output rather than only sequential, and the stream can be directed into memory rather than into a file, or read directly from memory.

PROGRAMMING EXERCISES

1. Modify e_c24_p1.ada to write the same data to two different files, except when Age is between 50 and 60. During this range, one of the characters should be changed for one of the files.(Solution)

Page 132: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Chapter 24 - Programming exercise 1with Ada.Text_IO;use Ada.Text_IO;with Ada.Sequential_IO;

procedure CH24_1 is

type MY_REC is record Age : INTEGER; Sex : CHARACTER; Initial : CHARACTER; end record;

package Seq_IO is new Ada.Sequential_IO(MY_REC); use Seq_IO;

Myself : MY_REC; My_Out_File : Seq_IO.FILE_TYPE; Other_File : Seq_IO.FILE_TYPE;begin

Create(My_Out_File, Out_File, "NAMEFILE.TXT"); Create(Other_File, Out_File, "OTHRFILE.TXT");

Myself.Sex := 'M'; for Index in 1..100 loop Myself.Age := Index; Myself.Initial := 'X'; Write(My_Out_File, Myself); if (Myself.Age >= 50) and (Myself.Age <= 60) then Myself.Initial := 'O'; Write(Other_File, Myself); else Write(Other_File, Myself); end if; end loop;

Close(My_Out_File); Close(Other_File);

end CH24_1;

-- Result of Execution

-- (The output is a binary file named NAMEFILE.TXT,-- and a binary file named OTHRFILE.TXT.)

2. Modify e_c24_p2.ada to read both files output from exercise 1 and list the differences in the two files.(Solution)

-- Chapter 24 - Programming exercise 2with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

Page 133: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

with Ada.Sequential_IO;

procedure CH24_2 is

type MY_REC is record Age : INTEGER; Sex : CHARACTER; Initial : CHARACTER; end record;

package Seq_IO is new Ada.Sequential_IO(MY_REC); use Seq_IO;

Myself, Other : MY_REC; My_In_File : Seq_IO.FILE_TYPE; Other_File : Seq_IO.FILE_TYPE;

begin

Open(My_In_File, In_File, "NAMEFILE.TXT"); Open(Other_File, In_File, "OTHRFILE.TXT");

for Index in 1..100 loop Read(My_In_File, Myself); Read(Other_File, Other); if (Myself.Age /= Other.Age) or (Myself.Sex /= Other.Sex) or (Myself.Initial /= Other.Initial) then

Put("Record number"); Put(Myself.Age); Put(" "); Put(Myself.Sex); Put(" "); Put(Myself.Initial);

Put(Other.Age,12); Put(" "); Put(Other.Sex); Put(" "); Put(Other.Initial); New_Line; end if; end loop;

Close(My_In_File); Close(Other_File);

end CH24_2;

-- Result of Execution

-- Record number 50 M X 50 M O-- Record number 51 M X 51 M O-- Record number 52 M X 52 M O-- Record number 53 M X 53 M O-- Record number 54 M X 54 M O-- Record number 55 M X 55 M O

Page 134: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Record number 56 M X 56 M O-- Record number 57 M X 57 M O-- Record number 58 M X 58 M O-- Record number 59 M X 59 M O-- Record number 60 M X 60 M O

3. Combine e_c24_p1.ada and e_c24_p2.ada in such a way that the file is written in one loop, then read back in and displayed in a successive loop.(Solution)

-- Chapter 24 - Programming exercise 3with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;with Ada.Sequential_IO;

procedure CH24_3 is

type MY_REC is record Age : INTEGER; Sex : CHARACTER; Initial : CHARACTER; end record;

package Seq_IO is new Ada.Sequential_IO(MY_REC); use Seq_IO;

Myself : MY_REC; My_Out_File : Seq_IO.FILE_TYPE; My_In_File : Seq_IO.FILE_TYPE;

begin

Create(My_Out_File, Out_File, "NAMEFILE.TXT");

Myself.Sex := 'M'; Myself.Initial := 'X';

for Index in 1..100 loop Myself.Age := Index; Write(My_Out_File, Myself); end loop;

Close(My_Out_File);

Open(My_In_File, In_File, "NAMEFILE.TXT");

for Index in 1..100 loop Read(My_In_File, Myself); if Myself.Age >= 82 then Put("Record number"); Put(Myself.Age); Put(" "); Put(Myself.Sex); Put(" "); Put(Myself.Initial); New_Line; end if; end loop;

Close(My_In_File);

Page 135: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

end CH24_3;

-- Result of Execution

-- Record number 82 M X-- Record number 83 M X-- Record number 84 M X-- Record number 85 M X-- Record number 86 M X-- Record number 87 M X-- Record number 88 M X-- Record number 89 M X-- Record number 90 M X-- Record number 91 M X-- Record number 92 M X-- Record number 93 M X-- Record number 94 M X-- Record number 95 M X-- Record number 96 M X-- Record number 97 M X-- Record number 98 M X-- Record number 99 M X-- Record number 100 M X

Page 136: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Ada Tutorial - Chapter 25

DYNAMIC ALLOCATION

Although this chapter is titled "Dynamic Allocation", it may be more proper to title it "Sorting with Linked Lists", since that is what we will actually do. This chapter contains example programs that will illustrate how to generate a linked list with dynamically allocated entries. It is meant to illustrate how to put several programming techniques together in a meaningful manner. It will also instruct you in dynamic allocation and deallocation techniques.

DYNAMIC DEALLOCATION

One of the most important topics covered in this chapter is that of dynamic deallocation. After variables are dynamically allocated and used, they can be deallocated, permitting the memory space to be reclaimed for reuse by other variables. Ada uses two techniques, one being garbage collection, and the other being unchecked deallocation. We will have a good bit to say about both of these in this chapter.

A SIMPLE LINKED LIST

Example program ------> e_c25_p1.ada

-- Chapter 25 - Program 1with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure LinkList is

Data_String : constant STRING := "This tests ADA";

type CHAR_REC; -- Incomplete declaration

type CHAR_REC_POINT is access CHAR_REC;

type CHAR_REC is -- Complete declaration record One_Letter : CHARACTER; Next_Rec : CHAR_REC_POINT; end record;

Start : CHAR_REC_POINT; -- Always points to start of list Last : CHAR_REC_POINT; -- Points to the end of the list

procedure Traverse_List(Starting_Point : CHAR_REC_POINT) is Temp : CHAR_REC_POINT; -- Moves through the list begin Put("In traverse routine. --->"); Temp := Starting_Point; if Temp = null then Put("No data in list."); else loop Put(Temp.One_Letter); Temp := Temp.Next_Rec; if Temp = null then exit; end if; end loop; end if; New_Line; end Traverse_List;

procedure Store_Character(In_Char : CHARACTER) is Temp : CHAR_REC_POINT;

Page 137: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

begin Temp := new CHAR_REC; Temp.One_Letter := In_Char; -- New record is now defined -- The system sets Next_Rec -- to the value of null if Start = null then Start := Temp; Last := Temp; else Last.Next_Rec := Temp; Last := Temp; end if; Traverse_List(Start); end Store_Character;

begin -- Store the characters in Data_String in a linked list for Index in Data_String'RANGE loop Store_Character(Data_String(Index)); end loop;

-- Traverse the final list New_Line; Put_Line("Now for the final traversal."); Traverse_List(Start);

-- Free the entire list now loop exit when Start = null; Last := Start.Next_Rec; Start.Next_Rec := null; Start := Last; end loop;

end LinkList;

-- Result of execution

-- In traverse routine. --->T-- In traverse routine. --->Th-- In traverse routine. --->Thi-- In traverse routine. --->This-- In traverse routine. --->This-- In traverse routine. --->This t-- In traverse routine. --->This te-- In traverse routine. --->This tes-- In traverse routine. --->This test-- In traverse routine. --->This tests-- In traverse routine. --->This tests-- In traverse routine. --->This tests A-- In traverse routine. --->This tests AD-- In traverse routine. --->This tests ADA---- Now for the final traversal.-- In traverse routine. --->This tests ADA

The program named e_c25_p1.ada contains all of the logic needed to generate and traverse a linked

Page 138: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

list. We will go through it in detail to illustrate how to build and use a linked list.

The first thing we need for a linked list is a record type containing an access variable which accesses itself. Line 9 is an incomplete record definition which will allow us to define the access type in line 11. After we have the access type defined, we can complete the record definition in lines 13 through 17 which includes a reference to the access type. The record type therefore contains a reference to itself. Line 9 is more properly called a type specification and lines 13 through 17 a type body. Note that the incomplete type definition can only be used to declare an access type.

We declare two additional access variables in lines 19 and 20, and two procedures to be used to generate and traverse the list as we will see shortly. Note that all of this is contained within the declaration part of the main program.

The program itself, beginning in line 56, begins with a for loop covering the range of the string variable named Data_String, defined earlier, and each character of this string is given in turn to the procedure Store_Character. It remains for us to discover what this procedure does.

THE PROCEDURE Store_Character

We enter this procedure with the single character and wish to store it away somehow for later use. We use a local variable, named Temp, which is an access variable to our record type and use it to dynamically allocate storage for a variable of the record type CHAR_REC in line 42, then assign the single character input from the calling program to its field named One_Letter. The field of this record named Next_Rec is an access type variable, and according to the definition of Ada, the system will set it to null when it is generated. Figure 25-1 is a graphical representation of the record variable, the two access type variables defined in lines 19 and 20, named Start and Last, and the local access variable named Temp. Now we need to insert the new record into our linked list, but there is a different way to do it depending on whether it is the first record, or an additional record.

THE FIRST RECORD

If it is the first record, or if this is the first time this procedure has been called, the value of the access variable Start will be null, because the system assigned a value of null to it when it was elaborated. We can test the value, and if it is null, we assign the value of the new record's access variable to the access variable Start and to Last. We have generated data that could be graphically depicted by figure 25-2, and we will see shortly just how this will be used.

AN ADDITIONAL RECORD

Page 139: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

If we find that the value of Start is not null, indicating that it has already been assigned to access another record, we need to add the new record to the end of the list. If it is the second time we have entered this procedure, we have data as pictured in figure 25-3, which includes the single record entered earlier, and the new record which is still disassociated with the first but accessed by the access variable named Temp.

We add the new record to the end of the list in lines 50 and 51, with the resulting list being pictured graphically in figure 25-4. Line 50 causes the access variable in the first record to access the new record, and line 51 causes the variable Last to refer to the new record which is now the end of the list. After entering the third element and adding it to the end of the list, we have the data structure pictured in figure 25-5. Further elements will not be diagrammed for this example, but it would be good for the student to draw a few additional diagrams.

TRAVERSING THE LIST

Each time a character is added to the list, the Traverse_List procedure is called which starts at the input access point, Start in this program, and lists all characters currently in the list. It does this

Page 140: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

through use of its own local access variable named Temp which is initially assigned the address of the first record in the list. It checks first to see if the list is empty, and if it is not, it enters a loop where it outputs the character in each record until it finds a record with a value of null in its access variable field named Next_Rec, which is an access pointer. The variable Temp is used to work its way through the list by the assignment in line 32 where Temp is assigned the access variable's value from the next record each time through the loop.

Since the list is traversed each time a character is entered into the list, the list of characters output will grow by one character each time a character is added and the updated list is traversed.

We traverse the list one more time in line 65, then we free the entire list one element at a time. You will notice that we do not actually deallocate the free'ed elements, we only assign the access variables the value of null. We will depend on the garbage collector to do the deallocation for us.

MORE ABOUT GARBAGE COLLECTION

We mentioned garbage collection in chapter 13 of this tutorial, but there is more to be discussed about this subject. An Ada compiler may include a garbage collection facility which will search through the access variables and the storage pool occasionally to see if there are any locations in the storage pool that are not accessed by access variables. If it finds any, it will return those locations to the free list so they will be available for allocation again. In this way, any memory that gets lost, either through faulty programming, or due to intentionally clearing an access variable, will be automatically restored and available for reuse as dynamic variables. Note however, that implementation of a garbage collector is optional in an Ada compiler. Check your documentation to see if a garbage collector is actually available with your compiler.

USING THE GARBAGE COLLECTOR

In lines 68 through 73, we execute a loop that will traverse the linked list, and assign all of the access variables the value of null. If there is a garbage collector, it will eventually find the locations in the storage pool that no longer have an access variable accessing them and return those locations to the available pool of usable memory. We used the word eventually because there is no predefined time when this will occur, but is at the discretion of the compiler writer. More will be said about this topic later in this chapter.

If your compiler does not have a garbage collector, the operating system will reclaim the lost memory when you complete execution of the program, so no memory will actually be lost.

WHAT IF THE DYNAMIC ALLOCATION FAILS?

As mentioned earlier in this tutorial, if there is not enough memory to provide the requested block of memory, the exception Storage_Error is raised. It is then up to you to handle the exception, and provide a graceful way to deal with this problem. You may want to suggest a means of recovery to the user.

Compile and Execute this program, observe the output, and then return to additional study if you do not completely understand its operation. You will need a good grasp of this program in order to understand the next program, so when you are ready, we will go on to a linked list that is a bit more complicated because it can be used to sort an array of characters alphabetically.

A LINKED LIST THAT SORTS ALPHABETICALLY

Example program ------> e_c25_p2.ada

-- Chapter 25 - Program 2with Ada.Text_IO, Ada.Integer_Text_IO, Unchecked_Deallocation;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure SortList is

Data_String : constant STRING := "This tests ADA";

Page 141: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

type CHAR_REC; -- Incomplete declaration

type CHAR_REC_POINT is access CHAR_REC;

type CHAR_REC is -- Complete declaration record One_Letter : CHARACTER; Next_Rec : CHAR_REC_POINT; end record;

Start : CHAR_REC_POINT; -- Always points to start of list Last : CHAR_REC_POINT; -- Points to the end of the list

procedure Free is new Unchecked_Deallocation(CHAR_REC, CHAR_REC_POINT);

pragma Controlled(CHAR_REC_POINT);

procedure Traverse_List(Starting_Point : CHAR_REC_POINT) is Temp : CHAR_REC_POINT; -- Moves through the list begin Put("In traverse routine. --->"); Temp := Starting_Point; if Temp = null then Put("No data in list."); else loop Put(Temp.One_Letter); Temp := Temp.Next_Rec; if Temp = null then exit; end if; end loop; end if; New_Line; end Traverse_List;

procedure Store_Character(In_Char : CHARACTER) is Temp : CHAR_REC_POINT; -- Moves through the list

procedure Locate_And_Store is Search : CHAR_REC_POINT; Prior : CHAR_REC_POINT; begin Search := Start; while In_Char > Search.One_Letter loop Prior := Search; Search := Search.Next_Rec; if Search = null then exit; end if; end loop; if Search = Start then -- New character at head of list Temp.Next_Rec := Start; Start := Temp; elsif Search = null then -- New character at tail of list Last.Next_Rec := Temp; Last := Temp; else -- New character within list Temp.Next_Rec := Search; Prior.Next_Rec := Temp; end if; end Locate_And_Store;

begin

Page 142: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Temp := new CHAR_REC; Temp.One_Letter := In_Char; -- New record is now defined -- The system sets Next_Rec -- to the value of null if Start = null then Start := Temp; Last := Temp; else Locate_And_Store; end if; Traverse_List(Start); end Store_Character;

begin -- Store the characters in Data_String in a linked list for Index in Data_String'RANGE loop Store_Character(Data_String(Index)); end loop;

-- Traverse the final list New_Line; Put_Line("Now for the final traversal."); Traverse_List(Start);

-- Deallocate the list now loop exit when Start = null; Last := Start.Next_Rec; Free(Start); Start := Last; end loop;

end SortList;

-- Result of execution---- In traverse routine. --->T-- In traverse routine. --->Th-- In traverse routine. --->Thi-- In traverse routine. --->This-- In traverse routine. ---> This-- In traverse routine. ---> Thist-- In traverse routine. ---> Tehist-- In traverse routine. ---> Tehisst-- In traverse routine. ---> Tehisstt-- In traverse routine. ---> Tehissstt-- In traverse routine. ---> Tehissstt-- In traverse routine. ---> ATehissstt-- In traverse routine. ---> ADTehissstt-- In traverse routine. ---> AADTehissstt---- Now for the final traversal.-- In traverse routine. ---> AADTehissstt

The next example program, named e_c25_p2.ada, uses a different storage technique that results in an alphabetically sorted list. This program is identical to the last except for the Store_Character procedure.

Page 143: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

In this program, the Store_Character procedure works just like the last one if it is the first character input as you can see by inspecting lines 74 through 77. If it is an additional input however, we make a call to the embedded procedure named Locate_And_Store. This procedure searches through the existing list looking for the first character in the list that is not less than the new character alphabetically. When it is found, the search is terminated and the new character must be inserted into the list prior to the located character. This is done by moving access variables around rather than by moving actual variables around. If the new character must be added to the starting point of the list, it must be handled in a special way, and if it must be the last element, it also requires special handling.

ADDING ELEMENTS TO THE LIST

Figure 25-6 illustrates the condition of the data when the fourth element is to be added to a three element list.

The three element list is identical to the list described in the last example program and Temp is accessing the new element to be inserted alphabetically. Lines 64 and 65 of the program alter the access variables to do the insertion, and figure 25-7 illustrates the result of changing the access variables to achieve that goal. Note that the character data used here is not the same as the data used in the program, but is different for illustrative purposes. The cases where the new record is added to the beginning of the list, and when it is added to the end of the list will not be illustrated graphically, but is left for the student to diagram.

MORE ABOUT Unchecked_Deallocation

In chapter 13, we first mentioned the generic procedure supplied with your compiler named Unchecked_Deallocation and illustrated how to use it in example programs there. Since it can be used with any dynamically allocated data, it can be used with these programs also. In order to use it,

Page 144: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

you must first mention the name in a with clause as is done in line 2 of this program to make it available. Because it is a generic procedure, it must be instantiated before use. Line 22 is the instantiation of the procedure where it is named Free. Pascal and C each have a deallocator available named Free, and the name Free has become fairly standard in Ada because of the other languages. You would be encouraged to use the name Free also for ease of communication with other Ada programmers. It would be perfectly legal to name it any other name provided it obeyed the rules of naming an identifier.

HOW DO YOU USE Unchecked_Deallocation

When you use the Unchecked_Deallocation procedure, you are essentially taking on the responsibility for managing the storage pool yourself, and you must tell the system that you will be responsible for garbage collection, and that it need not concern itself with it. You do this by using the pragma named CONTROLLED as illustrated in line 25. This tells the system that you will be responsible for managing all areas of the storage pool that are dynamically allocated to any access variable of type CHAR_REC_POINT. The system will not attempt to do any garbage collection for this type, but will assume that you are handling the memory management.

You may think that it would be a good idea to let the system maintain the storage pool and do the garbage collection automatically but this can be a real problem, which will be evident after we understand what garbage collection is and how it is implemented.

HOW IS GARBAGE COLLECTION IMPLEMENTED?

There is no predefined method of how often garbage collection should be implemented, so it is up to each compiler writer to define it his own way. One of the most common methods is to wait until the storage pool is used up, then do a search of all access variables and all storage pool areas to find all areas that are unreferenced. Those areas are then returned to the free list and program execution is resumed. The biggest problem with this is that it may take as much as a full second of execution time to accomplish this during which time the Ada program is essentially stopped. This is not acceptable in a real-time system because it could occur at a very inopportune time, such as during final approach of a 747 into an international airport. In that case, you would do well to use the pragma named CONTROLLED to tell the system to ignore garbage collection, and manage the storage pool yourself as we are doing in this program.

DEALLOCATING THE LIST

The loop in lines 95 through 100 will traverse the list and return all of the allocated data to the storage pool where it will be immediately available for reuse. The observant student will notice that the access variable from each record is copied to the variable named Last prior to deallocating that particular record.

Compile and execute this program, so you can see that it really does sort the input characters alphabetically. It should be apparent to you that this very simple case of sorting single characters is not really too useful in the real world, but sorting a large list of records containing 23 fields, by last name, first name, zipcode, and place of birth, could be a rather large undertaking, but could also lead to a very useful program. Remember that in this program we are only changing access variables rather than moving the data around, so the efficiency of this technique when using it for a large data base will be very good.

A BINARY TREE SORTING PROGRAM

Example program ------> e_c25_p3.ada

-- Chapter 25 - Program 3with Ada.Text_IO, Unchecked_Deallocation;use Ada.Text_IO;

procedure BTree is

Page 145: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Test_String : constant STRING := "DBCGIF"; Data_String : constant STRING := "This tests ADA";

type B_TREE_NODE; -- Incomplete declaration

type NODE_POINT is access B_TREE_NODE;

type B_TREE_NODE is -- Complete declaration record One_Letter : CHARACTER; Left : NODE_POINT; Right : NODE_POINT; end record;

procedure Free is new Unchecked_Deallocation(B_TREE_NODE, NODE_POINT);

pragma Controlled(NODE_POINT);

Root : NODE_POINT; -- Always points to the root of the tree

procedure Traverse_List(Start_Node : NODE_POINT) is begin if Start_Node.Left /= null then Traverse_List(Start_Node.Left); end if; Put(Start_Node.One_Letter); if Start_Node.Right /= null then Traverse_List(Start_Node.Right); end if; end Traverse_List;

procedure Store_Character(In_Char : CHARACTER) is Temp : NODE_POINT;

procedure Locate_And_Store(Begin_Node : in out NODE_POINT) is begin if In_Char < Begin_Node.One_Letter then if Begin_Node.Left = null then Begin_Node.Left := Temp; else Locate_And_Store(Begin_Node.Left); end if; else if Begin_Node.Right = null then Begin_Node.Right := Temp; else Locate_And_Store(Begin_Node.Right); end if; end if; end Locate_And_Store;

begin Temp := new B_TREE_NODE; Temp.One_Letter := In_Char; -- New record is now defined -- The system sets Next_Rec -- to the value of null if Root = null then Root := Temp; else Locate_And_Store(Root);

Page 146: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

end if; Put("Ready to traverse list. --->"); Traverse_List(Root); New_Line; end Store_Character;

begin -- Store the characters in Data_String in a Binary Tree for Index in Data_String'RANGE loop Store_Character(Data_String(Index)); end loop;

-- Traverse the list New_Line; Put_Line("Now for the final traversal of Data_String."); Put("Ready to traverse list. --->"); Traverse_List(Root); New_Line(2);

Root := null; -- Needed to clear out the last tree

-- Store the characters in Test_String in a Binary Tree for Index in Test_String'RANGE loop Store_Character(Test_String(Index)); end loop;

-- Traverse the list New_Line; Put_Line("Now for the final traversal of Test_String."); Put("Ready to traverse list. --->"); Traverse_List(Root); New_Line;

-- Now deallocate the tree declare procedure Free_Up(Current_Node : in out NODE_POINT) is begin if Current_Node.Left /= null then Free_Up(Current_Node.Left); end if; if Current_Node.Right /= null then Free_Up(Current_Node.Right); end if; Free(Current_Node); end Free_Up; begin if Root /= null then Free_Up(Root); end if; end;

end BTree;

-- Result of execution---- Ready to traverse list. --->T-- Ready to traverse list. --->Th-- Ready to traverse list. --->Thi-- Ready to traverse list. --->This

Page 147: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Ready to traverse list. ---> This-- Ready to traverse list. ---> Thist-- Ready to traverse list. ---> Tehist-- Ready to traverse list. ---> Tehisst-- Ready to traverse list. ---> Tehisstt-- Ready to traverse list. ---> Tehissstt-- Ready to traverse list. ---> Tehissstt-- Ready to traverse list. ---> ATehissstt-- Ready to traverse list. ---> ADTehissstt-- Ready to traverse list. ---> AADTehissstt---- Now for the final traversal of Data_String.-- Ready to traverse list. ---> AADTehissstt

-- Ready to traverse list. --->D-- Ready to traverse list. --->BD-- Ready to traverse list. --->BCD-- Ready to traverse list. --->BCDG-- Ready to traverse list. --->BCDGI-- Ready to traverse list. --->BCDFGI

-- Now for the final traversal of Test_String.-- Ready to traverse list. --->BCDFGI

The example file named e_c25_p3.ada illustrates the use of dynamic allocation and recursion to perform a sorting function. It uses a binary tree method of alphabetization, which is thoroughly defined in Wirth's book, "Algorithms + data structures = Programs". The method will be briefly defined here.

The sorting element is pictured in figure 25-8, where a node is composed of the stored data within the circle and the two pointers, each of which point to other nodes or to null values. Each of these nodes must have something pointing to it to make the entire system useful, and we need a few additional pointers to find our directions through the final overall structure.

The definition of the node is contained in the program in lines 14 through 19, where we define the type of the node with 2 access variables pointing to it's own type. You will see that we have one access variable named Left and another named Right which correspond to the two legs out of the bottom of the node in figure 25-8. The node itself contains the data which could be any number of different variables including arrays and records, but for our purposes of illustration, will contain only a single character.

BUILDING THE TREE

The main program begins in line 74 with a loop to call the procedure Store_Character once with each character in the input string named Data_String. We traverse the list one more time in line 82, and assign the root the value of null. The following description of operation will use Test_String as the string example. The first time we call Store_Character, with the character "D", we allocate a new record, store the "D" in it, and since Root is equal to null, we execute line 65 to assign the access variable named Root to point to the new record, resulting in the state found in figure 25-9.

The next time we call Store_Character, this time with the character "B", we allocate a new record, store the "B" in it, and since Root is no longer equal to null, we call the procedure Locate_And_Store from line 67, telling it to start at Root. The procedure

Page 148: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Locate_And_Store is recursive, calling itself successively until it successfully stores the character. The first time it is called, the input character is less than the stored character at the input node, which is "D", so the left branch is chosen, and the statement in lines 45 through 49 is executed. In this particular case, the left branch is null, so it is assigned the address of the input record resulting in the state found in figure 25-10. The tree is beginning to take shape.

THE THIRD CHARACTER

The next character is sent to Store_Character, this time a "C", resulting in another call to Locate_And_Store. On this pass through the logic, because the input character is less than the character at the root node, we select the left branch and call Locate_And_Store recursively from line 48. On this recursive call, we tell it to work with the node stored at the left branch of the present node. In the next procedure call, we find that "C" is not less than the "B" stored there and we find that the right branch of this node is null, so we can store the new node there. Our tree now looks like that given in figure 25-11.

Continuing through the remaining characters of our input stream, we finally have the structure as pictured in figure 25-12 with all 6 characters stored in it.

Page 149: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

TRAVERSING THE B-TREE

Traversing the tree is essentially the same as building it, except that we do not need to test for equality of the input data, only test each node's Left and Right access values. If the access values are not equal to null, there is a subtree where the access variable is pointing, and we recurse to that subtree and check each of its subtrees. With a bit of study, you should be able to trace your way through the tree to see that we actually do alphabetize the input characters by the way we built the tree, then traverse it for output.

DEALLOCATING THE TREE

Once again we use Unchecked_Deallocation and the pragma CONTROLLED to explicitly deallocate the tree. We do this by traversing the tree in a manner similar to when we printed the characters out. One important thing to keep in mind however is that you must free a node only after you have checked both subtrees, because once you free a node, its subtrees are no longer available.

PROGRAMMING EXERCISES

1. Use Unchecked_Deallocation to deallocate the list in the example program e_c25_p1.ada.(Solution)

-- Chapter 25 - Programming exercise 1with Ada.Text_IO, Ada.Integer_Text_IO, Unchecked_Deallocation;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure CH25_1 is

Data_String : constant STRING := "This tests ADA";

type CHAR_REC; -- Incomplete declaration

type CHAR_REC_POINT is access CHAR_REC;

type CHAR_REC is -- Complete declaration record One_Letter : CHARACTER; Next_Rec : CHAR_REC_POINT; end record;

Start : CHAR_REC_POINT; -- Always points to start of list Last : CHAR_REC_POINT; -- Points to the end of the list

procedure Free is new Unchecked_Deallocation(CHAR_REC,CHAR_REC_POINT);

procedure Traverse_List(Starting_Point : CHAR_REC_POINT) is

Page 150: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Temp : CHAR_REC_POINT; -- Moves through the list begin Put("In traverse routine. --->"); Temp := Starting_Point; if Temp = null then Put("No data in list."); else loop Put(Temp.One_Letter); Temp := Temp.Next_Rec; if Temp = null then exit; end if; end loop; end if; New_Line; end Traverse_List;

procedure Store_Character(In_Char : CHARACTER) is Temp : CHAR_REC_POINT; begin Temp := new CHAR_REC; Temp.One_Letter := In_Char; -- New record is now defined -- The system sets Next_Rec -- to the value of null if Start = null then Start := Temp; Last := Temp; else Last.Next_Rec := Temp; Last := Temp; end if; Traverse_List(Start); end Store_Character;

begin -- Store the characters in Data_String in a linked list for Index in Data_String'RANGE loop Store_Character(Data_String(Index)); end loop;

-- Traverse the final list New_Line; Put_Line("Now for the final traversal."); Traverse_List(Start);

-- Free the entire list now loop exit when Start = null; Last := Start.Next_Rec; Free(Start); Start := Last; end loop;

end CH25_1;

-- Result of execution

-- In traverse routine. --->T-- In traverse routine. --->Th-- In traverse routine. --->Thi

Page 151: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- In traverse routine. --->This-- In traverse routine. --->This-- In traverse routine. --->This t-- In traverse routine. --->This te-- In traverse routine. --->This tes-- In traverse routine. --->This test-- In traverse routine. --->This tests-- In traverse routine. --->This tests-- In traverse routine. --->This tests A-- In traverse routine. --->This tests AD-- In traverse routine. --->This tests ADA---- Now for the final traversal.-- In traverse routine. --->This tests ADA

2. Add a variable of type INTEGER named Character_Count to the record named B_TREE_NODE in e_c25_p3.ada. Store the current character count in this variable as the tree is generated. When the string is completed, output the sorted character list along with the position in the string it occupies.(Solution)

B is character 2 in the string. C is character 3 in the string. D is character 1 in the string. F is character 6 in the string. G is character 4 in the string. I is character 5 in the string.

-- Chapter 25 - Programming exercise 2with Ada.Text_IO, Ada.Integer_Text_IO, Unchecked_Deallocation;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure CH25_2 is

Test_String : constant STRING := "DBCGIF"; Data_String : constant STRING := "This tests ADA";

type B_TREE_NODE; -- Incomplete declaration

type NODE_POINT is access B_TREE_NODE;

type B_TREE_NODE is -- Complete declaration record One_Letter : CHARACTER; Character_Count : INTEGER; Left : NODE_POINT; Right : NODE_POINT; end record;

pragma CONTROLLED(NODE_POINT);

procedure Free is new Unchecked_Deallocation(B_TREE_NODE, NODE_POINT);

Root : NODE_POINT; -- Always points to the root of the tree

procedure Final_Traverse_List(Start_Node : NODE_POINT) is begin if Start_Node.Left /= null then Final_Traverse_List(Start_Node.Left);

Page 152: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

end if; Put(Start_Node.One_Letter); Put(" is character"); Put(Start_Node.Character_Count,3); Put_Line(" in the string."); if Start_Node.Right /= null then Final_Traverse_List(Start_Node.Right); end if; end Final_Traverse_List;

procedure Traverse_List(Start_Node : NODE_POINT) is begin if Start_Node.Left /= null then Traverse_List(Start_Node.Left); end if; Put(Start_Node.One_Letter); if Start_Node.Right /= null then Traverse_List(Start_Node.Right); end if; end Traverse_List;

procedure Store_Character(Char_Count : INTEGER; In_Char : CHARACTER) is Temp : NODE_POINT;

procedure Locate_And_Store(Begin_Node : in out NODE_POINT) is begin if In_Char < Begin_Node.One_Letter then if Begin_Node.Left = null then Begin_Node.Left := Temp; else Locate_And_Store(Begin_Node.Left); end if; else if Begin_Node.Right = null then Begin_Node.Right := Temp; else Locate_And_Store(Begin_Node.Right); end if; end if; end Locate_And_Store;

begin Temp := new B_TREE_NODE; Temp.One_Letter := In_Char; -- New record is now defined -- The system sets Next_Rec -- to the value of null Temp.Character_Count := Char_Count; if Root = null then Root := Temp; else Locate_And_Store(Root); end if; Put("Ready to traverse list. --->"); Traverse_List(Root); New_Line; end Store_Character;

begin -- Store the characters in Data_String in a Binary Tree for Index in Data_String'RANGE loop Store_Character(Index,Data_String(Index));

Page 153: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

end loop;

-- Traverse the list New_Line; Put_Line("Now for the final traversal of Data_String."); Final_Traverse_List(Root); New_Line(2);

Root := null; -- Needed to clear out the last tree

-- Store the characters in Test_String in a Binary Tree for Index in Test_String'RANGE loop Store_Character(Index,Test_String(Index)); end loop;

-- Traverse the list New_Line; Put_Line("Now for the final traversal of Test_String."); Final_Traverse_List(Root); New_Line;

-- Now deallocate the tree declare procedure Free_Up(Current_Node : in out NODE_POINT) is begin if Current_Node.Left /= null then Free_Up(Current_Node.Left); end if; if Current_Node.Right /= null then Free_Up(Current_Node.Right); end if; Free(Current_Node); end Free_Up; begin if Root /= null then Free_Up(Root); end if; end;

end CH25_2;

-- Result of execution---- Ready to traverse list. --->T-- Ready to traverse list. --->Th-- Ready to traverse list. --->Thi-- Ready to traverse list. --->This-- Ready to traverse list. ---> This-- Ready to traverse list. ---> Thist-- Ready to traverse list. ---> Tehist-- Ready to traverse list. ---> Tehisst-- Ready to traverse list. ---> Tehisstt-- Ready to traverse list. ---> Tehissstt-- Ready to traverse list. ---> Tehissstt-- Ready to traverse list. ---> ATehissstt-- Ready to traverse list. ---> ADTehissstt-- Ready to traverse list. ---> AADTehissstt---- Now for the final traversal of Data_String.

Page 154: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- is character 5 in the string.-- is character 11 in the string.-- A is character 12 in the string.-- A is character 14 in the string.-- D is character 13 in the string.-- T is character 1 in the string.-- e is character 7 in the string.-- h is character 2 in the string.-- i is character 3 in the string.-- s is character 4 in the string.-- s is character 8 in the string.-- s is character 10 in the string.-- t is character 6 in the string.-- t is character 9 in the string.

-- Ready to traverse list. --->D-- Ready to traverse list. --->BD-- Ready to traverse list. --->BCD-- Ready to traverse list. --->BCDG-- Ready to traverse list. --->BCDGI-- Ready to traverse list. --->BCDFGI

-- Now for the final traversal of Test_String.-- B is character 2 in the string.-- C is character 3 in the string.-- D is character 1 in the string.-- F is character 6 in the string.-- G is character 4 in the string.-- I is character 5 in the string.

Page 155: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Ada Tutorial - Chapter 26

SIMPLE TASKING

WHAT IS TASKING?

The topic of tasking is probably new to you regardless of what programming experience you have, because tasking is a relatively new technique which is not available with most programming languages. If you need some kind of a parallel operation with most other languages, you are required to use some rather tricky techniques or write a driver in assembly language. With Ada, however, tasking is designed into the language and is very easy to use.

BUT NOT TRULY PARALLEL IN ADA

Tasking is the ability of the computer to appear to be doing two or more things at once even though it only has one processor. True parallel operation occurs when there actually are multiple processors available in the hardware, but since most modern computers have only one processor, we must make the system appear to have more than one by sharing the processor between two or more tasks. We will have much more to say about this later, but we must discuss another topic first.

REAL-TIME REQUIRES TIMING

Example program ------> e_c26_p1.ada

-- Chapter 26 - Program 1with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

with Ada.Calendar; use Ada.Calendar;

procedure Timer is

package Fix_IO is new Ada.Text_IO.Fixed_IO(DAY_DURATION); use Fix_IO;

Year,Month,Day : INTEGER; Start,Seconds : DAY_DURATION; Time_And_Date : TIME;

begin

Put_Line("Begin 3.14 second delay"); delay 3.14; Put_Line("End of 3.14 second delay");

Time_And_Date := Clock; Split(Time_And_Date, Year, Month, Day, Start); -- get start time

for Index in 1..9 loop Put("The date and time are now"); Time_And_Date := Clock; Split(Time_And_Date, Year, Month, Day, Seconds); Put(Month, 3); delay 0.2; Put(Day, 3); delay 0.1; Put(Year, 5); delay 0.1; Put(Seconds - Start, 8, 3, 0); New_Line; delay 0.6; end loop;

Page 156: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Put_Line("Begin non-accumulative timing loop here.");

Time_And_Date := Clock; Split(Time_And_Date, Year, Month, Day, Start); -- get start time for Index in 1..9 loop Time_And_Date := Clock; Split(Time_And_Date, Year, Month, Day, Seconds); Put("The elapsed time is"); Put(Seconds - Start, 8, 3, 0); New_Line; delay DAY_DURATION(Index) - (Seconds - Start); end loop;

Time_And_Date := Clock; for Index in 1..12 loop Time_And_Date := Time_And_Date + 0.4; delay until Time_And_Date; Put("Tick "); end loop; New_Line;

end Timer;

-- Result of Execution

-- Begin 3.14 second delay-- End of 3.14 second delay-- The date and time are now 3 22 1997 0.000-- The date and time are now 3 22 1997 1.090-- The date and time are now 3 22 1997 2.140-- The date and time are now 3 22 1997 3.180-- The date and time are now 3 22 1997 4.230-- The date and time are now 3 22 1997 5.270-- The date and time are now 3 22 1997 6.320-- The date and time are now 3 22 1997 7.360-- The date and time are now 3 22 1997 8.400-- Begin non-accumulative timing loop here-- The elapsed time is 0.000-- The elapsed time is 1.100-- The elapsed time is 2.030-- The elapsed time is 3.020-- The elapsed time is 4.040-- The elapsed time is 5.030-- The elapsed time is 6.020-- The elapsed time is 7.010-- The elapsed time is 8.000-- Tick Tick Tick Tick Tick Tick Tick Tick Tick Tick Tick Tick

In the initial design of the Ada programming language, a requirement was included that it be capable of operating in a real-time environment. This requires that we have some control of time. We at least need the ability to read the current time and know when we arrive at some specified time. The example program named e_c26_p1.ada will illustrate how we can do just that.

The program begins in our usual way except for the addition of the new package listed in line 5, the Ada.Calendar package which must be supplied with your compiler if you have a validated Ada compiler. The specification package for Ada.Calendar is listed in section 9.6 of the Ada 95 reference Manual (ARM) and is probably listed somewhere in your compiler documentation. This

Page 157: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

package gives you the ability to read the system time and date, and allows you to set up a timed delay. Refer to a listing of the specification package of Ada.Calendar and follow along in the discussion in the next paragraph.

THE CLOCK FUNCTION

You will notice that the type TIME is private, so you cannot see how it is implemented, but you won't need to see it. A call to the function Clock returns the current time and date to a variable of type TIME, and other functions are provided to get the individual elements of the date or the number of seconds since midnight. You cannot read the individual elements directly, because some may change between subsequent reads leading to erroneous data. A procedure named Split is provided to split up the type TIME variable and return all four fields at once, and another is provided named Time_Of which will combine the individual elements into a TIME type variable when it is given the four elements as inputs.

Finally, you are provided with several overloadings of the addition, subtraction, and some compare operators in order to effectively use the Ada.Calendar package. A single exception is declared which will be raised if you attempt to use one of these subprograms wrong.

THE delay STATEMENT

The reserved word delay is used to indicate to the computer that you wish to include a delay at some point in the program. The delay is given in seconds as illustrated in line 19 of the program under study, and is declared as a fixed point number, which is defined by each implementation. The value of the delay is of type DAY_DURATION, but in this case, the universal_real type is used. The exact definition of the delay is given in Annex M of your compiler. It must allow a range of at least 0.0 to 86,400.0, which is the number of seconds in a day, and it must allow a delta of not more than 20 milliseconds. Refer to Annex M of your compiler documentation to see the exact declaration for this type for your particular compiler. The ARM requires that when the delay statement is encountered, the system must delay at the point of occurrence for at least the period specified in the delay statement, but does not say how much longer the system can delay. This leads to some inaccuracy in the delay which will be up to you to take care of. We will see how later in this example program.

A fixed point variable is used for the delay variable so that addition of times can be done with no loss in accuracy, and fixed point numbers have a fixed accuracy.

When you execute this program, you will see the first line displayed on the monitor, then a pause before the second message is displayed due to the delay statement in line 19. In fact, the pause will be at least 3.14 seconds according to the Ada specification.

USING THE CLOCK FUNCTION

In line 22, the Clock function is used to return the current time and date and assign it to the variable named Time_And_Date. In the next line we use the procedure named Split to split the time and date, which is contained in the composite variable named Time_And_Date, into its various components. Although the only component we are interested in is the Seconds field, we must provide a variable for each of the other fields simply because of the nature of the procedure call. Within the function call, we assign the value of Seconds to Start for later use. This is a record of the time when we started the loop which we will use later. The time is in the form of the number of seconds that have elapsed since midnight according to the definition of the calendar package.

We execute a loop in lines 25 through 38 where we read the time and date, split it into its component parts, and display each of the components. Instead of displaying the time since midnight, we subtract the Start time from the current time in line 35, where we are actually using one of the overloadings from the Ada.Calendar package. We display the elapsed time since we executed line 22 of this program. Finally, we put in a total delay of one second each time we pass through the loop so we can see the delays accumulate.

Page 158: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

WE ARE ACCUMULATING ERRORS IN THIS LOOP

You will recall that the delay statement requires a delay of at least the amount listed, but says nothing of extra delay allowed when returning to the program, in order to give the compiler writers leeway in how to implement the delay statement. In addition, we will require some time to execute the other statements in the loop, so it should not be surprising to you that when you compile and execute this program, the time will not advance by exactly one second for each pass through the loop, but will precess slightly as time passes.

A LOOP WITHOUT ERRORS

In lines 42 through 51, we essentially repeat the loop but with a slight difference. Instead of delaying for one second in each loop, we delay the amount needed to get to the desired point in time. In line 50, we convert the type of Index to DAY_DURATION with an explicit type conversion, then subtract the current elapsed time to calculate the desired time of the delay. This prevents an accumulation of error, and when you run the program you will see only the digitizing error introduced by the fixed point number. However, there is a potential problem with this method.

WHAT IF A NEGATIVE DELAY IS REQUESTED?

When calculating delay times like this, it is possible for the required delay time to result in a negative number. The Ada designers had enough foresight to see that in most applications, you would desire to simply push forward, so they defined the delay such that a negative value for the delay time would be construed as a zero, and no error would be raised. If a negative time should be considered an error condition for your application, it is up to you to detect it and issue an appropriate error message, or raise an exception.

The final point to be made about this example program is the delay until statement illustrated in line 56. The system delays here until the absolute time given which is of type Time. If the time given is previous to the time when this statement is executed, there will be no delay. The rest of this loop is trivial, so you can study it on your own.

Be sure to compile and execute this program and observe the output. Due to the delays in the first loop, the data output to the monitor is somewhat irregular and can be seen when the program is executed.

THE delay IS NOT PART OF CALENDAR

One final point must be made before we leave this program. The Ada.Calendar package and the delay statement were both introduced here, and even though they work well together, they are completely separate. The delay statement is not a part of the Ada.Calendar package as will be evidenced in the example programs later in this chapter.

OUR FIRST TASKING EXAMPLE

Example program ------> e_c26_p2.ada

-- Chapter 26 - Program 2with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Task1 is

task First_Task; task body First_Task is begin for Index in 1..4 loop Put("This is in First_Task, pass number "); Put(Index, 3); New_Line; end loop;

Page 159: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

end First_Task;

task Second_Task; task body Second_Task is begin for Index in 1..7 loop Put("This is in Second_Task, pass number"); Put(Index, 3); New_Line; end loop; end Second_Task;

task Third_Task; task body Third_Task is begin for Index in 1..5 loop Put("This is in Third_Task, pass number "); Put(Index, 3); New_Line; end loop; end Third_Task;

begin Put_Line("This is in the main program.");end Task1;

-- Result of Execution

-- This is in Third_Task, pass number 1-- This is in Third_Task, pass number 2-- This is in Third_Task, pass number 3-- This is in Third_Task, pass number 4-- This is in Third_Task, pass number 5-- This is in Second_Task, pass number 1-- This is in Second_Task, pass number 2-- This is in Second_Task, pass number 3-- This is in Second_Task, pass number 4-- This is in Second_Task, pass number 5-- This is in Second_Task, pass number 6-- This is in Second_Task, pass number 7-- This is in First Task, pass number 1-- This is in First Task, pass number 2-- This is in First Task, pass number 3-- This is in First Task, pass number 4-- This is in the main program.

Examine the file named e_c26_p2.ada for an example program containing some tasking. The first thing you should examine is the main program consisting of a single executable statement in line 38 that outputs a line of text to the monitor. It may seem strange that it doesn't call any of the code in the declaration part, but it doesn't have to as we shall see.

An Ada task is composed of a task specification and a task body, the former being illustrated in line 7, and the latter being illustrated in lines 8 through 15. This is the first task and both parts begin with the reserved word task. The structure of a task is very similar to the structure of a subprogram or package. This first example is a very simple task which executes a for loop containing output statements. The end result consists of four lines of text being displayed on the monitor.

Page 160: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

THE TASK SPECIFICATION

The task specification can be much more involved than this one, but this is a good first example. The general structure for the task specification is given by;

task <task-name> is <entry-points>; end <task-name>;

but if there are no entry points, then only the reserved word task is needed followed by the task name. As with the package, the task specification must come before the task body.

THE TASK BODY

The task body begins with the reserved words task and body, followed by the task name, and the remainder of the structure is identical to that of a procedure. There is a declarative part where types, variables, subprograms, packages, and even other tasks can be declared, followed by the executable part which follows the same rules as those for a main program. In this case, there is no declarative part, only an executable part. When this task is executed, it will output four lines to the monitor and stop. We will say a little more about this task when it gets executed in a few minutes.

TWO MORE TASKS

It should be clear to you that there are two additional tasks in this program, one in lines 17 through 25 and another in lines 27 through 35, each with its own respective task specification and task body. There are actually four tasks, because the main program is itself another task that will run in parallel with the three explicitly declared here. Since it is very critical that you understand the order of execution of the various tasks, we will spend some time defining it in detail.

Ada always uses linear declaration and when it loads any program, it loads things in the order given in the listing. Therefore when it finds the task body beginning in line 8, it elaborates all of its declarations, although there are none in this case, then loads the executable part of the task, but does not begin execution yet. It effectively makes the task wait at the begin in line 9 until the other tasks are ready to begin execution. It does the same for the task named Second_Task, and also for Third_Task. When all declarations are elaborated it arrives at the begin of the main program in line 37. At this point, all four tasks are waiting at their respective begin statements, and all four tasks are permitted to begin execution at the same time. All are of equal priority, but a strange thing happens when they begin execution.

ORDER OF EXECUTION IS UNDEFINED BY ADA

The rules of execution, as defined by the ARM, give no requirements that any form of time slicing be done, nor is it illegal for an implementation to allow starving to occur. Starving is where one task uses all of the available time and the other task or tasks are allowed to starve because they receive no time for operation. In addition, there is no required order of execution concerning which of the four tasks in our example will execute first, because we have not included any form of priority. Because of these rules, we cannot predict exactly what your implementation will do, but can only give the results of executing this program on one particular validated Ada compiler. As indicated by the results of execution listed following the program, this particular compiler allows the task named Third_Task to utilize all computing power until it runs to completion, then Second_Task uses all of the resources, followed by First_Task, and finally the main program runs. Your particular compiler may execute these statements in a different order, but that is still correct. The only requirement is that all 17 lines be output in any order. Of course the order of output is defined within any task according to the rules of sequential operation. For example, pass number 2 of Third_Task must follow pass 1 of the same task.

HOW DOES TASKING END?

When the task named Third_Task arrived at its end statement in line 35, it had executed all of its

Page 161: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

required statements and had nothing else to do, so it simply waited there until the other tasks were completed. When all four tasks, including the main program task, had arrived at their respective end statements, the Ada system knew there was nothing else to do, so it returned to the operating system as it does at any normal program completion.

Because of the way this program operates, it is not clear that all four tasks are operating in parallel, but they actually are, as we will see in the next example program. Compile and execute this program and study the output from your compiler to see if it is different from that obtained as output from our compiler.

ADDING DELAYS ADDS TO THE CLARITY OF OPERATION

Example program ------> e_c26_p3.ada

-- Chapter 26 - Program 3with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Task2 is

task First_Task; task body First_Task is begin for Index in 1..4 loop delay 2.0; Put("This is in First_Task, pass number "); Put(Index, 3); New_Line; end loop; end First_Task;

task Second_Task; task body Second_Task is begin for Index in 1..7 loop delay 1.0; Put("This is in Second_Task, pass number"); Put(Index, 3); New_Line; end loop; end Second_Task;

task Third_Task; task body Third_Task is begin for Index in 1..5 loop delay 0.3; Put("This is in Third_Task, pass number "); Put(Index, 3); New_Line; end loop; end Third_Task;

begin-- for Index in 1..5 loop-- delay 0.7; Put_Line("This is in the main program.");-- end loop;end Task2;

Page 162: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Result of Execution (with comments in main program)

-- This is in the main program.-- This is in Third_Task, pass number 1-- This is in Third_Task, pass number 2-- This is in Third_Task, pass number 3-- This is in Second_Task, pass number 1-- This is in Third_Task, pass number 4-- This is in Third_Task, pass number 5-- This is in First Task, pass number 1-- This is in Second_Task, pass number 2-- This is in Second_Task, pass number 3-- This is in First Task, pass number 2-- This is in Second_Task, pass number 4-- This is in Second_Task, pass number 5-- This is in First Task, pass number 3-- This is in Second_Task, pass number 6-- This is in Second_Task, pass number 7-- This is in First Task, pass number 4

-- Result of Execution (with main program comments removed)

-- This is in Third_Task, pass number 1-- This is in Third_Task, pass number 2-- This is in the main program.-- This is in Third_Task, pass number 3-- This is in Second_Task, pass number 1-- This is in Third_Task, pass number 4-- This is in the main program.-- This is in Third_Task, pass number 5-- This is in First Task, pass number 1-- This is in Second_Task, pass number 2-- This is in the main program.-- This is in the main program.-- This is in Second_Task, pass number 3-- This is in the main program.-- This is in First Task, pass number 2-- This is in Second_Task, pass number 4-- This is in Second_Task, pass number 5-- This is in First Task, pass number 3-- This is in Second_Task, pass number 6-- This is in Second_Task, pass number 7-- This is in First Task, pass number 4

Examine the next example program named e_c26_p3.ada and you will notice that delay statements are added within each of the loops. The delays were chosen to illustrate that each loop is actually operating in parallel. The main program is identical to the previous program if you ignore the commented out statements, and since the main program has no delay prior to its output statement, it will be the first to output to the monitor. The main program will also be the first to complete its operation and will then patiently wait at its end statement until the other three tasks complete their jobs.

Because of the differences in the delays, the three tasks will each complete their delays at different times and the output should be very predictable. If you examine the result of execution given at the end of the program, you will see that the three tasks actually are running in parallel as we stated

Page 163: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

earlier. It would be profitable for you to compile and execute this program so you can observe the output firsthand.

WHAT ABOUT THE MAIN PROGRAM?

Return once again to the program named e_c26_p3.ada so we can examine the main program. You should remove the comment marks from the three statements in the main program so that it also contains a loop and a delay within each pass through the loop. When you compile and execute the program as modified, it will execute a delay prior to the first output, and will output a total of five lines to the monitor interlaced with the outputs of the other tasks. This should be a good indication to you that the main program is acting just like another task. Compile and execute this program, as modified, to see that the main program acts just like another task.

A BLOCK CAN HAVE TASKS WITHIN IT

Example program ------> e_c26_p4.ada

-- Chapter 26 - Program 4with Ada.Text_IO; use Ada.Text_IO;

procedure Task3 is

begin

Put_Line("This is in the main program.");

declare task First_Task; task Second_Task; task Third_Task;

task body First_Task is begin for Index in 1..4 loop delay 0.0; Put_Line("This is in First_Task."); end loop; end First_Task;

task body Second_Task is begin for Index in 1..4 loop delay 0.0; Put_Line("This is in Second_Task."); end loop; end Second_Task;

task body Third_Task is begin for Index in 1..8 loop delay 0.0; Put_Line("This is in Third_Task."); end loop; end Third_Task; begin delay 0.0; Put_Line("This is in the block body."); delay 0.0; Put_Line("This is also in the block body."); end; -- of declare

Page 164: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Put_Line("This is at the end of the main program.");

end Task3;

-- Result of Execution

-- This is in the main program.-- This is in First_Task.-- This is in Second_Task.-- This is in the block body.-- This is in Third_Task.-- This is in First_Task.-- This is in Second_Task.-- This is also in the block body.-- This is in Third_Task.-- This is in First_Task.-- This is in Second_Task.-- This is in Third_Task.-- This is in First_Task.-- This is in Second_Task.-- This is in Third_Task.-- This is in Third_Task.-- This is in Third_Task.-- This is in Third_Task.-- This is in Third_Task.-- This is at the end of the main program.

Examine the program named e_c26_p4.ada for an example of a main program with tasks embedded within it. There is a block declared in the main program in lines 11 through 44 which is executed inline just like any other block. This block however, has the same three tasks we have used in the previous programs embedded within it. You will notice that the order of declaration is different here, since the three task specifications are given together in lines 12 through 14, but this is perfectly legal and would be legal in the last two programs also. The only requirement is that the task specification must be given before the task body for each declared task.

WHAT GOOD IS A DELAY OF ZERO?

You will notice that all of the delays are of zero time, which may lead you to ask why we should even bother to include the delays. Each time the system encounters a delay of any kind, it must look at each task to see if any of them have timed out and need to be exercised. When it does that, it may advance to the next task in order, but it is not required to. Which task will be selected as the next task is undefined by the ARM, so any task can be executed next, including the one that is presently executing. The end result is that all four tasks, including the one in the executable part of the block, may be executed in a round robin fashion, and none of the tasks are starved. It would be perfectly legal for a single task to starve the others, according to the ARM, so if your compiler allows this, it is not an error. Tasking, as used in a real programming situation will not have this problem since additional constructs will be included as we will discuss in the next few chapters of this tutorial.

In a manner similar to that in the other example programs, when all four tasks are at their end points, the block is completed, and the remaining statement is executed in the main program. Note carefully that the main program did not enter into the tasking that was executed within the block. The inline block was executed as another sequential statement as part of the main program. Thus line 9 was executed, then the block in lines 11 through 44 was executed. When all four tasks including the one within the block body were completed, the output statement in line 46 was allowed to execute.

Page 165: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Compile and execute this program and study the results. Use the results to prove to yourself that the statements in lines 9 and 46 of the main program did not enter into the tasking.

PROGRAMMING EXERCISES

1. Write a procedure that can be called from anywhere in the program named e_c26_p1.ada that will use the Seconds field returned from the procedure named Split, and display the time of day in hours, minutes, and seconds. Call this procedure from a few places within the program.(Solution)

-- Chapter 26 - Programming exercise 1with Ada.Text_IO; use Ada.Text_IO; with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;with Calendar; use Calendar;

procedure CH26_1 is

package Fix_IO is new Ada.Text_IO.Fixed_IO(DAY_DURATION); use Fix_IO;

Year,Month,Day : INTEGER; Start,Seconds : DAY_DURATION; Time_And_Date : TIME;

-- This procedure outputs the time in an Hour:Minute:Second format-- using a FLOAT type for splitting the time into the various-- fields. It works fine for simple time logging but lacks the-- accuracy required for finer time. If finer time is required,-- it will be necessary to keep the time in the fixed point format,-- and use lots of type conversions to get the three fields, and-- the fractional second field if needed. An alternative method-- would be to use a floating point type with more significant-- digits to maintain the accuracy, but it would require more time-- to execute. This is definitely an application for the fixed-- point data type. procedure Output_Time(Time_In_Seconds : DAY_DURATION) is Time : FLOAT; Hours, Minutes, Seconds : INTEGER; begin Time := FLOAT(Time_In_Seconds); Hours := INTEGER((Time - 30.0 * 60.0) / (60.0 * 60.0)); Time := Time - FLOAT(Hours) * 60.0 * 60.0; Minutes := INTEGER((Time - 30.0) / 60.0); Seconds := INTEGER(Time - 0.5) mod 60; Put(Hours, 3); Put(":"); Put(Minutes, 2); Put(":"); Put(Seconds, 2); end Output_Time;

begin

Time_And_Date := Clock; Split(Time_And_Date, Year, Month, Day, Seconds); Output_Time(Seconds); Put_Line(" Begin 3.14 second delay");

delay 3.14;

Time_And_Date := Clock; Split(Time_And_Date, Year, Month, Day, Seconds); Output_Time(Seconds);

Page 166: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Put_Line(" End of 3.14 second delay");

Time_And_Date := Clock; Split(Time_And_Date, Year, Month, Day, Start); -- get starting time

for Index in 1..9 loop Put("The date and time are now"); Time_And_Date := Clock; Split(Time_And_Date,Year,Month,Day,Seconds); Put(Month,3); delay 0.2; Put(Day,3); delay 0.1; Put(Year,5); delay 0.1; Put(Seconds - Start,8,3,0); New_Line; delay 0.6; end loop;

Put_Line("Begin non-accumulative timing loop here.");

Time_And_Date := Clock; Split(Time_And_Date, Year, Month, Day, Start); -- get starting time for Index in 1..9 loop Time_And_Date := Clock; Split(Time_And_Date, Year, Month, Day, Seconds); Put("The elapsed time is"); Put(Seconds - Start, 8, 3, 0); New_Line; delay Day_Duration(Index) - (Seconds - Start); end loop;

Put(" The current time is "); Time_And_Date := Clock; Split(Time_And_Date, Year, Month, Day, Seconds); Output_Time(Seconds);

end CH26_1;

-- Result of Execution

-- 9:54:13 Begin 3.14 second delay-- 9:54:16 End of 3.14 second delay-- The date and time are now 7 22 1988 0.000-- The date and time are now 7 22 1988 1.090-- The date and time are now 7 22 1988 2.140-- The date and time are now 7 22 1988 3.180-- The date and time are now 7 22 1988 4.230-- The date and time are now 7 22 1988 5.270-- The date and time are now 7 22 1988 6.320-- The date and time are now 7 22 1988 7.360-- The date and time are now 7 22 1988 8.400-- Begin non-accumulative timing loop here-- The elapsed time is 0.000-- The elapsed time is 1.100-- The elapsed time is 2.030-- The elapsed time is 3.020-- The elapsed time is 4.040-- The elapsed time is 5.030-- The elapsed time is 6.020-- The elapsed time is 7.010

Page 167: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- The elapsed time is 8.000-- The current time is 9:54:35

2. Modify the program named e_c26_p4.ada to include the executable statements in lines 9 and 46 within loops, with delays, to prove that the statements are executed in a sequential fashion and do not enter into the tasking which is restricted to the block in lines 11 through 44.(Solution)

-- Chapter 26 - Programming exercise 2with Ada.Text_IO; use Ada.Text_IO;with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure CH26_2 is

begin

for Index in 1..4 loop delay 0.5; Put_Line("This is in the main program."); end loop;

declare task First_Task; task Second_Task; task Third_Task;

task body First_Task is begin for Index in 1..4 loop delay 0.0; Put_Line("This is in First_Task."); end loop; end First_Task;

task body Second_Task is begin for Index in 1..4 loop delay 0.0; Put_Line("This is in Second_Task."); end loop; end Second_Task;

task body Third_Task is begin for Index in 1..8 loop delay 0.0; Put_Line("This is in Third_Task."); end loop; end Third_Task; begin delay 0.0; Put_Line("This is in the block body."); delay 0.0; Put_Line("This is also in the block body."); end; -- of declare

for Index in 1..4 loop delay 0.5; Put_Line("This is at the end of the main program."); end loop;

Page 168: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

end CH26_2;

-- Result of Execution

-- This is in the main program.-- This is in the main program.-- This is in the main program.-- This is in the main program.-- This is in First_Task.-- This is in Second_Task.-- THis is in the block body.-- This is in Third_Task.-- This is in First_Task.-- This is in Second_Task.-- This is also in the block body.-- This is in Third_Task.-- This is in First_Task.-- This is in Second_Task.-- This is in Third_Task.-- This is in First_Task.-- This is in Second_Task.-- This is in Third_Task.-- This is in Third_Task.-- This is in Third_Task.-- This is in Third_Task.-- This is in Third_Task.-- This is at the end of the main program.-- This is at the end of the main program.-- This is at the end of the main program.-- This is at the end of the main program.

Page 169: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Ada Tutorial - Chapter 27

THE SIMPLE RENDEZVOUS

COMMUNICATION BETWEEN TASKS

In the last chapter, we ran a few programs with tasking, but they all used simple tasking with no form of synchronization. In a real situation using tasking, some form of communication between tasks will be required so we will study the simple rendezvous in this chapter.

Example program ------> e_c27_p1.ada

-- Chapter 27 - Program 1with Ada.Text_IO;use Ada.Text_IO;

procedure HotDog is

task Gourmet is entry Make_A_Hot_Dog; end Gourmet;

task body Gourmet is begin Put_Line("I am ready to make a hot dog for you"); for Index in 1..4 loop accept Make_A_Hot_Dog do delay 0.8; Put("Put hot dog in bun "); Put_Line("and add mustard"); end Make_A_Hot_Dog; end loop; Put_Line("I am out of hot dogs"); end Gourmet;

begin for Index in 1..4 loop Gourmet.Make_A_Hot_Dog; delay 0.1; Put_Line("Eat the resulting hot dog"); New_Line; end loop; Put_Line("I am not hungry any longer");end HotDog;

-- Result of execution

-- I am ready to make a hot dog for you-- Put hot dog in bun and add mustard-- Eat the resulting hot dog---- Put hot dog in bun and add mustard-- Eat the resulting hot dog---- Put hot dog in bun and add mustard-- Eat the resulting hot dog---- Put hot dog in bun and add mustard-- I am out of hot dogs-- Eat the resulting hot dog--

Page 170: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- I am not hungry any longer

Examine the program named e_c27_p1.ada which will illustrate the simple rendezvous. This program consists of two tasks, the one in lines 7 through 22 and the main program itself.

THE entry STATEMENT

The task specification is a little more complicated here than it was in any of the example programs in the previous chapter because we have an entry point declared in the task. The reserved word entry begins the entry declaration and is followed by the entry name, which is Make_A_Hot_Dog in this case. The entry statement defines the interface to the outside of a task in much the same way as the procedure header defines the external interface to a package in the package specification. Unlike a package, no types, variables, or constants are allowed to be declared in the task specification, but there is no limit to the number of entry points allowed. An entry can have formal parameters declared as part of the entry name, (we will have an example in the next example program), but it cannot have a return parameter similar to a function. One or more of the formal parameters is permitted to have a parameter of mode out or in out however, so data can be passed both ways while a synchronization is effected. The in mode is also permitted. We will have more to say about this topic later in this chapter.

THE accept STATEMENT

The task body is a very simple sequence of statements in which a message is output to the monitor, a loop is executed four times, and another message is output. The thing that is new here is the accept statement within the loop. The accept statement begins with the reserved word accept and has the following general form;

accept <entry-name> do <executable statements> end <entry-name>;

and any legal Ada statements can be contained within it. When execution of the task reaches the accept statement, the task "goes to sleep" until some other task makes a call to this particular accept statement. The term "goes to sleep" means that the task does not simply sit there and execute a do nothing loop while it is waiting, effectively wasting the resources of the system. Instead, it is actually doing nothing until it is awakened by an entry call. It is also possible to have an accept statement with no statements contained within it. It will have the following form;

accept <entry-name>;

This statement will only be used for task synchronization since there is no data passed to the entered task.

THE ENTRY CALL

The main part of the program is another loop with four iterations followed by a statement to display a line of text on the monitor. The only thing unusual about the loop is the statement in line 26 which is an entry call to the entry named Make_A_Hot_Dog in the task named Gourmet. Keep in mind that these are two tasks that are operating in parallel and we will carefully explain what is happening.

The entry call is executed at line 26 which wakes up the sleeping task at line 15, and the task named Gourmet continues from where it went to sleep. Notice that the calling program is not controlling the execution of Gourmet, but Gourmet itself is in control now that it has been allowed to continue operation. The entry call is not like a procedure call where the sequence of operation continues in the procedure, but is instead only a synchronization call that allows Gourmet to continue what it was doing prior to being put to sleep.

Page 171: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

WHAT IS THE CALLING PROGRAM DOING NOW?

During the time that the called task is executing statements within its accept block, the calling task is effectively put to sleep, and must wait until the called task completes its accept block. When Gourmet reaches line 19, both tasks are allowed to operate in parallel again until one or both reach their point of rendezvous again. If the main program reaches its entry call before Gourmet is ready to accept the call, then the main program will wait until Gourmet is ready. The accept statement, and the corresponding entry call, are therefore used to synchronize the two tasks.

If you were to move the end of Make_A_Hot_Dog to the line immediately after the accept statement, including a null of course, the output statements would then be running in parallel. The delays have been selected in such a way that after making this change, the hot dog would be eaten before it was made. This is one of the programming exercises at the end of this chapter.

A COUPLE OF FINE POINTS MUST BE MADE HERE

Although the entry call looks very much like a procedure call, it is different because it is not legal to use a use clause for a task. It is required therefore that every entry call must use the extended naming convention, or the "dotted" notation. Renaming can be used to reduce the size of the names used and will be illustrated in the next program.

You will notice that the two tasks were composed of exactly four calls and four executions of the accept statement. This was done on purpose in order to simplify the problem of task termination at this point in our study. The study of task termination will be covered in detail in the next chapter. Until then, you should see what happens when the two loops are different. Change the loop in the Gourmet task to 5 iterations and see what happens if it waits to accept a call that never comes, then make the loop in the main program bigger to see what happens when there is nothing to accept its call. We will study tasking exceptions in a later chapter of this tutorial.

The execution of a return statement (not illustrated here), within an accept statement corresponds to reaching the final end and effectively terminates the rendezvous.

SERVERS AND USERS

Tasks are categorized into two rather loosely defined groups, server tasks and user tasks. A server task is one that accepts calls from other tasks, and a user task is one that makes calls to one or more server tasks. In the present example program, Gourmet is a server task, and the main program is a user task. Some tasks both accept calls and make calls, and they are referred to as intermediate tasks. This terminology, or some other fairly self defining terminology may be used in the literature about Ada, so you should be aware that such classifications may exist.

Be sure to compile and execute this program making the changes suggested earlier and observe the results.

MULTIPLE ENTRY POINTS

Example program ------> e_c27_p2.ada

-- Chapter 27 - Program 2with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure HotDogs is

task Gourmet is entry Make_A_Hot_Dog(Serial_Number : INTEGER); end Gourmet;

procedure Get_Dog(Serial_Number : INTEGER) renames Gourmet.Make_A_Hot_Dog;

Page 172: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

task body Gourmet is begin Put_Line("I am ready to make a hot dog for you");

accept Make_A_Hot_Dog(Serial_Number : INTEGER) do Put_Line("This will be the first hot dog"); Put("Put hot dog in bun "); Put_Line("and add mustard"); delay 0.8; end Make_A_Hot_Dog;

for Index in 1..4 loop accept Make_A_Hot_Dog(Serial_Number : INTEGER) do Put("This will be hot dog number"); Put(Serial_Number, 3); New_Line; Put("Put hot dog in bun "); Put_Line("and add mustard"); delay 0.8; end Make_A_Hot_Dog; end loop;

accept Make_A_Hot_Dog(Serial_Number : INTEGER) do Put_Line("This will be the last hot dog"); Put("Put hot dog in bun "); Put_Line("and add mustard"); delay 0.8; end Make_A_Hot_Dog;

Put_Line("I am out of hot dogs"); end Gourmet;

begin for Index in 1..6 loop Get_Dog(Index); Put_Line("Eat the resulting hot dog"); New_Line; end loop; Put_Line("I am not hungry any longer");end HotDogs;

-- Result of execution

-- I am ready to make a hot dog for you-- This will be the first hot dog-- Put hot dog in bun and add mustard-- Eat the resulting hot dog---- This will be hot dog number 2-- Put hot dog in bun and add mustard-- Eat the resulting hot dog---- This will be hot dog number 3-- Put hot dog in bun and add mustard-- Eat the resulting hot dog---- This will be hot dog number 4-- Put hot dog in bun and add mustard-- I am out of hot dogs-- Eat the resulting hot dog--

Page 173: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- This will be hot dog number 5-- Put hot dog in bun and add mustard-- Eat the resulting hot dog---- This will be the last hot dog-- Put hot dog in bun and add mustard-- Eat the resulting hot dog---- I am not hungry any longer

Examine the program named e_c27_p2.ada for a few added tasking topics beginning with multiple accept statements. You will notice that there are three accept statements in the task body corresponding to a single entry point declared in the task specification. There is no limit to the number of accept statements and they are executed when the logic causes execution to arrive at each one in turn. Remember that the task is executed in the logical order as defined in the sequence of statements, except that each time the logic causes execution to arrive at an accept statement, the task will "go to sleep" until an entry call is made to the waiting accept statement. Moreover, the logic does not care where the entry call comes from, nor does it know where it came from, it only cares that the entry call is made. Thus it is possible for several different tasks to be calling this entry point and allowing the task to progress through its logic. Because there is only one other task in this program, we know exactly where the entry call is being generated.

Careful study of the logic will reveal that the accept statement in line 18 must be called, followed by four calls to line 26, and another call to line 36. After six calls to this entry point, the task will reach the end of its execution and will be completed. You will notice that we make exactly six calls to this entry point by the task which is the main program, so everything will come out right.

RENAMING A TASK ENTRY

The alternate name, which is declared in line 11, is used in the main program task to illustrate the use of the renaming facility. Note that the dotted notation is not required in this case. Once again, you can change either the number of calls or the number of accepts to see the exception error or a deadlock condition.

ENTRY PARAMETERS

You should have noticed by now that each of the accept statements has a formal parameter associated with it in the same manner that a subprogram can have. In fact, it is permissible to have as many parameters as you desire to transfer data either from the calling task to the called task or back the other way. All three modes of parameter passing are legal for use and the in mode will be used if none is specified as is done here. There is one difference in a task however, you are not permitted to have a return parameter like a function has, but you can return a value by using an out or an in out parameter.

Since you can pass parameters both ways, the task appears to be executed just like a procedure, but there is a very definite difference as mentioned earlier. A procedure is actually executed by the calling program, but the task is simply let free to run on its own in parallel with the calling program. The task is not a subservient program but is running its own logic as it is coded to do.

Be sure to compile and execute this program after you understand what it is supposed to do.

MULTIPLE CALLING TASKS

Example program ------> e_c27_p3.ada

-- Chapter 27 - Program 3

Page 174: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure ManyDogs is

task Gourmet is entry Make_A_Hot_Dog(Serial_Number : INTEGER; With_Mustard : BOOLEAN); end Gourmet;

task body Gourmet is begin Put_Line("I am ready to make a hot dog for you"); for Index in 1..5 loop accept Make_A_Hot_Dog(Serial_Number : INTEGER; With_Mustard : BOOLEAN) do Put("Put hot dog number"); Put(Serial_Number, 2); Put(" in bun "); if With_Mustard then Put_Line("and add mustard"); else Put_Line("and hold the mustard"); end if; delay 0.8; end Make_A_Hot_Dog; end loop; Put_Line("I am out of hot dogs"); end Gourmet;

task Bill; task John;

task body Bill is begin for Index in 1..3 loop Gourmet.Make_A_Hot_Dog(Index, FALSE); Put_Line("Bill is eating the hot dog without mustard"); New_Line; end loop; Put_Line("Bill is not hungry any longer"); end Bill;

task body John is begin for Index in 1..2 loop Gourmet.Make_A_Hot_Dog(Index, TRUE); Put_Line("John is eating the hot dog with mustard"); New_Line; end loop; Put_Line("John is not hungry any longer"); end John;

begin null;end ManyDogs;

-- Result of execution

-- I am ready to make a hot dog for you-- Put hot dog number 1 in bun and add mustard

Page 175: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Put hot dog number 1 in bun and hold the mustard-- John is eating the hot dog with mustard---- Put hot dog number 2 in bun and add mustard-- Bill is eating the hot dog without mustard---- Put hot dog number 2 in bun and hold the mustard-- John is eating the hot dog with mustard---- John is not hungry any longer-- Bill is eating the hot dog without mustard---- Put hot dog number 3 in bun and hold the mustard-- I am out of hot dogs-- Bill is eating the hot dog without mustard---- Bill is not hungry any longer

Examine the program named e_c27_p3.ada for an example of two tasks calling a third task's entry point. The task named Gourmet has a single entry point, but this time there are two formal parameters declared in the task specification and the same two declared in the accept statement. These must agree of course. The entry point named Make_A_Hot_Dog in line 16 contains some text to display and a delay of 0.8 second, because it takes a little time to make a hot dog. You will notice that sometimes mustard is added, and sometimes it is not, depending on who is eating the result. We will say more about this later.

We have two additional tasks in lines 35 through 53 named Bill and John, each of which executes in parallel, and each of which requests a number of hot dogs and consumes them in zero time, since there is no delay prior to their next request. You will also notice that once again the numbers come out right because Bill requests three hot dogs and John asks for two while the task that supplies them makes exactly five. We will discuss better methods of task termination later. For the time being, simply accept the utopian situation depicted here.

ENTRIES STACK UP AT THE ENTRY POINT

Considering all that we have said so far about tasking, you should understand that all three of these tasks begin execution at the same time and Bill and John both request a hot dog immediately. Since there is only one entry point, only one can be served at a time, and the Ada definition does not specify which will be serviced first. It does specify that both will ultimately be served, so there is an implicit queue at the entry point where requests can be stored until they can be serviced in turn. All requests are serviced on a First In First Out (FIFO) basis with no concern for priority of tasks (to be defined later). This queue is a "hidden" queue as far as you, the programmer, are concerned because you have no access to it. You cannot therefore, sort through the entries and redefine the order of execution of the various entry requests. The attribute named COUNT is available which will return the number of requests pending on any entry queue. Its use will be illustrated in the program named e_c29_p6.ada in chapter 29 of this tutorial.

After we study some of the advanced tasking topics, you will have the ability to define several queues with different priorities and set up your own priorities as needed.

THE ORDER OF OUTPUT IS SORT OF ODD

Back to the program at hand. Examining the result of execution will reveal that for some unknown reason, the task named John made the first request and the task named Gourmet made a hot dog with mustard first, because John's task was the one requesting a hot dog with mustard. However, before John was allowed to continue execution, the Gourmet task continued and serviced the next call in its entry queue for Make_A_Hot_Dog, and made a hot dog without mustard for Bill. At this

Page 176: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

point Gourmet had completed all of its entry calls and allowed one of the other tasks to run. The task named John was then allowed to continue execution. John ate his hot dog and requested another. Once again, by some undefined method, the task named Gourmet was allowed to run and make a second hot dog for John, with mustard, when Bill still hasn't been allowed to eat his first one. This continues until all conditions have been satisfied, which means that five hot dogs have been made, and all five have been consumed. Both consuming tasks declare their lack of hunger, and the task named Gourmet declares that it is out of hot dogs. The program has finally run to completion.

WHAT ABOUT THE FUNNY ORDER OF RESULTS?

Even though the results seemed to come out in a funny order, they did follow all the rules we set down for them to follow. Remember that as an experienced programmer, you are accustomed to seeing everything come out in a very well defined precise order, because you have spent your programming career writing sequential programs. If you look at this output from the point of view of each task, you will see that the output from each task is perfectly sequential, as defined by the logic of the task. Additionally, you will see that the order of execution has been preserved as defined by the various rendezvous, because nobody eats a hot dog before it is made, and there are no hot dogs made too early. The synchronization of the tasks has been done exactly as we requested.

Spend enough time studying the logic here to completely understand what is happening, then compile and execute this program to see if your compiler does anything in a different order.

THE select STATEMENT

Example program ------> e_c27_p4.ada

-- Chapter 27 - Program 4with Ada.Text_IO;use Ada.Text_IO;

procedure Retail1 is

Number_Of_Dogs : INTEGER := 0;

task Retail_Hot_Dogs is entry Stock_With_A_Hot_Dog; entry Deliver_A_Hot_Dog; end Retail_Hot_Dogs;

task body Retail_Hot_Dogs is begin accept Stock_With_A_Hot_Dog do Put_Line("Put the first hot dog on the shelf"); Number_Of_Dogs := Number_Of_Dogs + 1; end Stock_With_A_Hot_Dog;

for Index in 1..7 loop Put("In loop => "); select accept Stock_With_A_Hot_Dog do Put_Line("Add a hot dog to the shelf"); Number_Of_Dogs := Number_Of_Dogs + 1; end Stock_With_A_Hot_Dog; or accept Deliver_A_Hot_Dog do Put_Line("Remove a hot dog from the shelf"); Number_Of_Dogs := Number_Of_Dogs - 1; end Deliver_A_Hot_Dog; end select; end loop;

Page 177: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

end Retail_Hot_Dogs;

begin for Index in 1..4 loop Retail_Hot_Dogs.Stock_With_A_Hot_Dog; Retail_Hot_Dogs.Deliver_A_Hot_Dog; end loop;end Retail1;

-- Result of execution

-- Put the first hot dog on the shelf-- In loop => Remove a hot dog from the shelf-- In loop => Add a hot dog to the shelf-- In loop => Remove a hot dog from the shelf-- In loop => Add a hot dog to the shelf-- In loop => Remove a hot dog from the shelf-- In loop => Add a hot dog to the shelf-- In loop => Remove a hot dog from the shelf

Examine the program named e_c27_p4.ada for our first example program using a select statement. The select statement is used to allow a task to select between two or more alternative entry points. In effect, a task can be waiting at two or more entry points for an entry call to be made, and can act on the first occurring entry call. The structure of the select statement is given by;

select accept ...; -- Complete entry point logic or accept ...; -- Complete entry point logic or accept ...; -- Complete entry point logic end select;

and is illustrated in lines 23 through 33 of the present example program. In this case there are two select alternatives, but there is no limit to the number of selections that can be included. Each additional branch is delimited by the reserved word or. When program control of the task arrives at the select statement in line 23, either entry call can be accepted and acted upon immediately.

THE OVERALL PROGRAM

Common sense tells us that we cannot deliver a hot dog until we stock the shelf with a hot dog, so the program has been written to reflect this. The task requires an entry call to Stock_With_A_Hot_Dog before it begins the loop with the select in it to assure that at least one hot dog will be available. After that, it doesn't care what the order of entry calls is because the select statement in the loop will allow them to occur in any order. This is a very simplistic approach to setting up a precedence requirement in an Ada task, but it is too simple to really be effective which we shall see when we examine some of the problems that can occur.

In the first place, if the main program, or task, fails to call Stock_With_A_Hot_Dog first, the system will simply lock up with the calling program demanding a delivered hot dog and steadfastly refusing to continue until it does, and the called task refusing to deliver one until it has been stocked with one in line 16. The system is in deadlock with both tasks refusing to do anything. You can simulate this condition by reversing the two calls in lines 39 and 40. Your compiler will probably give a message indicating deadlock has occurred and terminate operation of the program. Another problem has to do with the inflexibility of this program, since we have once again counted the

Page 178: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

number of calls required to complete the two tasks and programmed compatible numbers in the two tasks.

A further problem involves the fact that, after one hot dog has been stocked, there is nothing to prevent us from taking delivery of hundreds of hot dogs without adding any more to the shelves.

When you think you understand this program, compile and execute it, then we will go on to the next program where we will solve two of the three problems mentioned in connection with the present program.

SELECT STATEMENTS WITH GUARDS

Example program ------> e_c27_p5.ada

-- Chapter 27 - Program 5with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Retail2 is

Number_Of_Dogs : INTEGER := 0;

-- The main program adds and deletes -- hot dogs to/from the shelf.

task Five_Dogs; -- This task adds five hot dogs to stock

task Remove_Five_Dogs; -- This task deletes five from stock

task Retail_Hot_Dogs is entry Stock_With_A_Hot_Dog; -- This adds a hot dog to stock entry Deliver_A_Hot_Dog; -- This deletes one from stock end Retail_Hot_Dogs;

task body Retail_Hot_Dogs is begin for Index in 1..18 loop Put("In loop => "); select when Number_Of_Dogs < 8 => accept Stock_With_A_Hot_Dog do Number_Of_Dogs := Number_Of_Dogs + 1; Put("Add a hot dog to the shelf, number ="); Put(Number_Of_Dogs, 3); New_Line; end Stock_With_A_Hot_Dog; or when Number_Of_Dogs > 0 => accept Deliver_A_Hot_Dog do Put_Line("Remove a hot dog from the shelf"); Number_Of_Dogs := Number_Of_Dogs - 1; end Deliver_A_Hot_Dog; end select; end loop; end Retail_Hot_Dogs;

task body Five_Dogs is begin for Index in 1..5 loop delay 0.1; Retail_Hot_Dogs.Stock_With_A_Hot_Dog; end loop; end Five_Dogs;

Page 179: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

task body Remove_Five_Dogs is begin for Index in 1..5 loop delay 0.6; Retail_Hot_Dogs.Deliver_A_Hot_Dog; end loop; end Remove_Five_Dogs;

begin for Index in 1..4 loop delay 0.9; Retail_Hot_Dogs.Stock_With_A_Hot_Dog; Retail_Hot_Dogs.Deliver_A_Hot_Dog; end loop;end Retail2;

-- Result of execution (With no changes)

-- Add a hot dog to the shelf, number = 1-- Add a hot dog to the shelf, number = 2-- Add a hot dog to the shelf, number = 3-- Add a hot dog to the shelf, number = 4-- Add a hot dog to the shelf, number = 5-- Remove a hot dog from the shelf-- Add a hot dog to the shelf, number = 5-- Remove a hot dog from the shelf-- Remove a hot dog from the shelf-- Remove a hot dog from the shelf-- Add a hot dog to the shelf, number = 3-- Remove a hot dog from the shelf-- Remove a hot dog from the shelf-- Add a hot dog to the shelf, number = 2-- Remove a hot dog from the shelf-- Remove a hot dog from the shelf-- Add a hot dog to the shelf, number = 1-- Remove a hot dog from the shelf

-- Result of execution (With line 29 changed so limit is 3)

-- Add a hot dog to the shelf, number = 1-- Add a hot dog to the shelf, number = 2-- Add a hot dog to the shelf, number = 3-- Remove a hot dog from the shelf-- Add a hot dog to the shelf, number = 3-- Remove a hot dog from the shelf-- Add a hot dog to the shelf, number = 3-- Remove a hot dog from the shelf-- Add a hot dog to the shelf, number = 3-- Remove a hot dog from the shelf-- Add a hot dog to the shelf, number = 3-- Remove a hot dog from the shelf-- Remove a hot dog from the shelf-- Add a hot dog to the shelf, number = 2-- Remove a hot dog from the shelf

Page 180: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Remove a hot dog from the shelf-- Add a hot dog to the shelf, number = 1-- Remove a hot dog from the shelf

-- Result of execution (With delays swapped in lines 49 and 57)

-- Add a hot dog to the shelf, number = 1-- Remove a hot dog from the shelf-- Add a hot dog to the shelf, number = 1-- Remove a hot dog from the shelf-- Add a hot dog to the shelf, number = 1-- Remove a hot dog from the shelf-- Add a hot dog to the shelf, number = 1-- Remove a hot dog from the shelf-- Add a hot dog to the shelf, number = 1-- Remove a hot dog from the shelf-- Add a hot dog to the shelf, number = 1-- Remove a hot dog from the shelf-- Add a hot dog to the shelf, number = 1-- Remove a hot dog from the shelf-- Add a hot dog to the shelf, number = 1-- Remove a hot dog from the shelf-- Add a hot dog to the shelf, number = 1-- Remove a hot dog from the shelf

Examine the program named e_c27_p5.ada for an example of a select statement with guards. The guards are used to guard the entry points of the select statement to prevent the kinds of silly things that happened in the last program. The task body Retail_Hot_Dogs has been modified in this program to include guards in lines 26 and 34 for the select statement in lines 25 through 39. A guard is simply a BOOLEAN condition that must be satisfied before that particular entry point can be accepted and its logic executed. The general form of the select statement with guards is given as;

select when <BOOLEAN condition> => accept ...; -- Complete entry point logic or when <BOOLEAN condition> => accept ...; -- Complete entry point logic or when <BOOLEAN condition> => accept ...; -- Complete entry point logic end select;

and there is no limit to the number of permissible selections, each being separated by the reserved word or. In fact, one or more of the selections can have no guard, in which case it is similar to having a guard which always evaluates to TRUE. When the select statement is encountered, each of the guards is evaluated for TRUE or FALSE, and those conditions that evaluate to TRUE are allowed to enter into the active wait state for an entry, while those that have guards evaluating to FALSE are not. Those with guards evaluating to FALSE are treated as if they didn't exist for this pass through the loop. Once the guards are evaluated upon entering the select statement, they are not reevaluated until the next time the select statement is encountered, but remain static.

LIMITING THE NUMBER OF HOT DOGS ON THE SHELF

In this program, when the select statement is entered in line 25, the guard at line 26 is evaluated and

Page 181: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

if the number of hot dogs on the shelf is less than 8, then the accept statement in line 27 is enabled and we are permitted to stock the shelf with one more hot dog. If the number of hot dogs is greater than zero, as the guard at line 34 tests for us, then the accept statement in line 35 is enabled and we are allowed to deliver a hot dog. Even though we may be allowed to either stock the shelf with a hot dog, or deliver a hot dog, we must wait until some other task requests us to do so before we actually do one of the operations. It should be clear to you that in this particular case we will always be permitted to do at least one of the operations, and in many cases both will be permitted. If none of the guards evaluate to TRUE, then none of the selections can be taken, and the program is therefore effectively deadlocked and the exception named Tasking_Error will be raised. You, the programmer, can trap this exception in much the same way that you can trap any other exception and handle it in your own manner, but the rules are a little different for tasking exceptions than for exceptions raised during sequential operation. We will cover tasking exceptions in detail later.

It should be pointed out that, even if a guard evaluates to FALSE, entries can be added to the entry queue and serviced during subsequent executions of the select statement when the guard may become TRUE. Because of this method of defining the entry queue, no calls to the entry are lost, and the operation is predictable.

This program contains four tasks, counting the main program, with one named Five_Dogs stocking the shelf very quickly with five hot dogs, because of the short delay, and another removing five hot dogs a little slower. The main program stocks and retrieves four hot dogs rather slowly due to the relatively long time delay built into the loop.

WATCH THE GUARDS DO THEIR JOB

When you run this program you will see very little action with the guards because of the selection of the guard limits. The five hot dogs are put on the shelf very quickly, but the upper limit of 8 is never reached, and there are always hot dogs on the shelf to supply the limited demands. In fact, as listed in the result of execution, there are never more than 5 on the shelf, and always more than zero. You should compile and execute the program to see if your compiler does the same thing as the one used for this execution.

Change line 26 so that the limit is 3, and recompile and execute the resulting program. In this case, you will very clearly see that the first guard prevents more than three hot dogs from being placed on the shelf. In effect it builds up the entry queue for the Stock_With_A_Hot_Dog entry point and requires the suppliers to wait for shelf space.

Reverse the delays in lines 46 and 54, as your next exercise, so that the hot dogs are consumed much faster than they are stocked so that the guard on the entry point named Deliver_A_Hot_Dog will be needed to protect the delivery of too many hot dogs. In this case, the queue to this entry point will build up a list of requests to be satisfied as hot dogs are delivered.

THIS PROGRAM IS MUCH BETTER

This program solved two of the three problems listed concerning the last program but we still must use the method of counting the required entry calls and providing the proper number of entries. As promised before, this problem will be remedied soon. Be sure you compile and execute this program three times, once unmodified, and twice with the suggested changes, then study the output to assure yourself that you understand it completely.

A PROTECTED AREA OF MEMORY

Example program ------> e_c27_p6.ada

Page 182: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Chapter 27 - Program 6

with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Protect is protected Animals is procedure Add_Some(Dogs_In : INTEGER; Cats_IN : INTEGER); procedure Subtract_Some(Dogs_In : INTEGER; Cats_In : INTEGER); procedure Get_Count(Dogs_Out : out INTEGER; Cats_Out : out INTEGER);private Dogs : INTEGER := 5; Cats : INTEGER := 3;end Animals;

protected body Animals is procedure Add_Some(Dogs_In : INTEGER; Cats_In : INTEGER) is begin Dogs := Dogs + Dogs_In; Cats := Cats + Cats_In; end Add_Some;

procedure Subtract_Some(Dogs_In : INTEGER; Cats_In : INTEGER) is begin Dogs := Dogs - Dogs_In; Cats := Cats - Cats_In; end Subtract_Some;

procedure Get_Count(Dogs_Out : out INTEGER; Cats_Out : out INTEGER) is begin Dogs_Out := Dogs; Cats_Out := Cats; end Get_Count;

end Animals;

task Farm_Animals; task City_Animals; task Catch_Animals;

task body Farm_Animals is begin for Index in 1..5 loop delay(0.3); Animals.Subtract_Some(3, 2); end loop; end Farm_Animals;

task body City_Animals is begin for Index in 1..4 loop delay(0.5); Animals.Subtract_Some(1, 2); end loop; end City_Animals;

task body Catch_Animals is

Page 183: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

begin for Index in 1..7 loop delay(0.6); Animals.Add_Some(7, 4); end loop; end Catch_Animals;

Number_Of_Dogs : INTEGER;Number_Of_Cats : INTEGER;

begin for Index in 1..12 loop Animals.Get_Count(Number_Of_Dogs, Number_Of_Cats); Put("Dog count = "); Put(Number_Of_Dogs, 4); Put(" Cat count = "); Put(Number_Of_Cats, 4); New_Line; delay 0.5; end loop;end Protect;

-- Result of execution

-- Dog count = 5 Cat count = 3-- Dog count = 1 Cat count = -1-- Dog count = 1 Cat count = -3-- Dog count = 4 Cat count = -3-- Dog count = 7 Cat count = -3-- Dog count = 14 Cat count = 1-- Dog count = 21 Cat count = 5-- Dog count = 21 Cat count = 5-- Dog count = 28 Cat count = 9-- Dog count = 35 Cat count = 13-- Dog count = 35 Cat count = 13-- Dog count = 35 Cat count = 13

The example program named e_c27_p6.ada gives a very simple example of data protection. If you have several tasks writing to the same record, it is conceivable that before one is finished writing, another task gets control and begins writing to the same record. This would result in corrupted data and is a major problem when writing programs with multiple tasks. The protected area in lines 8 through 39 defines three procedures that operate just like any other procedures we have worked with in this tutorial, with the exception that they are protected from multiple entry. It is impossible for more than one task to be executing code within these three procedures at once because that is their purpose. If more than one task requests a call into these three procedures, one will enter the procedure it called, and the others will be made to wait outside of the procedure they called until the first one leaves the procedure it called. This prevents data corruption.

The remainder of the program is trivial, being composed of three tasks plus the main program that work together to simply add and subtract animals from the common pool. Note that all data must be in the private part of the specification, none is allowed in the protected body of the protected code.

FUNCTIONS ARE A LITTLE DIFFERENT

The procedures are allowed to read or write to the private data, but a function is only allowed to have in mode parameters and it is only allowed to read the data in the private section. Since they are only allowed to read data, multiple tasks are permitted to read the private data at the same time, but not while another task is executing code in a procedure, since it may be writing to the internal data.

Page 184: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

This permits multiple calls to read which solves the classic problem of readers and writers.

The protected section has a lot more flexibility than we are covering here, but this will give you a good start in understanding what it is used for.

ONE MORE RESERVED WORD

The reserved word requeue is used within a protected block to call another subprogram on behalf of the calling program. The conditions may not yet exist for execution of the desired code, so it is requeue'd until the conditions are right. This is a very advanced technique that you can study on your own when you need it. It will probably be a long time before you reach the level of expertise needed to effectively use this technique.

PROGRAMMING EXERCISES

1. Move the end of the accept statement in e_c27_p1.ada to the line immediately after the accept statement itself to see that it is possible to eat the hot dog before it is made because the tasks are both running at the same time.(Solution)

-- Chapter 27 - Programming exercise 1with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure CH27_1 is

task Gourmet is entry Make_A_Hot_Dog; end Gourmet;

task body Gourmet is begin Put_Line("I am ready to make a hot dog for you"); for Index in 1..4 loop accept Make_A_Hot_Dog do null; end Make_A_Hot_Dog; delay 0.8; Put("Put hot dog in bun "); Put_Line("and add mustard"); end loop; Put_Line("I am out of hot dogs"); end Gourmet;

begin for Index in 1..4 loop Gourmet.Make_A_Hot_Dog; delay 0.1; Put_Line("Eat the resulting hot dog"); New_Line; end loop; Put_Line("I am not hungry any longer");end CH27_1;

-- Result of execution

-- I am ready to make a hot dog for you-- Eat the resulting hot dog---- Put hot dog in bun and add mustard-- Eat the resulting hot dog---- Put hot dog in bun and add mustard

Page 185: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Eat the resulting hot dog---- Put hot dog in bun and add mustard-- Eat the resulting hot dog---- I am not hungry any longer-- Put hot dog in bun and add mustard-- I am out of hot dogs

2. Add another task to e_c27_p5.ada that executes a loop 10 times with a 0.3 second delay that outputs the current number of hot dogs on the shelf.(Solution)

-- Chapter 27 - Programming exercise 2with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure CH27_2 is

Number_Of_Dogs : INTEGER := 0;

-- The main program adds and deletes -- hot dogs to/from the shelf.

task Five_Dogs; -- This task adds five hot dogs to stock

task Remove_Five_Dogs; -- This task deletes five from stock

task How_Many_Are_Left; -- This task lists the number on shelf

task Retail_Hot_Dogs is entry Stock_With_A_Hot_Dog; -- This adds a hot dog to stock entry Deliver_A_Hot_Dog; -- This deletes one from stock end Retail_Hot_Dogs;

task body Retail_Hot_Dogs is begin for Index in 1..18 loop select when Number_Of_Dogs < 8 => accept Stock_With_A_Hot_Dog do Number_Of_Dogs := Number_Of_Dogs + 1; Put("Add a hot dog to the shelf, number ="); Put(Number_Of_Dogs,3); New_Line; end Stock_With_A_Hot_Dog; or when Number_Of_Dogs > 0 => accept Deliver_A_Hot_Dog do Put_Line("Remove a hot dog from the shelf"); Number_Of_Dogs := Number_Of_Dogs - 1; end Deliver_A_Hot_Dog; end select; end loop; end Retail_Hot_Dogs;

task body Five_Dogs is begin for Index in 1..5 loop delay 0.1; Retail_Hot_Dogs.Stock_With_A_Hot_Dog;

Page 186: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

end loop; end Five_Dogs;

task body Remove_Five_Dogs is begin for Index in 1..5 loop delay 0.6; Retail_Hot_Dogs.Deliver_A_Hot_Dog; end loop; end Remove_Five_Dogs;

task body How_Many_Are_Left is begin for Index in 1..10 loop delay 0.3; Put("There are"); Put(Number_Of_Dogs,3); Put_Line(" hotdogs left on the shelf."); end loop; end How_Many_Are_Left;

begin for Index in 1..4 loop delay 0.9; Retail_Hot_Dogs.Stock_With_A_Hot_Dog; Retail_Hot_Dogs.Deliver_A_Hot_Dog; end loop;end CH27_2;

-- Result of execution (With no changes)

-- Add a hot dog to the shelf, number = 1-- Add a hot dog to the shelf, number = 2-- Add a hot dog to the shelf, number = 3-- There are 3 hotdogs left on the shelf.-- Add a hot dog to the shelf, number = 4-- Add a hot dog to the shelf, number = 5-- Remove a hot dog from the shelf-- There are 4 hotdogs left on the shelf.-- Add a hot dog to the shelf, number = 5-- Remove a hot dog from the shelf-- There are 4 hotdogs left on the shelf.-- Remove a hot dog from the shelf-- There are 3 hotdogs left on the shelf.-- There are 3 hotdogs left on the shelf.-- Remove a hot dog from the shelf-- Add a hot dog to the shelf, number = 3-- There are 3 hotdogs left on the shelf.-- Remove a hot dog from the shelf-- There are 2 hotdogs left on the shelf.-- Remove a hot dog from the shelf-- There are 1 hotdogs left on the shelf.-- There are 1 hotdogs left on the shelf.-- Add a hot dog to the shelf, number = 2-- Remove a hot dog from the shelf-- Remove a hot dog from the shelf-- There are 0 hotdogs left on the shelf.-- Add a hot dog to the shelf, number = 1-- Remove a hot dog from the shelf

Page 187: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

3. Using the package Ada.Calendar, output the elapsed time each time the new procedure defined in exercise 2 outputs the number of hot dogs on the shelf.(Solution)

-- Chapter 27 - Programming exercise 3with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Calendar;use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Calendar;

procedure CH27_3 is

package Fix_IO is new Ada.Text_IO.Fixed_IO(DAY_DURATION); use Fix_IO;

Number_Of_Dogs : INTEGER := 0;

-- The main program adds and deletes -- hot dogs to/from the shelf.

task Five_Dogs; -- This task adds five hot dogs to stock

task Remove_Five_Dogs; -- This task deletes five from stock

task How_Many_Are_Left; -- This task lists the number on shelf

task Retail_Hot_Dogs is entry Stock_With_A_Hot_Dog; -- This adds a hot dog to stock entry Deliver_A_Hot_Dog; -- This deletes one from stock end Retail_Hot_Dogs;

task body Retail_Hot_Dogs is begin for Index in 1..18 loop select when Number_Of_Dogs < 8 => accept Stock_With_A_Hot_Dog do Number_Of_Dogs := Number_Of_Dogs + 1; Put("Add a hot dog to the shelf, number ="); Put(Number_Of_Dogs,3); New_Line; end Stock_With_A_Hot_Dog; or when Number_Of_Dogs > 0 => accept Deliver_A_Hot_Dog do Put_Line("Remove a hot dog from the shelf"); Number_Of_Dogs := Number_Of_Dogs - 1; end Deliver_A_Hot_Dog; end select; end loop; end Retail_Hot_Dogs;

task body Five_Dogs is begin for Index in 1..5 loop delay 0.1; Retail_Hot_Dogs.Stock_With_A_Hot_Dog; end loop; end Five_Dogs;

task body Remove_Five_Dogs is begin for Index in 1..5 loop delay 0.6; Retail_Hot_Dogs.Deliver_A_Hot_Dog;

Page 188: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

end loop; end Remove_Five_Dogs;

task body How_Many_Are_Left is Time_And_Date : TIME; Start, Seconds : DAY_DURATION; Year, Month, Day : INTEGER; begin Time_And_Date := Clock; Split(Time_And_Date, Year, Month, Day, Start); for Index in 1..10 loop delay 0.3; Time_And_Date := Clock; Split(Time_And_Date, Year, Month, Day, Seconds); Put("Elapsed time ="); Put(Seconds - Start, 3, 3, 0); Put(", and there are"); Put(Number_Of_Dogs, 3); Put_Line(" hotdogs left on the shelf."); end loop; end How_Many_Are_Left;

begin for Index in 1..4 loop delay 0.9; Retail_Hot_Dogs.Stock_With_A_Hot_Dog; Retail_Hot_Dogs.Deliver_A_Hot_Dog; end loop;end CH27_3;

-- Result of execution (With no changes)

-- Add a hot dog to the shelf, number = 1-- Add a hot dog to the shelf, number = 2-- Add a hot dog to the shelf, number = 3-- Elapsed time = 0.330, and there are 3 hotdogs left on the shelf.-- Add a hot dog to the shelf, number = 4-- Add a hot dog to the shelf, number = 5-- Remove a hot dog from the shelf-- Elapsed time = 0.660, and there are 4 hotdogs left on the shelf.-- Add a hot dog to the shelf, number = 5-- Remove a hot dog from the shelf-- Elapsed time = 0.990, and there are 4 hotdogs left on the shelf.-- Remove a hot dog from the shelf-- Elapsed time = 1.320, and there are 3 hotdogs left on the shelf.-- Elapsed time = 1.650, and there are 3 hotdogs left on the shelf.-- Remove a hot dog from the shelf-- Add a hot dog to the shelf, number = 3-- Elapsed time = 1.980, and there are 3 hotdogs left on the shelf.-- Remove a hot dog from the shelf-- Elapsed time = 2.290, and there are 2 hotdogs left on the shelf.-- Remove a hot dog from the shelf-- Elapsed time = 2.620, and there are 1 hotdogs left on the shelf.-- Elapsed time = 2.950, and there are 1 hotdogs left on the shelf.-- Add a hot dog to the shelf, number = 2-- Remove a hot dog from the shelf-- Remove a hot dog from the shelf-- Elapsed time = 3.280, and there are 0 hotdogs left on the shelf.-- Add a hot dog to the shelf, number = 1

Page 189: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Remove a hot dog from the shelf

Page 190: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Ada Tutorial - Chapter 28

THE CONDITIONAL RENDEZVOUS

IMPATIENT PROGRAMMING

Many times in your life you wish to do something but you decide after trying for a short period of time, that it cannot be done, so you change your mind and do something else. This capability must be available in a computer program also, since a computer program is often used to simulate something in real life. This chapter will give examples of impatient programs, those that refuse to simply wait forever for a certain job to be done.

THE BASIC PROGRAM OUTLINE

Example program ------> e_c28_p1.ada

-- Chapter 28 - Program 1with Ada.Text_IO;use Ada.Text_IO;

procedure Meals1 is

HOURS : constant := 1; type PERSON is (BILL, JOHN);

package Enum_IO is new Ada.Text_IO.Enumeration_IO(PERSON); use Enum_IO;

task Bills_Day;

task Johns_Day;

task Restaurant is entry Eat_A_Meal(Customer : PERSON); end Restaurant;

task Burger_Boy is entry Eat_A_Meal(Customer : PERSON); end Burger_Boy;

task body Bills_Day is My_Name : PERSON := BILL; begin delay 1.0 * HOURS; Restaurant.Eat_A_Meal(My_Name); delay 1.0 * HOURS; Restaurant.Eat_A_Meal(My_Name); delay 1.0 * HOURS; Restaurant.Eat_A_Meal(My_Name); end Bills_Day;

task body Johns_Day is My_Name : PERSON := JOHN; begin delay 0.4 * HOURS; Restaurant.Eat_A_Meal(My_Name); delay 0.4 * HOURS; Restaurant.Eat_A_Meal(My_Name); delay 4.0 * HOURS; Restaurant.Eat_A_Meal(My_Name); end Johns_Day;

Page 191: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

task body Restaurant is begin loop accept Eat_A_Meal(Customer : PERSON) do Put(Customer); Put_Line(" is ordering at the restaurant"); delay 0.5 * HOURS; Put(Customer); Put_Line(" is eating at the restaurant"); delay 0.5 * HOURS; end Eat_A_Meal; end loop; end Restaurant;

task body Burger_Boy is begin loop accept Eat_A_Meal(Customer : PERSON) do Put(Customer); Put_Line(" is ordering at the Burger Boy"); delay 0.1 * HOURS; Put(Customer); Put_Line(" is eating at the Burger Boy"); delay 0.1 * HOURS; end Eat_A_Meal; end loop; end Burger_Boy;

begin null;end Meals1;

-- Result of execution

-- JOHN is ordering at the restaurant-- JOHN is eating at the restaurant-- BILL is ordering at the restaurant-- BILL is eating at the restaurant-- JOHN is ordering at the restaurant-- JOHN is eating at the restaurant-- BILL is ordering at the restaurant-- BILL is eating at the restaurant-- BILL is ordering at the restaurant-- BILL is eating at the restaurant-- JOHN is ordering at the restaurant-- JOHN is eating at the restaurant-- -- (The program will halt due to deadlock.)

Examine the file named e_c28_p1.ada for the program that will serve as the outline for the selective rendezvous even though it does not contain one. The program consists of four tasks, one each to describe Bill's day and John's day, and one each to describe eating a meal at a restaurant and at a fast food Burger Boy (hopefully not an actual restaurant). In this program, all of the delay times have been defined in terms of a constant named HOURS which actually causes a delay of one second per hour to speed up program execution to a reasonable level.

Eating a meal at the restaurant requires half an hour to be served and another half hour to consume

Page 192: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

the meal, whereas the Burger Boy requires only one tenth of an hour to be served and another tenth to consume the meal. The task named Bills_Day has him eating three meals only one hour apart, and the task named Johns_Day has him eating two meals in rapid succession then waiting four hours until he eats supper. Another interesting point is the fact that both eat every meal at the restaurant with no concern for how long they have to wait to be seated and eat. It is of no concern either to the Ada program at hand or the analysis of the problem, that one of the tasks is never called, because this is perfectly legal. Finally, both the restaurant and the Burger Boy can only serve one customer at a time, since this is the way the tasks were programmed. They can however, each serve a different customer at the same time.

EATING MEALS IS VERY SLOW

You will notice, by inspecting the delays, that John waits 0.4 hours and gets to the restaurant first, then takes a total of one hour to eat. Bill waits one hour, and goes to the restaurant, but must wait on the entry queue for 0.4 hours for John to finish eating, then takes another hour to eat. During this hour, John has returned and is waiting to be served again. They continue getting in each others way and finally each consumes three meals.

DEADLOCK OCCURS

The observant student will notice that the tasks named Bills_Day and Johns_Day each run through sequentially to completion and make no additional calls. The two named Restaurant and Burger_Boy however, each consist of an infinite loop and never actually complete. When the first two tasks execute to completion, the system will recognize that there are no tasks running that are capable of making calls to the waiting accept statements. The system will recognize this condition, and it will terminate the program due to deadlock after displaying a message that deadlock has occurred.

We still have not given an acceptable method of terminating our program gracefully, but we will later in this chapter. You should spend the time necessary to thoroughly understand this program since it will be the basis for the remaining example programs in this chapter. When you do understand it, compile and execute it to see if your compiler will recognize the deadlock condition.

THE CONDITIONAL RENDEZVOUS

Example program ------> e_c28_p2.ada

-- Chapter 28 - Program 2with Ada.Text_IO;use Ada.Text_IO;

procedure Meals2 is

HOURS : constant := 1; type PERSON is (BILL, JOHN);

package Enum_IO is new Ada.Text_IO.Enumeration_IO(PERSON); use Enum_IO;

task Bills_Day;

task Johns_Day;

task Restaurant is entry Eat_A_Meal(Customer : PERSON); end Restaurant;

task Burger_Boy is entry Eat_A_Meal(Customer : PERSON); end Burger_Boy;

Page 193: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

task body Bills_Day is My_Name : PERSON := BILL; begin delay 1.0 * HOURS; select Restaurant.Eat_A_Meal(My_Name); else Burger_Boy.Eat_A_Meal(My_Name); end select; delay 1.0 * HOURS; select Restaurant.Eat_A_Meal(My_Name); or delay 0.1 * HOURS; Burger_Boy.Eat_A_Meal(My_Name); end select; delay 1.0 * HOURS; Restaurant.Eat_A_Meal(My_Name); end Bills_Day;

task body Johns_Day is My_Name : PERSON := JOHN; begin delay 0.4 * HOURS; Restaurant.Eat_A_Meal(My_Name); delay 0.4 * HOURS; Restaurant.Eat_A_Meal(My_Name); delay 4.0 * HOURS; Restaurant.Eat_A_Meal(My_Name); end Johns_Day;

task body Restaurant is begin loop accept Eat_A_Meal(Customer : PERSON) do Put(Customer); Put_Line(" is ordering at the restaurant"); delay 0.5 * HOURS; Put(Customer); Put_Line(" is eating at the restaurant"); delay 0.5 * HOURS; end Eat_A_Meal; end loop; end Restaurant;

task body Burger_Boy is begin loop accept Eat_A_Meal(Customer : PERSON) do Put(Customer); Put_Line(" is ordering at the Burger Boy"); delay 0.1 * HOURS; Put(Customer); Put_Line(" is eating at the Burger Boy"); delay 0.1 * HOURS; end Eat_A_Meal; end loop; end Burger_Boy;

begin null;end Meals2;

Page 194: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Result of execution

-- JOHN is ordering at the restaurant-- JOHN is eating at the restaurant-- BILL is ordering at the Burger Boy-- BILL is eating at the Burger Boy-- JOHN is ordering at the restaurant-- BILL is ordering at the Burger Boy-- BILL is eating at the Burger Boy-- JOHN is eating at the restaurant-- BILL is ordering at the restaurant-- BILL is eating at the restaurant-- JOHN is ordering at the restaurant-- JOHN is eating at the restaurant---- (The program will halt due to deadlock.)

Examine the program named e_c28_p2.ada for our first example of a conditional rendezvous. This program is identical to the last one named e_c28_p1.ada except for the addition of a few statements in the task named Bills_Day. In this program Bill is in a bit of a hurry to eat his first two meals and puts in a few conditions to indicate this.

THE SELECTED RENDEZVOUS

When Bill starts his day, he is very impatient and will not wait at all for his first meal. If the restaurant is not ready to serve a meal immediately, he will go to the Burger Boy and have his breakfast. The statements in lines 29 through 33 replace the single statement of the last program to indicate this impatience. In Ada terms, if the task named Restaurant is not waiting at the Eat_A_Meal entry point, the call will not be made there, but a call will be made to the entry point named Eat_A_Meal in the Burger_Boy task. If the Burger_Boy task is not waiting at the entry point, then Bill will be required to wait until it is ready, when he will be served.

The reserved word else is used to indicate the selected rendezvous and it can follow as many or clauses as desired. The general form is;

select <entry call>; or >entry call>; else <entry call>; end select;

The else clause will be taken if none of the other entry points are waiting at their respective rendezvous points.

THE DELAYED RENDEZVOUS

Bill is not quite as hungry for his second meal so he is willing to wait for a short period at the restaurant, but if he is not served within one tenth of an hour, he will go to the Burger Boy for lunch. The single statement in lines 35 through 40 replaces the single statement in the previous program. In Ada terms, the task named Bills_Day will wait .1 hour for the Restaurant task to reach its entry point, after which it will call the entry point of Burger_Boy and wait no matter how long it takes to be served there.

As many selections as desired can be used, with delay statements in as many of the selections as

Page 195: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

needed. The general form is given by;

select delay <time>; <entry call>; or delay <time>; <entry call>; or delay <time>; <entry call>; end select;

An else clause cannot be used if any delay statements are used because the else clause will be executed immediately and any statement with a delay would never have a chance to time out. It would therefore never be executed and must be regarded as unexecutable code.

BILL'S STUBBORN RESOLUTION FOR SUPPER

Bill has decided that under no conditions will he eat supper at the Burger Boy. He will wait for as long as necessary in order to be served at the restaurant, and this is reflected in the single call in line 42 in exactly the same manner that was indicated in the previous program.

Take careful notice that both of these select statements are in the calling task, not the called task, but we will see shortly that the same sort of things are available in the called task.

Examination of the result of execution will reveal that Bill did eat both of his early meals at the Burger Boy. In addition, you will see that his second meal was ordered and eaten at the Burger Boy during the time that John was eating his meal at the Restaurant. This indicates that resources are being well used.

Be sure to compile and execute this program. Observe the result of execution to see that it does send Bill to the Burger Boy on occasion. Because we still have no graceful termination, deadlock occurs once again.

THE DELAYED ENTRY POINT

Example program ------> e_c28_p3.ada

-- Chapter 28 - Program 3with Ada.Text_IO;use Ada.Text_IO;

procedure Meals3 is

HOURS : constant := 1; type PERSON is (BILL, JOHN);

package Enum_IO is new Ada.Text_IO.Enumeration_IO(PERSON); use Enum_IO;

task Bills_Day;

task Johns_Day;

task Restaurant is entry Eat_A_Meal(Customer : PERSON); end Restaurant;

task Burger_Boy is entry Eat_A_Meal(Customer : PERSON); end Burger_Boy;

Page 196: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

task body Bills_Day is My_Name : PERSON := BILL; begin delay 1.0 * HOURS; select Restaurant.Eat_A_Meal(My_Name); else Burger_Boy.Eat_A_Meal(My_Name); end select; delay 1.0 * HOURS; select Restaurant.Eat_A_Meal(My_Name); or delay 0.1 * HOURS; Burger_Boy.Eat_A_Meal(My_Name); end select; delay 1.0 * HOURS; Restaurant.Eat_A_Meal(My_Name); end Bills_Day;

task body Johns_Day is My_Name : PERSON := JOHN; begin delay 0.4 * HOURS; Restaurant.Eat_A_Meal(My_Name); delay 0.4 * HOURS; Restaurant.Eat_A_Meal(My_Name); delay 4.0 * HOURS; Restaurant.Eat_A_Meal(My_Name); end Johns_Day;

task body Restaurant is begin loop select accept Eat_A_Meal(Customer : PERSON) do Put(Customer); Put_Line(" is ordering at the restaurant"); delay 0.5 * HOURS; Put(Customer); Put_Line(" is eating at the restaurant"); delay 0.5 * HOURS; end Eat_A_Meal; or delay 1.5 * HOURS; Put_Line("The restaurant is closed for the day"); exit; end select; end loop; end Restaurant;

task body Burger_Boy is begin loop select accept Eat_A_Meal(Customer : PERSON) do Put(Customer); Put_Line(" is ordering at the Burger Boy"); delay 0.1 * HOURS; Put(Customer); Put_Line(" is eating at the Burger Boy"); delay 0.1 * HOURS;

Page 197: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

end Eat_A_Meal; or delay 2.1 * HOURS; Put_Line("The Burger Boy is closed for the day"); exit; end select; end loop; end Burger_Boy;

begin null;end Meals3;

-- Result of execution

-- JOHN is ordering at the restaurant-- JOHN is eating at the restaurant-- BILL is ordering at the Burger Boy-- BILL is eating at the Burger Boy-- JOHN is ordering at the restaurant-- BILL is ordering at the Burger Boy-- BILL is eating at the Burger Boy-- JOHN is eating at the restaurant-- BILL is ordering at the restaurant-- BILL is eating at the restaurant-- The Burger Boy is closed for the day-- The restaurant is closed for the day

Examine the program named e_c28_p3.ada for yet another addition to our example of Bill and John satisfying their desire for nourishment. In this case, the restaurant will not remain open forever, nor will the Burger Boy, both choosing to close for the day if there are no customers for a period of time.

The select statement in lines 59 through 72 contains two alternatives, one to handle a customer as usual, and another with a delay of 1.5 hours followed by a restaurant closing statement. If there are no entry calls to the entry named Eat_A_Meal for 1.5 seconds (since HOURS is actually one second), then the second branch of the select statement will be executed, resulting in the display of a message and execution of an exit statement. You will recall that an exit will take you out of the most immediate loop and continue execution of statements from that point. In the case of the Restaurant task, the task is therefore completed and waits for the other tasks to complete.

The task named Burger_Boy has the same alternative entry point, except for a delay of 2.1 seconds. It also reaches its end statement and waits for the others to finish. Examination of the result of execution will reveal that both eating establishments actually did reach timeout and closed for the day leaving John to go hungry since he never ate his third meal. Since John did not eat his third meal of the day, some code was not executed and it was not reported as such. This seems to be an error, but it is actually not, because it is a tasking error that is not propagated to the main program. This is according to the definition of Ada and is explained more fully in the last two paragraphs of this chapter.

Be sure to compile and run this program and observe the early closing of the eating establishments.

ORDERLY TERMINATION OF TASKS

Example program ------> e_c28_p4.ada

-- Chapter 28 - Program 4

Page 198: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

with Ada.Text_IO;use Ada.Text_IO;

procedure Terminat is

HOURS : constant := 1; type PERSON is (BILL, JOHN);

package Enum_IO is new Ada.Text_IO.Enumeration_IO(PERSON); use Enum_IO;

task Bills_Day;

task Johns_Day;

task Restaurant is entry Eat_A_Meal(Customer : PERSON); end Restaurant;

task Burger_Boy is entry Eat_A_Meal(Customer : PERSON); end Burger_Boy;

task body Bills_Day is My_Name : PERSON := BILL; begin delay 1.0 * HOURS; Restaurant.Eat_A_Meal(My_Name); delay 1.0 * HOURS; Restaurant.Eat_A_Meal(My_Name); delay 1.0 * HOURS; Restaurant.Eat_A_Meal(My_Name); end Bills_Day;

task body Johns_Day is My_Name : PERSON := JOHN; begin delay 0.4 * HOURS; Restaurant.Eat_A_Meal(My_Name); delay 0.4 * HOURS; Restaurant.Eat_A_Meal(My_Name); delay 4.0 * HOURS; Restaurant.Eat_A_Meal(My_Name); end Johns_Day;

task body Restaurant is begin loop select accept Eat_A_Meal(Customer : PERSON) do Put(Customer); Put_Line(" is ordering at the restaurant"); delay 0.5 * HOURS; Put(Customer); Put_Line(" is eating at the restaurant"); delay 0.5 * HOURS; end Eat_A_Meal; or terminate; end select; end loop; end Restaurant;

Page 199: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

task body Burger_Boy is begin loop select accept Eat_A_Meal(Customer : PERSON) do Put(Customer); Put_Line(" is ordering at the Burger Boy"); delay 0.1 * HOURS; Put(Customer); Put_Line(" is eating at the Burger Boy"); delay 0.1 * HOURS; end Eat_A_Meal; or terminate; end select; end loop; end Burger_Boy;

begin null;end Terminat;

-- Result of execution

-- JOHN is ordering at the restaurant-- JOHN is eating at the restaurant-- BILL is ordering at the restaurant-- Bill is eating at the restaurant-- JOHN is ordering at the restaurant-- JOHN is eating at the restaurant-- BILL is ordering at the restaurant-- BILL is eating at the restaurant-- BILL is ordering at the restaurant-- BILL is eating at the restaurant-- JOHN is ordering at the restaurant-- JOHN is eating at the restaurant

Examine the program named e_c28_p4.ada for an example of orderly termination of tasks. This program is identical to the first program in this chapter named e_c28_p1.ada except for two minor changes. The entry point for the task named Restaurant has been put inside of a select statement in a manner similar to that done in e_c28_p2.ada or e_c28_p3.ada but a different or clause is used, one that contains the reserved word terminate. Each time through the loop, the entry point named Eat_A_Meal can be selected for execution and the task execution will wait until this entry point is called. While the program is waiting for the entry call, the branch of the select with the terminate allows the possibility of a termination to occur. However, the termination can only occur under the right conditions. The right conditions require that all other concurrent tasks, including the task in the main program, are either at their final end waiting to terminate, or have a similar terminate alternative to select.

The task named Burger_Boy has a similar terminate alternative. When the two calling tasks reach their final end, the two serving tasks have terminate alternatives available, so there is an orderly termination and the program returns to the operating system. Be sure to compile and execute this program, then study the result for conformance to this definition.

THE abort STATEMENT

Page 200: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

The abort statement will unconditionally abort any tasks with no regard to their present operating condition. It is extremely abrupt and does not provide for an orderly termination. It should therefore be used only in conditions of an emergency nature. Possibly the detection of loss of power would be such a circumstance. The tasks to be aborted are listed following the reserved word abort in an executable statement anywhere that it is legal to use an executable statement.

TWO TASK ATTRIBUTES

There are two useful task attributes that can be used to find the condition of any given task. They are CALLABLE, and TERMINATED, and each returns a BOOLEAN type indication of the current status of the task to which the attribute is appended. See the ARM for details on the use of these two attributes.

TASK EXCEPTIONS

The tasking exception named Tasking_Error is raised during essentially any communication error. Any error during task elaboration will raise this exception (this will be illustrated in a later example program). This exception is raised in the caller if the called task is aborted, and is raised in the originators of all calls on an entry queue of an aborted task.

If an exception is raised during a rendezvous, it is propagated into both tasks since each may need some form of recovery. If the exception is not handled internally by the task, it is not propagated to the calling program because to do so would be very disruptive. This means that a program that appears to be working correctly could actually have a very disruptive tasking error, much like the example program named e_c28_p3.ada in this chapter. Each significant task should therefore have an exception handler to guard against the occurrence of an unknown exception.

PROGRAMMING EXERCISE

1. Modify the program named e_c28_p2.ada such that it has an orderly termination upon completion.(Solution)

-- Chapter 28 - Programming exercise 1with Ada.Text_IO;use Ada.Text_IO;

procedure CH28_1 is

HOURS : constant := 1; type PERSON is (BILL, JOHN);

package Enum_IO is new Ada.Text_IO.Enumeration_IO(PERSON); use Enum_IO;

task Bills_Day;

task Johns_Day;

task Restaurant is entry Eat_A_Meal(Customer : PERSON); end Restaurant;

task Burger_Boy is entry Eat_A_Meal(Customer : PERSON); end Burger_Boy;

task body Bills_Day is My_Name : PERSON := BILL; begin delay 1.0 * HOURS; select Restaurant.Eat_A_Meal(My_Name);

Page 201: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

else Burger_Boy.Eat_A_Meal(My_Name); end select; delay 1.0 * HOURS; select Restaurant.Eat_A_Meal(My_Name); or delay 0.1 * HOURS; Burger_Boy.Eat_A_Meal(My_Name); end select; delay 1.0 * HOURS; Restaurant.Eat_A_Meal(My_Name); end Bills_Day;

task body Johns_Day is My_Name : PERSON := JOHN; begin delay 0.4 * HOURS; Restaurant.Eat_A_Meal(My_Name); delay 0.4 * HOURS; Restaurant.Eat_A_Meal(My_Name); delay 4.0 * HOURS; Restaurant.Eat_A_Meal(My_Name); end Johns_Day;

task body Restaurant is begin loop select accept Eat_A_Meal(Customer : PERSON) do Put(Customer); Put_Line(" is ordering at the restaurant"); delay 0.5 * HOURS; Put(Customer); Put_Line(" is eating at the restaurant"); delay 0.5 * HOURS; end Eat_A_Meal; or terminate; end select; end loop; end Restaurant;

task body Burger_Boy is begin loop select accept Eat_A_Meal(Customer : PERSON) do Put(Customer); Put_Line(" is ordering at the Burger Boy"); delay 0.1 * HOURS; Put(Customer); Put_Line(" is eating at the Burger Boy"); delay 0.1 * HOURS; end Eat_A_Meal; or terminate; end select; end loop; end Burger_Boy;

begin

Page 202: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

null;end CH28_1;

-- Result of execution

-- JOHN is ordering at the restaurant-- JOHN is eating at the restaurant-- BILL is ordering at the Burger Boy-- BILL is eating at the Burger Boy-- JOHN is ordering at the restaurant-- BILL is ordering at the Burger Boy-- BILL is eating at the Burger Boy-- JOHN is eating at the restaurant-- BILL is ordering at the restaurant-- BILL is eating at the restaurant-- JOHN is ordering at the restaurant-- JOHN is eating at the restaurant

2. Modify e_c28_p2.ada even further such that John eats every meal at the Burger Boy unless he has to wait there, in which case he eats at the restaurant.(Solution)

-- Chapter 28 - Programming exercise 2with Ada.Text_IO;use Ada.Text_IO;

procedure CH28_2 is

HOURS : constant := 1; type PERSON is (BILL, JOHN);

package Enum_IO is new Ada.Text_IO.Enumeration_IO(PERSON); use Enum_IO;

task Bills_Day;

task Johns_Day;

task Restaurant is entry Eat_A_Meal(Customer : PERSON); end Restaurant;

task Burger_Boy is entry Eat_A_Meal(Customer : PERSON); end Burger_Boy;

task body Bills_Day is My_Name : PERSON := BILL; begin delay 1.0 * HOURS; select Restaurant.Eat_A_Meal(My_Name); else Burger_Boy.Eat_A_Meal(My_Name); end select; delay 1.0 * HOURS; select Restaurant.Eat_A_Meal(My_Name);

Page 203: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

or delay 0.1 * HOURS; Burger_Boy.Eat_A_Meal(My_Name); end select; delay 1.0 * HOURS; Restaurant.Eat_A_Meal(My_Name); end Bills_Day;

procedure Johns_Choice(Name : PERSON) is begin select Burger_Boy.Eat_A_Meal(Name); else Restaurant.Eat_A_Meal(Name); end select; end;

task body Johns_Day is My_Name : PERSON := JOHN; begin delay 0.4 * HOURS; Johns_Choice(My_Name); delay 0.4 * HOURS; Johns_Choice(My_Name); delay 4.0 * HOURS; Johns_Choice(My_Name); end Johns_Day;

task body Restaurant is begin loop select accept Eat_A_Meal(Customer : PERSON) do Put(Customer); Put_Line(" is ordering at the restaurant"); delay 0.5 * HOURS; Put(Customer); Put_Line(" is eating at the restaurant"); delay 0.5 * HOURS; end Eat_A_Meal; or terminate; end select; end loop; end Restaurant;

task body Burger_Boy is begin loop select accept Eat_A_Meal(Customer : PERSON) do Put(Customer); Put_Line(" is ordering at the Burger Boy"); delay 0.1 * HOURS; Put(Customer); Put_Line(" is eating at the Burger Boy"); delay 0.1 * HOURS; end Eat_A_Meal; or terminate; end select; end loop;

Page 204: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

end Burger_Boy;

begin null;end CH28_2;

-- Result of execution

-- JOHN is ordering at the Burger Boy-- JOHN is eating at the Burger Boy-- BILL is ordering at the restaurant-- JOHN is ordering at the Burger Boy-- JOHN is eating at the Burger Boy-- BILL is eating at the restaurant-- BILL is ordering at the restaurant-- BILL is eating at the restaurant-- BILL is ordering at the restaurant-- JOHN is ordering at the Burger Boy-- JOHN is eating at the Burger Boy-- BILL is eating at the restaurant

Page 205: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Ada Tutorial - Chapter 29

ADDITIONAL TASKING TOPICS

We have covered most of the topics concerning tasking in the last three chapters, but there are a few more tasking themes that must be considered in order for you to use tasking to the fullest. The topics in this chapter are definitely advanced tasking topics and you may wish to ignore some of this material until you have gained considerable experience in the use of Ada. It would be worth your time to at least read through this material once to gain some exposure to it and to realize that these capabilities exist in the Ada programming language.

THE TASK TYPE

Example program ------> e_c29_p1.ada

-- Chapter 29 - Program 1with Ada.Text_IO;use Ada.Text_IO;

procedure TaskType is

task type SHORT_LINE isend SHORT_LINE;

task type LONG_LINE isend LONG_LINE;

Cow, Dog, Pig : SHORT_LINE;Elephant, Hippopotamus : LONG_LINE;

task body SHORT_LINE isbegin for Index in 1..4 loop delay 0.0; Put_Line("This is a short line"); end loop;end SHORT_LINE;

task body LONG_LINE isbegin for Index in 1..3 loop delay 0.0; Put_Line("This is a much longer line to be displayed"); end loop;end LONG_LINE;

begin Put_Line("This is an example of use of a task type");end TaskType;

-- Result of execution

-- This is an example of use of a task type-- This is a much longer line to be displayed-- This is a much longer line to be displayed-- This is a short line-- This is a short line-- This is a short line-- This is a much longer line to be displayed

Page 206: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- This is a much longer line to be displayed-- This is a short line-- This is a short line-- This is a short line-- This is a much longer line to be displayed-- This is a much longer line to be displayed-- This is a short line-- This is a short line-- This is a short line-- This is a short line-- This is a short line-- This is a short line

Examine the program named e_c29_p1.ada for an example of the task type in Ada. This will seem like a very strange construct, but if you spend time thinking about it, you will see that it does have a place in an Ada program, and it can be very useful.

In all previous programs with tasking, we have declared a task specification followed by a task body, but that is not the general case. It is actually a shorthand notation for declaring tasking and is somewhat similar to an anonymous type. The more general case of task definition is to declare a task type specification, then the task body, followed by a task that is of the declared type. In fact, the task is declared in much the same way we have been declaring variables in Ada, the task name followed by a colon and the task type. In the present program we declare the first task type specification in lines 7 and 8, and the corresponding task body in lines 16 through 22. The task itself is declared in line 13 where Cow is declared to be of task type SHORT_LINE. In fact, there are three tasks declared in this line, the other two being named Dog and Pig.

WHAT DOES IT REALLY MEAN?

If we declared a data type, then used the data type to declare three variables, you would have no trouble understanding what was happening, because you have been doing that all along in this tutorial. It may be a little more difficult for you to understand, but we are doing the same thing with the task, except that we are declaring a type of a section of code, then generating and executing three copies of that code. It is the same as if we declared a task specification named Cow that was identical to that given in lines 7 and 8, then a task body named Cow that was identical to that given in lines 16 through 22, and did exactly the same thing for the other two tasks named Dog and Pig. Instead of duplicating the code three times we declared a pattern for the task then told the system to run three copies of it.

The task itself is extremely simple, consisting of a loop with a zero delay, and a statement to output a message to the standard output device, the monitor. The tasks themselves are declared in line 13 before the task body is declared, but this should pose no difficulty for you since we have used partial declaration of subprograms and types before.

A fine point should be mentioned here. A task type is always of limited private type, which means you can do no operations on it. Assignment and comparisons are not available with a limited private type, and it makes sense to prevent these operations with tasks.

THERE ARE TWO TASK TYPES DECLARED

In addition to the task type named SHORT_LINE, there is another named LONG_LINE similar to the first, which outputs only three lines until it completes, but outputs a much longer line of text. Two copies of this type are declared and executed.

The end result of all of this is the generation and execution of five tasks all running in parallel with no rendezvous between them except for the zero time delay statements which we discussed in an earlier chapter.

Page 207: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

WHEN DO THEY RUN?

We discussed in an earlier chapter how each of the tasks got started running and this is no different. When all five tasks have been completely elaborated and are all waiting at their respective begin statements, and when the main program arrives at its begin statement, then all of the tasks begin to execute. Remember that there are six tasks counting the main program as a task. Likewise, when all of the tasks, including the main program, reach their end points, an orderly termination is effected and the program ceases to execute, returning control to the operating system.

Compile and run this program and you will see that all five tasks do indeed run as described above. Add a few additional tasks in lines 13 and 14 to see how easy it is to add additional tasks once the task type is completed. It would be well worth your time to study this program until you understand this material, because it will be used in the next few example programs.

A TASK AS PART OF A RECORD

It will not be illustrated here, but it is possible to declare a task type variable as part of a record. Such a record can be used to declare a task variable along with a few other variables for use by the task.

AN ARRAY OF TASKS

Example program ------> e_c29_p2.ada

-- Chapter 29 - Program 2with Ada.Text_IO;use Ada.Text_IO;

procedure TaskArry is

task type SHORT_LINE isend SHORT_LINE;

task type LONG_LINE isend LONG_LINE;

Cow : array (1..4) of SHORT_LINE;Elephant, Hippopotamus : LONG_LINE;

task body SHORT_LINE isbegin for Index in 1..4 loop delay 0.0; Put_Line("This is a short line"); end loop;end SHORT_LINE;

task body LONG_LINE isbegin for Index in 1..3 loop delay 0.0; Put_Line("This is a much longer line to be displayed"); end loop;end LONG_LINE;

begin Put_Line("This is an example of use of a task type array");end TaskArry;

Page 208: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Result of execution

-- This is an example of use of a task type-- This is a much longer line to be displayed-- This is a much longer line to be displayed-- This is a short line-- This is a short line-- This is a short line-- This is a short line-- This is a much longer line to be displayed-- This is a much longer line to be displayed-- This is a short line-- This is a short line-- This is a short line-- This is a short line-- This is a much longer line to be displayed-- This is a much longer line to be displayed-- This is a short line-- This is a short line-- This is a short line-- This is a short line-- This is a short line-- This is a short line-- This is a short line-- This is a short line

Examine the program named e_c29_p2.ada for an example of an array of task types. This program is identical to the last except for the addition of an array of tasks in line 13. The task name Cow is declared to be an array of tasks named Cow(1), Cow(2), ... Cow(4), so there are four tasks of this type running concurrently when execution begins. These four are in addition to the two of type LONG_LINE as in the previous program. Nothing more needs to be said about this program except for you to compile and execute it to see that you really do get all six tasks running in parallel with the task in the main program. There are actually seven tasks running in parallel. It would be a snap to increase the Cow array to 6 tasks and see nine running, the six in the array, the two which are the other type, and the main program.

A TASK ACCESS TYPE

Example program ------> e_c29_p3.ada

-- Chapter 29 - Program 3with Ada.Text_IO;use Ada.Text_IO;

procedure TaskAces is

task type SHORT_LINE isend SHORT_LINE;

task type LONG_LINE isend LONG_LINE;

type LONG_POINT is access LONG_LINE;

Cow, Dog, Pig : SHORT_LINE;Elephant, Hippopotamus : LONG_POINT;

task body SHORT_LINE isbegin for Index in 1..4 loop

Page 209: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

delay 0.0; Put_Line("This is a short line"); end loop;end SHORT_LINE;

task body LONG_LINE isbegin for Index in 1..3 loop delay 0.0; Put_Line("This is a much longer line to be displayed"); end loop;end LONG_LINE;

begin

Put_Line("This is an example of use of a task type"); Elephant := new LONG_LINE; Hippopotamus := new LONG_LINE;

end TaskAces;

-- Result of execution

-- This is an example of use of a task type-- This is a short line-- This is a short line-- This is a short line-- This is a short line-- This is a short line-- This is a short line-- This is a much longer line to be displayed-- This is a short line-- This is a short line-- This is a short line-- This is a much longer line to be displayed-- This is a short line-- This is a short line-- This is a short line-- This is a much longer line to be displayed-- This is a much longer line to be displayed-- This is a much longer line to be displayed-- This is a much longer line to be displayed

Examine the program named e_c29_p3.ada for an example of an access type which can be used to access a task. Once we have the ability to access a task through an access variable, we can also dynamically allocate a task and execute it. We will illustrate this operation in the present example program.

This program in nearly identical to the last two except that the variables named Elephant and Hippopotamus are not task type variables, but task access type variables because we declared the type LONG_POINT as an access variable in line thirteen.

When we execute this program, the three statically declared tasks begin executing when the main program does, but the two access type variables do nothing because they are simply access types that access nothing yet. The main program outputs a line of text to the monitor, then dynamically allocates a copy of the task type LONG_LINE in line 37 and the new task begins executing. Another task is allocated and begins execution in line 38. All five tasks will run to completion and

Page 210: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

there will be an orderly termination.

In this case, the main program is a parent task to the two dynamically allocated tasks and they are referred to as children tasks of the main program.

WHEN DO THEY BEGIN EXECUTION?

The biggest difference between this program and the previous two, involves when the respective tasks begin execution. All of the statically declared tasks begin execution when the main program begins, but the dynamically allocated tasks begin execution when they are allocated. This can be clearly seen by studying the result of execution of each of the three programs. In the present program, the first output of each of the two allocated tasks is delayed considerably behind the first output of the statically declared tasks.

Be sure to compile and execute this program and compare the output of your compiler with that generated by the author's Ada compiler. It would be a simple matter for you to add a few more access variables and allocate additional tasks just to gain the experience.

A VERY INEFFICIENT EXAMPLE OF TASKING

Example program ------> e_c29_p4.ada

-- Chapter 29 - Program 4with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Parallel is

DAYS : constant := 7;EMPLOYEES : constant := 11;

type WORKED_ARRAY is array (1..EMPLOYEES, 1..DAYS) of INTEGER;type TOTAL_ARRAY is array (1..EMPLOYEES) of INTEGER;

Hours_Worked : WORKED_ARRAY;Weekly_Totals : TOTAL_ARRAY;Result : INTEGER;

task type SUMMING_TASK_TYPE is entry Start_Sums(E_No : in INTEGER); entry Return_Sum(Result : out INTEGER);end SUMMING_TASK_TYPE;

Adding_Task : array(1..EMPLOYEES) of SUMMING_TASK_TYPE;

task body SUMMING_TASK_TYPE isTotal : INTEGER := 0;Local_E_No : INTEGER;begin accept Start_Sums(E_No : in INTEGER) do Local_E_No := E_No; end Start_Sums;

for Index in 1..Days loop Total := Total + Hours_Worked(Local_E_No, Index); end loop;

accept Return_Sum(Result : out INTEGER) do Result := Total; end Return_Sum;end SUMMING_TASK_TYPE;

begin

Page 211: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

for Emp_Number in 1..EMPLOYEES loop for Day in 1..DAYS loop Hours_Worked(Emp_Number, Day) := 8; end loop; end loop; Hours_Worked(2, 5) := 3; Hours_Worked(3, 5) := 0;

Put_Line("The Hours_Worked array is filled");

-- Start all parallel additions for Emp_Number in 1..EMPLOYEES loop Adding_Task(Emp_Number).Start_Sums(Emp_Number); end loop;

-- Get the results back for Emp_Number in 1..EMPLOYEES loop Adding_Task(Emp_Number).Return_Sum(Result); Weekly_Totals(Emp_Number) := Result; end loop;

for Emp_Number in 1..EMPLOYEES loop Put("Employee number"); Put(Emp_Number, 3); Put(" worked"); Put(Weekly_Totals(Emp_Number), 3); Put_Line(" hours."); end loop;

end Parallel;

-- Result of execution

-- The Hours_Worked array is filled-- Employee number 1 worked 56 hours-- Employee number 2 worked 51 hours-- Employee number 3 worked 48 hours-- Employee number 4 worked 56 hours-- Employee number 5 worked 56 hours-- Employee number 6 worked 56 hours-- Employee number 7 worked 56 hours-- Employee number 8 worked 56 hours-- Employee number 9 worked 56 hours-- Employee number 10 worked 56 hours-- Employee number 11 worked 56 hours

The example program named e_c29_p4.ada is an example of a very poor way to solve this particular problem but is a meaningful example of a tasking solution to a problem. In this program we will declare eleven parallel tasks, start all eleven running, then retrieve the results of the eleven tasks.

We begin by declaring several types and variables, then declare a task specification in lines 17 through 20 with two entry points. Because the constant EMPLOYEES is declared as having the value 11, we declare 11 tasks in line 22, named Adding_Task(1), Adding_Task(2), ... Adding_Task(11), each of which will begin running when we begin execution of the main program. The task body, given in lines 24 through 39, consists of a local variable, and two accept

Page 212: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

statements, with a few calculations between them.

The main program is the most interesting part of this program, but we need to get through the initialization code before we come to the interesting part. The initialization code in lines 42 through 48, assigns values to the big array declared previously including a couple of funny data points which will show up in the results. Finally, we display a message that the array is filled.

START ALL TASKS RUNNING

The loop in lines 53 through 55 calls the eleven tasks at their first entry point and begins them executing so that they all are executing their respective for loops. If you had a computer with a large number of actual processors, each task could be assigned to a separate processor and all calculations could be done concurrently. In the present case of adding seven numbers, the calculations are trivial, but if each task required the performance of several million floating point operations, the time saved through the efficient use of parallel hardware could be very significant. This program serves to illustrate the technique that could be used and is the most practical illustration of tasking to be demonstrated in this course.

ALL TASKS ARE NOW COMPLETE

The loop in lines 58 through 61 once again calls all eleven tasks one at a time at their final entry point where it requests that the result of the summation be returned. All results are stored in the Weekly_Totals array, and the results are then displayed along with appropriate text. You will notice, when you examine the result of execution, that employees numbered 2 and 3 have the funny data reflected in their totals.

The point of interest you should have gleaned from this program is that all eleven tasks were operating in parallel, with the main program being a twelfth task. The logic of the main program requires concurrent tasks as opposed to eleven calls to a single subprogram. Keep in mind that this would actually be a very poor way to program this particular problem, but was included as an illustration.

Be sure to compile and execute this program on your system to verify that the results are the same. After it does compile and execute correctly, change the number of employees to see how many tasks can be operated in parallel with your system. You will not be limited by some arbitrary upper limit on the number of allowable tasks imposed by your particular compiler, but by the amount of memory required for each task.

PRIORITY OF TASKS

Example program ------> e_c29_p5.ada

-- Chapter 29 - Program 5with Ada.Text_IO;use Ada.Text_IO;

procedure Priority is

task type SHORT_LINE is pragma PRIORITY (5);end SHORT_LINE;

task type LONG_LINE is pragma PRIORITY (1);end LONG_LINE;

pragma PRIORITY (3); -- This is the priority for the main program

Cow, Dog, Pig : SHORT_LINE;Elephant, Hippopotamus : LONG_LINE;

Page 213: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

task body SHORT_LINE isbegin for Index in 1..4 loop delay 0.0; Put_Line("This is a short line"); end loop;end SHORT_LINE;

task body LONG_LINE isbegin for Index in 1..3 loop delay 0.0; Put_Line("This is a much longer line to be displayed"); end loop;end LONG_LINE;

begin Put_Line("This is an example of use of a task type");end Priority;

-- Result of execution

-- This is a short line-- This is a short line-- This is a short line-- This is a short line-- This is a short line-- This is a short line-- This is a short line-- This is a short line-- This is a short line-- This is a short line-- This is a short line-- This is a short line-- This is an example of use of a task type-- This is a much longer line to be displayed-- This is a much longer line to be displayed-- This is a much longer line to be displayed-- This is a much longer line to be displayed-- This is a much longer line to be displayed-- This is a much longer line to be displayed

The example program named e_c29_p5.ada contains an example of establishing priority within tasks. The priority of tasks is indicated by the use of the pragma named PRIORITY as illustrated in the task specifications and the declaration part of the main program. An integer class variable is included in the parentheses with the larger numbers indicating the higher priority or the most urgent tasks. The range of allowable priorities are defined for your individual compiler in the System specification package definition as a subrange of INTEGER. According to the ARM, a range of 0..0 is legal. If your compiler only supports a single value for the priority, you will have to change the priorities in this program to the single value and you will, in effect, have no priority.

Compile and execute this program, then change the priorities and see what order of execution you can achieve. It is important to realize that the ARM does not absolutely define how priorities will be handled, but it only gives a rather vague definition. Priorities should be used with care in a production program.

Page 214: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

A FAMILY OF ENTRIES

Example program ------> e_c29_p6.ada

-- Chapter 29 - Program 6with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Family is

type SPEED is (FAST, MEDIUM, SLOW);

task Selector is entry Answer_Query(SPEED)(Counter : INTEGER);end Selector;

procedure Output_Value(Counter : INTEGER) isbegin Put(Counter, 3); New_Line;end Output_Value;

task body Selector isbegin loop select accept Answer_Query(FAST)(Counter : INTEGER) do Put("FAST Query made"); Output_Value(Counter); end; or when Answer_Query(FAST)'COUNT = 0 => accept Answer_Query(MEDIUM)(Counter : INTEGER) do Put("MEDIUM Query made"); Output_Value(Counter); end; or when Answer_Query(FAST)'COUNT = 0 and Answer_Query(MEDIUM)'COUNT = 0 => accept Answer_Query(SLOW)(Counter : INTEGER) do Put("SLOW Query made"); Output_Value(Counter); end; or terminate; end select; end loop;end Selector;

begin

Put_Line("Begin the main program"); Selector.Answer_Query(FAST)(1); Selector.Answer_Query(FAST)(2); Selector.Answer_Query(SLOW)(3); Selector.Answer_Query(MEDIUM)(4); Selector.Answer_Query(SLOW)(5); Selector.Answer_Query(MEDIUM)(6); Selector.Answer_Query(FAST)(7); Selector.Answer_Query(FAST)(8); Put_Line("End of the main program");

end Family;

Page 215: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Result of Execution

-- Begin the main program-- FAST Query made 1-- FAST Query made 2-- SLOW Query made 3-- MEDIUM Query made 4-- SLOW Query made 5-- MEDIUM Query made 6-- FAST Query made 7-- FAST Query made 8-- End of main program

The example program named e_c29_p6.ada illustrates one more construct that can be used with the tasking rendezvous to achieve a user defined priority structure. In this case, there are three entry points which are very similar but have only a small difference between them. An enumerated variable is declared and used as the discriminator between the three entries. The only real advantage to using a family of entries is that they all use the same name and therefore add to the clarity of the resulting program.

The use of the family of entries is illustrated here and should be very easy for you to understand. When you do, compile and execute this program and compare your output with that given in the result of execution section.

PROGRAMMING EXERCISES

1. Increase the size of the array in e_c29_p2.ada to see how big you can make this array before the program no longer fits in memory. This will give you an idea of how many tasks can be run at the same time with your system.(Solution)

-- Chapter 29 - Programming exercise 1with Ada.Text_IO;use Ada.Text_IO;

procedure CH29_1 is

task type SHORT_LINE isend SHORT_LINE;

task type LONG_LINE isend LONG_LINE;

Cow : array (1..17) of SHORT_LINE;Elephant, Hippopotamus : LONG_LINE;

task body SHORT_LINE isbegin for Index in 1..4 loop delay 0.0; Put_Line("This is a short line"); end loop;end SHORT_LINE;

task body LONG_LINE isbegin for Index in 1..3 loop delay 0.0;

Page 216: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Put_Line("This is a much longer line to be displayed"); end loop;end LONG_LINE;

begin Put_Line("This is an example of use of a task type array");end CH29_1;

-- Result of execution

-- This is an example of use of a task type-- The output is similar to the program TASKARRY.ADA but there-- is a lot more of it.

-- Note that 17 was the largest number that could be used for-- the number of tasks and still execute properly. When the-- number 18 is used, a storage_error exception is generated.-- This is for one compiler, and the number could vary over a-- rather large range for different compilers.

2. Add another access variable to e_c29_p3.ada and initialize it as a part of its declaration. When will this task begin execution?(Solution)

-- Chapter 29 - Programming example 2with Ada.Text_IO;use Ada.Text_IO;

procedure CH29_2 is

task type SHORT_LINE isend SHORT_LINE;

task type LONG_LINE isend LONG_LINE;

type LONG_POINT is access LONG_LINE;

Cow, Dog, Pig : SHORT_LINE;Elephant, Hippopotamus : LONG_POINT;Horse : LONG_POINT := new LONG_LINE;

task body SHORT_LINE isbegin for Index in 1..4 loop delay 0.0; Put_Line("This is a short line"); end loop;end SHORT_LINE;

task body LONG_LINE isbegin for Index in 1..3 loop delay 0.0; Put_Line("This is a much longer line to be displayed"); end loop;end LONG_LINE;

Page 217: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

begin

Put_Line("This is an example of use of a task type"); Elephant := new LONG_LINE; Hippopotamus := new LONG_LINE;

end CH29_2;

-- The task named Horse will begin execution at the same time-- that the main body of the program begins execution.

-- Exception never handled - program_error

-- Result of execution

-- This is an example of use of a task type-- This is a short line-- This is a short line-- This is a short line-- This is a short line-- This is a short line-- This is a short line-- This is a much longer line to be displayed-- This is a short line-- This is a short line-- This is a short line-- This is a much longer line to be displayed-- This is a short line-- This is a short line-- This is a short line-- This is a much longer line to be displayed-- This is a much longer line to be displayed-- This is a much longer line to be displayed-- This is a much longer line to be displayed

Page 218: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Ada Tutorial - Chapter 30

GENERIC SUBPROGRAMS

GENERIC UNITS ADD TO ADA'S FLEXIBILITY

The concept of packages along with generic units gives Ada the capability to be used for general purpose software components. Utility routines can be written once and used in several programs eliminating the need to rewrite and debug the utility again and again. This ability would be greatly diminished without the generic capability of Ada.

WHAT IS A GENERIC UNIT?

Example program ------> e_c30_p1.ada

-- Chapter 30 - Program 1 generic type ITEM is range <>; -- integer class only procedure Exchange_Data(X,Y : in out ITEM);

procedure Exchange_Data(X,Y : in out ITEM) is Temp : ITEM; begin Temp := X; X := Y; Y := Temp; end Exchange_Data;

-- This is the beginning of the main program which uses the generic-- procedure defined above.with Exchange_Data;with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure SwapSome is

type MY_INT is new INTEGER range 1..128;

procedure SwapInt is new Exchange_Data(INTEGER); procedure SwapNew is new Exchange_Data(MY_INT);

Index1 : INTEGER := 17; Index2 : INTEGER := 33; Limit1 : MY_INT := 3; Limit2 : MY_INT := 7;

begin

SwapInt(Index1, Index2); SwapNew(Limit1, Limit2);

Put(Index1, 5); Put(Index2, 5); New_Line; Put(INTEGER(Limit1), 5); Put(INTEGER(Limit2), 5); New_Line(2);

SwapInt(Index1, INTEGER(Limit1)); SwapNew(MY_INT(Index2), Limit2);

Page 219: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Put(Index1, 5); Put(Index2, 5); New_Line; Put(INTEGER(Limit1), 5); Put(INTEGER(Limit2), 5); New_Line(2);

end SwapSome;

-- Result of Execution

-- 33 17-- 7 3-- -- 7 3-- 33 17

A generic unit is a template for a subprogram or a package, but it cannot be executed directly. A copy of the generic unit can be instantiated, and the resulting unit can be executed just as any other subprogram or package. As with all other topics in this course, the best way to learn how to use this new technique is through use of an example, so examine the program named e_c30_p1.ada.

A SIMPLE GENERIC PROCEDURE

The generic specification is given in lines 2 through 4 and the generic body is given in lines 6 through 12. A careful inspection of this procedure will reveal that there is no actual type defined for the type listed as type ITEM. The purpose of using a generic procedure is to allow you to use the procedure for any type you desire, within reasonable limits, without being forced to rewrite the procedure for each specific type. In order to use this procedure in a program, we will supply a type which will be substituted in the procedure each place where the type ITEM appears when we instantiate a copy of the procedure.

The reserved word generic is used to mark the beginning of a generic unit, which may be a package, a procedure, or a function. Between the reserved words, in this case generic and procedure, we include a list of formal generic parameters which define the optional types, variables, and other entities which will be used in the body of the procedure. In this case there is only one formal generic parameter named ITEM, and it is constrained to be any type of the integer class of types. An explanation will be given soon to define why the type of ITEM is constrained to be of the integer class of types. For the time being, simply accept the statement as true.

The procedure specification in line 4 and the procedure body in lines 6 through 12 are no different than any of the other procedures we have used throughout this tutorial, except for the fact that the type named ITEM is undefined throughout the procedure. Since the type ITEM is undefined, the procedure is unusable in its present form.

HOW DO WE USE THE GENERIC PROCEDURE?

In order to use the generic procedure, we must first tell the system to get a copy of it which we do in line 18 using the with clause.

Referring to the main program, we declare a derived type of the integer class named MY_INT in line 24. In line 26 we declare a procedure named SwapInt and since we are using the reserved word new after the declaration, it is defining an instantiation of the generic package named Exchange_Data for use with type INTEGER. You will recall that instantiating the procedure means we create an instance of the generic procedure, and in this case we are defining a procedure

Page 220: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

that can swap INTEGER types of variables.

The type INTEGER is used in the resulting executable procedure each time the generic word ITEM is used in the original generic procedure. The result would be exactly the same as making a copy of the generic procedure, changing its name to SwapInt, then substituting the type INTEGER for each appearance of the word ITEM. A call to SwapInt with any two INTEGER type variables will result in exchanging their values.

WHAT GOOD DID THIS DO US?

Admittedly, we could have simply defined the procedure SwapInt in the first place and everything would have been just fine. The real benefit comes from the next line of the program where we instantiate another copy of the generic procedure named Exchange_Data, name it SwapNew, and tell the system to use the type MY_INT as the replacement for the formal generic type named ITEM. Line 27 is therefore equivalent to writing out the generic procedure once again for the new type we declared earlier.

If we had a large number of different types of the integer class of variables, we could instantiate a copy of this procedure for each with a single line for each, so the benefit would be very significant.

REUSABLE SOFTWARE

Once this procedure is written and debugged, it can be used in any number of programs because it does not have to be modified for each new type we make up. Different programmers can use the procedure in different packages once it is tested thoroughly, so each programmer does not have to write it anew. There is a new industry developing in the software community. This industry specializes in writing reusable software components in Ada that are written with the flexibility afforded by the generic units.

The remainder of the program should pose no problem for your understanding, so you will be left on your own to study it, then compile and execute it.

A FEW NOTES ABOUT GENERIC UNITS

This program could be split into two separate files with the first including the generic procedure in lines 1 through 12, and the second including the using program in lines 16 through 56. The files can be compiled separately, and once the generic procedure has been compiled, it never needs to be compiled again, but can be included in the Ada library in compiled form. The generic procedure and the main program were combined here for the sake of convenience.

ANOTHER NOTE ON COMPILATION UNITS

In some cases, the generic part can be split once again into two separate files, the first being the generic specification in lines 2 through 4 and the other being the procedure body in lines 6 through 12. If they are split in this manner, the order of compilation must be maintained so that the type checking can be done as described earlier in this tutorial. The Ada definition allows a compiler writer to require the generic specification and body be included in the same file for his convenience, so it depends on the particular compiler you are using to define whether or not you can split the generic procedure into two parts.

Of course, with such a simple procedure, it is difficult to grasp the magnitude of the benefits of this new Ada construct, but in a real programming situation, many cases of reusability will become apparent and the benefits will be appreciated.

ADDITIONAL FORMAL GENERIC TYPES

Example program ------> e_c30_p2.ada

-- Chapter 30 - Program 2 generic

Page 221: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

type ITEM is private; procedure Exchange_Data(X, Y : in out ITEM);

procedure Exchange_Data(X, Y : in out ITEM) is Temp : ITEM; begin Temp := X; X := Y; Y := Temp; end Exchange_Data;

generic type ANOTHER_ITEM is range <>; -- Integer class only function Average(X, Y : ANOTHER_ITEM) return ANOTHER_ITEM;

function Average(X, Y : ANOTHER_ITEM) return ANOTHER_ITEM is Temporary : ANOTHER_ITEM; begin Temporary := (X + Y) / 2; return Temporary; end Average;

-- This is the beginning of the main program which uses the generic-- procedure and function defined above.

with Exchange_Data, Average;with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;

procedure SwapMore is

type NEW_TYPE is new INTEGER range 122..12345;

type MY_RECORD is record My_Grade : INTEGER range 1..128; My_Class : CHARACTER; end record;

procedure Swap is new Exchange_Data(INTEGER); procedure Swap is new Exchange_Data(MY_RECORD); procedure Swap is new Exchange_Data(FLOAT); procedure Swap is new Exchange_Data(NEW_TYPE); procedure Swap is new Exchange_Data(CHARACTER); procedure Trade is new Exchange_Data(CHARACTER); procedure Exchange is new Exchange_Data(CHARACTER); procedure Puppy is new Exchange_Data(CHARACTER);

function Mean is new Average(INTEGER); function Mean is new Average(NEW_TYPE); function Swap is new Average(INTEGER); -- This is dumb to do, -- but it is legal.

Index1 : INTEGER := 117; Index2 : INTEGER := 123; Data1 : MY_RECORD := (15, 'A');

Page 222: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Data2 : MY_RECORD := (91, 'B'); Real1 : FLOAT := 3.14; Real2 : FLOAT := 77.02; Value1 : NEW_TYPE := 222; Value2 : NEW_TYPE := 345;

begin

Put(Real1, 4, 2, 0); Put(Real2, 4, 2, 0); New_Line; Swap(Real1, Real2); Put(Real1, 4, 2, 0); Put(Real2, 4, 2, 0); New_Line(2);

Put(Index1, 6); Put(Index2, 6); New_Line;

Swap(Index1, Index2); Swap(Data1, Data2);

Put(Index1, 6); Put(Index2, 6); New_Line;

-- Now to exercise some of the functions

Index1 := Mean(Index2, 16); Value1 := Mean(Value2, Value2 + 132); Index1 := Swap(Index1, Index2 + 12); -- This actually gets the -- mean of the inputs.

end SwapMore;

-- Result of Execution

-- 3.14 77.02-- 77.02 3.14---- 117 123-- 123 117

Examine the program named e_c30_p2.ada for two additional examples of generic subprograms. You will notice that the first is a generic procedure and the second is a generic function, indicating to you that either form of subprogram can be written as a generic unit. The first subprogram is nearly identical to the one in the last example program, the only difference being in the declaration of the formal generic type in line 3 which we will discuss shortly. The second subprogram, the function in lines 16 through 25, will be simple for you to comprehend if you substitute, in your mind, the type INTEGER for each occurrence of the type ANOTHER_ITEM. In fact, it averages the two values given to it and returns the result.

WHY THE DIFFERENT TYPES?

In line 3, we declare the type ITEM as private, and in the function named Average we declare the type ANOTHER_ITEM as a type of the integer class of types (we will show you how shortly, just

Page 223: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

be a little patient). As with so many things, the type selected is the result of a compromise. If we restrict the type to only the integer class, then in the generic subprogram, we can perform any operations that are defined for the integer class of variables, such as arithmetic operations, all six comparisons, and logical operations. On the other hand, we are restricted in the number of types that the generic subprogram can be used for, since it cannot be used for any real types, records, or arrays. In a sense, we have restricted the usage of the generic procedure by allowing more freedom of use within the procedure.

The first subprogram declares the formal generic type to be of type private which severely limits the operations that can be performed on the data within the procedure. The generic procedure is limited to assignment, and compares for equality and inequality. No other operations are allowed within the generic procedure. However, by limiting the type to private within the generic procedure, we allow a much greater flexibility in the calling program. This generic procedure can be instantiated with any type that can be assigned or compared for equality or inequality, which is essentially any type. By limiting the operations allowed within the generic procedure, we have made it much more usable to the caller.

HOW FLEXIBLE ARE THE GENERIC SUBPROGRAMS?

Jumping ahead to the main program for a few moments, we see that the generic procedure Exchange_Data is successfully instantiated for the type INTEGER, MY_RECORD, and FLOAT, in addition to a few others. This procedure is extremely flexible because it was declared with a very restrictive (restrictive within the generic procedure itself) generic formal type. In addition, it makes sense to be able to swap two elements of record types, or two integers, or two real variables.

Continuing in the main program, you will see in lines 57 and 58 that there are not nearly as many uses for the function which was declared with the much more liberal (liberal within the generic function itself) type. Since arithmetic operations on integers were permitted in the function, a record type cannot be used in an instantiation of this generic procedure, because it makes no sense to add two records together.

A SEEMINGLY UNNECESSARY LIMITATION

You will note that because of the way we declared the generic formal type, real types cannot be used in an instantiation of the function. It would make sense to be able to take the average of two real numbers, but it cannot be done with this function because of the definition of Ada. The reason is because there is no generalized scalar type that can be of either integer or real. The addition of one would add lots of extra baggage to the language and would not add much to the utility of Ada. The language is already big enough without adding another burden to it.

AN INEFFICIENT THING TO DO

Lines 52 through 55 each instantiate a copy of the generic procedure Exchange_Data, and each uses the same type for its instantiation, namely CHARACTER. This will result in four sections of code that each do the same thing, the only difference being in the name by which the procedure will be called. If there is in fact a basis for using different names for the same procedure the renaming facility should be used because it will only create an alias for each new name. Only one section of code will be required.

A REALLY DUMB THING TO DO

Line 59 contains an instantiation of the generic function Average that uses type INTEGER and names it Swap, a name that has already been overloaded five times in lines 48 through 52. The Ada system is smart enough to figure out what you really want to do by comparing types and recognizing the fact that this is a function call, and it will do exactly what you want it to do. It would be very poor practice to use a nondescriptive name for a function in any Ada program, but it would be especially bad to use a name that has already been used for a different purpose. The Ada

Page 224: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

compiler would handle this correctly, but you could have a really hard time deciphering it later.

As discussed earlier, this file could be separated into at least three different files and compiled separately, and with some compilers, it could be separated into five files.

A QUICK REVIEW

Remember that when selecting the types for the generic formal parameters, if you select a type that is very restrictive within the subprogram, the subprogram can be used with many types by the user. If you select a type that is rather permissive within the subprogram, the resulting generic subprogram cannot be used with as many types by the user. Be sure to compile and execute this program after you understand the details of its operation.

WHAT ARE THE FORMAL GENERIC TYPES?

Example program ------> e_c30_p3.ada

-- Chapter 30 - Program 3generic type LIMPRIV is limited private; -- Limited private type type PRIV is private; -- Private type type DISCRETE_TYPE is (<>); -- Discrete type type INT_TYPE is range <>; -- Integer type type MOD_TYPE is mod <>; -- Modular type type FLOAT_TYPE is digits <>; -- Floating point type type FIXED_TYPE is delta <>; -- Fixed point type type DECIMAL_TYPE is delta <> digits <>; -- Decimal typeprocedure AllGener;

procedure AllGener isbegin null;end AllGener;

generic type LIMPRIV is limited private; type PRIV is private; type DISCRETE_TYPE is (<>); type MOD_TYPE is mod <>; type FLOAT_TYPE is digits <>; type FIXED_TYPE is delta <>;procedure ManyUsed(A, B, C : MOD_TYPE; Data_In : LIMPRIV; More_Dat : PRIV; List_Of : DISCRETE_TYPE; Real_Dat : FLOAT_TYPE; Fix_Dat : FIXED_TYPE);

procedure ManyUsed(A, B, C : MOD_TYPE; Data_In : LIMPRIV; More_Dat : PRIV; List_Of : DISCRETE_TYPE; Real_Dat : FLOAT_TYPE; Fix_Dat : FIXED_TYPE) is

Index2 : MOD_TYPE := 4;

Index3 : INTEGER := 7;

type NEW_INTEGER is new INTEGER range 12..34;

Page 225: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Small_Int : NEW_INTEGER := 17;

begin Index2 := A + B + C + MOD_TYPE(Index3) + MOD_TYPE(Small_Int);end ManyUsed;

-- Result of execution

-- (No results, this is not executable)

Examine the program named e_c30_p3.ada for an example of nearly all of the available formal generic types. We will study each one in succession in some amount of detail. Beginning with the limited private type illustrated in line 3, we have no freedom within the subprogram, but it can be used with any type in the calling program. The private type is very limited within the generic subprogram, but allows nearly any type in the calling program.

THE DISCRETE FORMAL PARAMETER TYPE

The discrete formal parameter type is declared with the reserved word type followed by the type name, the reserved word is, and the box "<>" within parentheses as illustrated. The type name used here can be matched with any actual type which is of a discrete class. These types are integer class types, enumeration types, the BOOLEAN, and the CHARACTER type. Within the generic subprogram, any operation can be used which can be done to an enumerated variable. This explains why it was necessary for the POS and ORD attributes to be defined for integer type variables as mentioned in an earlier lesson. Because these attributes are available, the integer types can be used with this formal generic type.

THE INTEGER CLASS FORMAL PARAMETER TYPE

The integer formal parameter type, as illustrated in line 6, is declared with the reserved word type followed by the type name, the reserved words is and range, and the "box" as illustrated. The type name used here can be matched with any actual type that is of the integer class. Within the generic subprogram, the arithmetic operations are available, in addition to all of those operations available with the discrete generic formal parameter. But as you may expect, a generic subprogram using this generic formal parameter can not be instantiated with an enumerated type.

THE REAL FORMAL PARAMETER TYPES

The real formal parameter types are declared as illustrated in lines 7 and 8, with the reserved words digits or delta indicating the floating or fixed variety of real types. Only operations permitted for those types are permitted within the generic subprogram, and only the respective real types can be used by the calling program when instantiating a copy of the generic unit.

A procedure that actually uses none of the types is given in line 9. The body for the procedure is given in lines 11 through 14, but it is not very interesting because it doesn't do anything. The second procedure, named ManyUsed, lists all of the types in a similar manner but declares a procedure that uses all six for formal parameters as an illustration. The program itself uses little of the declared interface.

The details of this program will be left for your study after which you should compile it. Because this is composed of only generic subprograms, it is not executable.

THE ARRAY TYPE FORMAL GENERIC PARAMETER

Example program ------> e_c30_p4.ada

Page 226: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Chapter 30 - Program 4 -- Constrained array generic elementgeneric type SUBSCRIPT is (<>); type FLOAT_TYPE is digits <>; type CONSTRAINED_ARRAY is array (SUBSCRIPT) of FLOAT_TYPE;procedure SumArray(Input_Array : CONSTRAINED_ARRAY; Sum_Of_Elements : in out FLOAT_TYPE);

procedure SumArray(Input_Array : CONSTRAINED_ARRAY; Sum_Of_Elements : in out FLOAT_TYPE) isbegin Sum_Of_Elements := 0.0; for Index in Input_Array'FIRST..Input_Array'LAST loop Sum_Of_Elements := Sum_Of_Elements + Input_Array(Index); end loop;end SumArray;

-- Unconstrained array generic elementgeneric type MY_TYPE is range <>; type MY_ARRAY is array (POSITIVE range <>) of MY_TYPE;function AddArray(Input_Array : MY_ARRAY) return MY_TYPE;

function AddArray(Input_Array : MY_ARRAY) return MY_TYPE isSum_Of_Elements : MY_TYPE := 0;begin for Index in Input_Array'FIRST..Input_Array'LAST loop Sum_Of_Elements := Sum_Of_Elements + Input_Array(Index); end loop; return Sum_Of_Elements;end AddArray;

with Ada.Text_IO, SumArray, AddArray;use Ada.Text_IO;

procedure ArrayGen is

type LIMIT_RANGE is new INTEGER range 1..5;type MY_FLOAT is new FLOAT digits 5;type FLOAT_ARRAY is array(LIMIT_RANGE) of MY_FLOAT;

procedure Little_Sum is new SumArray(LIMIT_RANGE, MY_FLOAT, FLOAT_ARRAY);

Total : MY_FLOAT;My_Data : FLOAT_ARRAY := (1.0, 2.2, 4.3, 3.1, 5.4);

type FLEX_ARRAY is array(POSITIVE range <>) of INTEGER;Big_Data : FLEX_ARRAY(1..5) := (13, 12, 17, 33, 41);More_Data : FLEX_ARRAY(12..14) := (34, 101, 56);Result : INTEGER;

function My_Add is new AddArray(INTEGER, FLEX_ARRAY);

begin

Page 227: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Little_Sum(My_Data,Total); Little_Sum((2.4, 1.3, 5.2, 4.7, 0.1),Total);

Result := My_Add(Big_Data); Result := My_Add(More_Data);

end ArrayGen;

-- Result of execution

-- (No output from this program)

Examine the program named e_c30_p4.ada for examples of array type generic formal parameters. The specification and body of a procedure with a constrained array type generic formal parameter are given in lines 3 through 17. There are actually three generic formal parameters listed here, one on each of the three lines numbered 4 through 6.

To define how this procedure is used, we will refer to the instantiation call in line 49 of this file which happens to be within the declaration part of the main program. The generic formal name SUBSCRIPT must be replaced with an actual parameter that is of any discrete type because the type mark contains a "<>" in parentheses. Furthermore, the first element in the actual parameter list will be substituted for this parameter because it is first in the formal parameter list. To begin our instantiation requested in line 49, we can replace every occurrence of the word SUBSCRIPT in lines 4 through 17 with the word LIMIT_RANGE and we are on our way to creating a usable procedure.

The second parameter of the actual parameter list is the type named MY_FLOAT, and it will be used to replace every occurrence of the generic formal type named FLOAT_TYPE listed in the second line of the generic formal parameters. You will notice that the required type is any floating point type because of the occurrence of the reserved word digits in line 5. The type of the actual parameter, as listed in line 50, and defined in line 46 is a floating point type.

CONSTRAINED ARRAY GENERIC PARAMETER

The third line of the formal parameters, which is line 6, declares the constrained array with which a given instantiation can be used. You will notice that this formal parameter depends on both formal parameters declared previously, but since there is only one new parameter in this line, the Ada compiler can properly assign the actual type FLOAT_ARRAY as the replacement for the formal parameter named CONSTRAINED_ARRAY. If you actually make a copy of this generic procedure, and replace the formal parameters with their corresponding actual parameters, then change the name of the procedure to Sum_Array, you could replace the instantiation in lines 49 and 50 with the resulting procedure and the program operation would be identical. Lines 64 and 65 give examples of using the instantiated procedure and will be left to your study.

UNCONSTRAINED ARRAY GENERIC PARAMETER

Lines 22 through 34 give an example of use of an unconstrained array as a generic formal parameter, and is nearly identical to the last example except that the index type is declared to be of type POSITIVE rather than being flexible as in the last example. The type of the index variable could be declared as a generic type parameter with another line added prior to line 24, and the instantiating statement would require three types instead of only two. Line 60 is the example instantiating statement and lines 67 and 68 give examples of use of the resulting function.

As mentioned several times before, each of these subprograms could be separated into separate files

Page 228: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

and compiled separately, then used by any calling program. Be sure to compile and execute this program.

THE ACCESS FORMAL GENERIC PARAMETER

Example program ------> e_c30_p5.ada

-- Chapter 30 - Program 5generic type ACCESS_INT is access INTEGER;procedure Swap_Int_Data(Dat1, Dat2 : ACCESS_INT);

procedure Swap_Int_Data(Dat1, Dat2 : ACCESS_INT) isTemp : ACCESS_INT := new INTEGER;begin Temp.all := Dat1.all; Dat1.all := Dat2.all; Dat2.all := Temp.all;end Swap_Int_Data;

generic type ANY_TYPE is private; type ACCESS_ANY is access ANY_TYPE;procedure Swap_Any_Data(Dat1, Dat2 : ACCESS_ANY);

procedure Swap_Any_Data(Dat1, Dat2 : ACCESS_ANY) isTemp : ACCESS_ANY := new ANY_TYPE;begin Temp.all := Dat1.all; Dat1.all := Dat2.all; Dat2.all := Temp.all;end Swap_Any_Data;

with Ada.Text_IO, Swap_Int_Data, Swap_Any_Data;use Ada.Text_IO;

procedure AccesGen is

type ACCESS_INT is access INTEGER;Address1, Address2 : ACCESS_INT := new INTEGER;

procedure Transpose_Integers is new Swap_Int_Data(ACCESS_INT);

type NAME_ARRAY is array(1..6) of CHARACTER;

type PERSONAL_STUFF is record Age : INTEGER; Grade : INTEGER; Name : NAME_ARRAY; end record;

type PERSONAL_ACCESS is access PERSONAL_STUFF;

Page 229: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Male_Student : PERSONAL_ACCESS := new PERSONAL_STUFF;Female_Student : PERSONAL_ACCESS := new PERSONAL_STUFF;

type ACCESS_FLOAT is access FLOAT;Address_Float1 : ACCESS_FLOAT;Address_Float2 : ACCESS_FLOAT;

procedure Transpose_Floats is new Swap_Any_Data(FLOAT, ACCESS_FLOAT);procedure Transpose_Records is new Swap_Any_Data(PERSONAL_STUFF, PERSONAL_ACCESS);procedure Transpose_Ints is new Swap_Any_Data(INTEGER, ACCESS_INT);

begin

Put_Line("Begin the generic access routine");

Address1.all := 23; Address2.all := 13 * Address1.all; Transpose_Integers(Address1, Address2); Transpose_Ints(Address1, Address2);

Address_Float1 := new FLOAT; Address_Float2 := new FLOAT; Address_Float1.all := 3.141592; Address_Float2.all := 144.0; Transpose_Floats(Address_Float1, Address_Float2);

Male_Student.all := (16, 11, "Johnny"); Female_Student.all := (15, 11, "Sandy "); Transpose_Records(Male_Student, Female_Student);

end Accesgen;

-- Result of execution

-- Begin the generic access routine

Examine the program named e_c30_p5.ada for our first look at the last generic formal parameter type, the access type. The first procedure, in lines 2 through 14 use an access type that matches only an access type which accesses an INTEGER, resulting in little or no flexibility since an access type to an INTEGER type variable is used infrequently. A copy of this generic procedure is instantiated in line 45 of the main program, and the procedure is exercised in line 76 as an example for your study.

The second procedure, given in lines 19 through 32 illustrates a much more useful procedure because this procedure can be used for any access type, including the composite types of arrays and records. Three copies of the generic procedure are instantiated in lines 64 through 68 and all three are exercised in the main program. Note that the more general procedure is used in line 77 to transpose the INTEGER type variables back to their original positions.

The details of this program should be simple for you to follow, so no additional comment needs to be made. Be sure to compile and execute this program.

PROGRAMMING EXERCISES

1. Add a subtype of INTEGER to e_c30_p1.ada and determine what happens if you instantiate

Page 230: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

a copy of Exchange_Data for the subtype in addition to the instantiation for INTEGER. Add statements to the main program to swap values of the new type.(Solution)

-- Chapter 30 - Programming exercise 1 generic type ITEM is range <>; -- integer class only procedure Exchange_Data(X,Y : in out ITEM);

procedure Exchange_Data(X,Y : in out ITEM) is Temp : ITEM; begin Temp := X; X := Y; Y := Temp; end Exchange_Data;

-- This is the beginning of the main program which uses the generic-- procedure defined above.with Exchange_Data;with Ada.Text_IO;use Ada.Text_IO;

procedure CH30_1 is

package Int_IO is new Ada.Text_IO.Integer_IO(INTEGER); use Int_IO;

type MY_INT is new INTEGER range 1..128; subtype SUB_INT is INTEGER range 1.. 55;

procedure SwapInt is new Exchange_Data(INTEGER); procedure SwapNew is new Exchange_Data(MY_INT); procedure SwapSub is new Exchange_Data(SUB_INT);

Index1 : INTEGER := 17; Index2 : INTEGER := 33; Limit1 : MY_INT := 3; Limit2 : MY_INT := 7; Small1 : SUB_INT := 13; Small2 : SUB_INT := 27;

begin

SwapInt(Index1,Index2); SwapNew(Limit1,Limit2); SwapSub(Small1,Small2); SwapInt(Small1,Small2);

Put(Index1); Put(Index2); New_Line; Put(INTEGER(Limit1)); Put(INTEGER(Limit2)); New_Line(2);

SwapInt(Index1,INTEGER(Limit1)); SwapNew(MY_INT(Index2),Limit2);

Put(Index1); Put(Index2); New_Line; Put(INTEGER(Limit1)); Put(INTEGER(Limit2));

Page 231: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

New_Line(2);

end CH30_1;

-- Note that it is legal to instantiate a copy of the generic-- procedure for the subtype. It is a waste of code however,-- because the procedure for the parent type can be used for-- the subtype as illustrated in line 44.

-- Result of Execution

-- 33 17-- 7 3---- 7 3-- 33 17

2. Attempt to instantiate a copy of Average that uses type FLOAT in e_c30_p1.ada.(Solution) -- Chapter 30 - Programming exercise 2 generic type ITEM is range <>; -- integer class only procedure Exchange_Data(X,Y : in out ITEM);

procedure Exchange_Data(X,Y : in out ITEM) is Temp : ITEM; begin Temp := X; X := Y; Y := Temp; end Exchange_Data;

-- This is the beginning of the main program which uses the generic-- procedure defined above.with Exchange_Data;with Ada.Text_IO;use Ada.Text_IO;

procedure CH30_2 is

package Int_IO is new Ada.Text_IO.Integer_IO(INTEGER); use Int_IO;

type MY_INT is new INTEGER range 1..128;

procedure SwapInt is new Exchange_Data(INTEGER); procedure SwapNew is new Exchange_Data(MY_INT); procedure SwapFlt is new Exchange_Data(FLOAT);

Index1 : INTEGER := 17; Index2 : INTEGER := 33; Limit1 : MY_INT := 3; Limit2 : MY_INT := 7;

begin

SwapInt(Index1, Index2);

Page 232: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

SwapNew(Limit1, Limit2);

Put(Index1); Put(Index2); New_Line; Put(INTEGER(Limit1)); Put(INTEGER(Limit2)); New_Line(2);

SwapInt(Index1, INTEGER(Limit1)); SwapNew(MY_INT(Index2), Limit2);

Put(Index1); Put(Index2); New_Line; Put(INTEGER(Limit1)); Put(INTEGER(Limit2)); New_Line(2);

end CH30_2;

-- Result of Execution

-- Compile error,-- Line 29 - must instantiate with integer type for "ITEM".

3. Attempt to perform an addition in the body of Exchange_Data in violation of the private type.(Solution)

-- Chapter 30 - Programming exercise 3 generic type ITEM is private; procedure Exchange_Data(X, Y : in out ITEM);

procedure Exchange_Data(X, Y : in out ITEM) is Temp : ITEM; begin Temp := X + 1; -- This should produce an error X := Y; Y := Temp; end Exchange_Data;

generic type ANOTHER_ITEM is range <>; -- Integer class only function Average(X, Y : ANOTHER_ITEM) return ANOTHER_ITEM;

function Average(X, Y : ANOTHER_ITEM) return ANOTHER_ITEM is Temporary : ANOTHER_ITEM; begin Temporary := (X + Y) / 2; return Temporary; end Average;

Page 233: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- This is the beginning of the main program which uses the generic-- procedure and function defined above.

with Exchange_Data, Average;with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;

procedure CH30_3 is

type NEW_TYPE is new INTEGER range 122..12345;

type MY_RECORD is record My_Grade : INTEGER range 1..128; My_Class : CHARACTER; end record;

procedure Swap is new Exchange_Data(INTEGER); procedure Swap is new Exchange_Data(MY_RECORD); procedure Swap is new Exchange_Data(FLOAT); procedure Swap is new Exchange_Data(NEW_TYPE); procedure Swap is new Exchange_Data(CHARACTER); procedure Trade is new Exchange_Data(CHARACTER); procedure Exchange is new Exchange_Data(CHARACTER); procedure Puppy is new Exchange_Data(CHARACTER);

function Mean is new Average(INTEGER); function Mean is new Average(NEW_TYPE); function Swap is new Average(INTEGER); -- This is dumb to do, -- but it is legal.

Index1 : INTEGER := 117; Index2 : INTEGER := 123; Data1 : MY_RECORD := (15,'A'); Data2 : MY_RECORD := (91,'B'); Real1 : FLOAT := 3.14; Real2 : FLOAT := 77.02; Value1 : NEW_TYPE := 222; Value2 : NEW_TYPE := 345;

begin

Put(Real1, 4, 2, 0); Put(Real2, 4, 2, 0); New_Line; Swap(Real1, Real2); Put(Real1, 4, 2, 0); Put(Real2, 4, 2, 0); New_Line(2);

Put(Index1); Put(Index2); New_Line;

Swap(Index1, Index2); Swap(Data1, Data2);

Put(Index1); Put(Index2); New_Line;

Page 234: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Now to exercise some of the functions

Index1 := Mean(Index2, 16); Value1 := Mean(Value2, Value2 + 132); Index1 := Swap(Index1, Index2 + 12); -- This actually gets the -- mean of the inputs.

end CH30_3;

-- Result of Execution

-- Compile error,-- Line 9 - Illegal operation for a private type, type clash.

Page 235: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Ada Tutorial - Chapter 31

GENERIC PACKAGES

OUR FIRST GENERIC PACKAGE

In the last chapter we studied the use of generic subprograms. This chapter will be devoted to the study of generic packages, and we will exhaust the topic because only procedures, functions, and packages can be used as generic units.

Example program ------> e_c31_p1.ada

-- Chapter 31 - Program 1generic type MY_INT_TYPE is (<>); type MY_REAL_TYPE is digits <>;package EasyPkg is procedure Trade_Values (X, Y : in out MY_INT_TYPE); function Average_Values (X, Y : MY_REAL_TYPE) return MY_REAL_TYPE;end EasyPkg;

package body EasyPkg is procedure Trade_Values (X, Y : in out MY_INT_TYPE) is Temp : MY_INT_TYPE; begin Temp := X; X := Y; Y := Temp; end Trade_Values;

function Average_Values (X, Y : MY_REAL_TYPE) return MY_REAL_TYPE is begin return (X + Y) / 2.0; end Average_Values;

end EasyPkg;

with Ada.Text_IO, EasyPkg;use Ada.Text_IO;

procedure GenPkg is

type MY_NEW_TYPE is new INTEGER range -12..123;type MY_NEW_FLOAT is new FLOAT digits 6;

package Funny_Stuff is new EasyPkg(MY_NEW_TYPE, MY_NEW_FLOAT);use Funny_Stuff;package Usual_Stuff is new EasyPkg(INTEGER, FLOAT);use Usual_Stuff;

Int1 : INTEGER := 12;Int2 : INTEGER := 35;My_Int1 : MY_NEW_TYPE := 1;My_Int2 : MY_NEW_TYPE := 14;

Real1 : FLOAT;My_Real1 : MY_NEW_FLOAT;

Page 236: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

begin Trade_Values(Int1, Int2); -- Uses Usual_Stuff.Trade_Values Trade_Values(My_Int1, My_Int2); -- Uses Funny_Stuff.Trade_Values Usual_Stuff.Trade_Values(Int1, Int2); Funny_Stuff.Trade_Values(My_Int1, My_Int2);-- Usual_Stuff.Trade_Values(My_Int1, My_Int2); -- Illegal-- Trade_Values(My_Int1, Int2); -- Illegal

Real1 := Average_Values(2.71828, 3.141592); Real1 := Average_Values(Real1, 2.0 * 3.141592); My_Real1 := Average_Values(12.3, 27.345); My_Real1 := Average_Values(My_Real1, 2.0 * 3.141592); My_Real1 := Funny_Stuff.Average_Values(12.3, 27.345);end GenPkg;

-- Result of execution

-- (There is no output from this program)

Examine the file named e_c31_p1.ada for our first example of a generic package. This package is named EasyPkg, because it is so easy to understand, and is composed of one procedure and one function. The generic package begins with the reserved word generic, followed by two generic formal parameters, and the standard format for an Ada package specification in lines 5 through 9. The first generic formal parameter is a discrete type, and the second is a floating point type as we discussed in the last chapter.

The instantiating statements are given in lines 39 and 41 of the main program and are no different than those used in the last chapter except for the first word, the reserved word package, because we are declaring an instance of a package in this case. If we replace the formal parameter types with the types declared in each instantiation statement, we will have a normal package just as we studied previously. In the case of a package, we can add the use clause as we have in lines 40 and 42, eliminating the need for the extended naming notation, commonly called the dot notation. After declaring a few variables to work with, we are ready to exercise the two procedures and functions we have declared. It should be clear to you that we have a procedure and a function in the package named Funny_Stuff, and another procedure and function in the package named Usual_Stuff.

A fine point must be mentioned about the use clause when used with the generic package. It is not legal to use a use clause with the generic package itself. Every instantiation must explicitly give the extended name for the generic package. Of course this only occurs with nested generic packages which are the topic of the next example program. The use clause however, is legal for use with any and every instantiated copy of a generic package. The instantiated package's new name cannot be overloaded because it is not permitted to overload the name of any package.

USING THE INSTANTIATED PACKAGES

In line 53, we call the procedure named Trade_Values, but since there are two procedures with this name, Ada will pick the correct one by comparing the types. This is our old friend called overloading again. In line 54, because of the types used, the other procedure will be used as indicated in the comments. In lines 55 and 56, we explicitly tell the system which overloading to use, but it will still check the types to see if they are compatible. In line 57 we tell the system to use the wrong one which leads to a type mismatch and a compile error, and in line 58, we use two different types for the parameters, which is another type mismatch. (Lines 57 and 58 are commented out so that we will not actually get the error, but you should remove the comments to

Page 237: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

see that the compiler does report an error.)

Lines 60 through 64 give examples of proper usage of the two functions instantiated above and illustrate that the literals of type universal_real are compatible with both copies of the function as you would expect. The compiler uses the type of the assignment variable on the left hand side of the assignment statement to decide which overloading to use for each statement.

After you spend enough time to understand this program, compile and execute it even though it has no output.

NESTED GENERIC PACKAGES

Example program ------> e_c31_p2.ada

-- Chapter 31 - Program 2package EasyPkg is procedure Trade_Values (X, Y : in out INTEGER);

generic type MY_REAL_TYPE is digits <>; package Nested_Generic is function Average_Values (X, Y : MY_REAL_TYPE) return MY_REAL_TYPE; end Nested_Generic;end EasyPkg;

package body EasyPkg is procedure Trade_Values (X, Y : in out INTEGER) is Temp : INTEGER; begin Temp := X; X := Y; Y := Temp; end Trade_Values;

package body Nested_Generic is function Average_Values (X, Y : MY_REAL_TYPE) return MY_REAL_TYPE is begin return (X + Y) / 2.0; end Average_Values; end Nested_Generic;

end EasyPkg;

with Ada.Text_IO, EasyPkg;use Ada.Text_IO, EasyPkg;

procedure NestPkg is

type MY_NEW_FLOAT is new FLOAT digits 6;

package Funny_Stuff is new EasyPkg.Nested_Generic(MY_NEW_FLOAT);use Funny_Stuff;package Usual_Stuff is new Nested_Generic(FLOAT);use Usual_Stuff;

Int1 : INTEGER := 12;Int2 : INTEGER := 35;

Page 238: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Real1 : FLOAT;My_Real1 : MY_NEW_FLOAT;

begin Trade_Values(Int1, Int2); -- Uses Trade_Values directly EasyPkg.Trade_Values(Int1, Int2); -- Uses Trade_Values directly-- Usual_Stuff.Trade_Values(Int1, Int2); -- Illegal-- Funny_Stuff.Trade_Values(Int1, Int2); -- Illegal

Real1 := Average_Values(2.71828, 3.141592); Real1 := Average_Values(Real1, 2.0 * 3.141592); My_Real1 := Average_Values(12.3, 27.345); My_Real1 := Average_Values(My_Real1, 2.0 * 3.141592); My_Real1 := Funny_Stuff.Average_Values(12.3, 27.345);end NestPkg;

-- Result of execution

-- (There is no output from this program)

The example program named e_c31_p2.ada contains a generic package nested within another non-generic package. The outer package contains a single procedure, and the nested generic package contains a single formal generic parameter of a floating point type, and a single function. The procedure and function are the same as those in the last program, but they are organized differently here for illustration.

Note carefully that since the procedure is not in the generic part of the outer package, it is directly available in the same manner as if it were in a package without a generic part. The package name can be declared in a use clause which will make the non-generic portion of the package usable.

The executable part of the program is very simple and very similar to the last example program so it will be left to the student to study, compile, and execute this program.

OBJECTS AS GENERIC FORMAL PARAMETERS

Example program ------> e_c31_p3.ada

-- Chapter 31 - Program 3generic type ITEM is range <>; ROWS : in POSITIVE := 8; COLUMNS : in POSITIVE := 12;package Matrix_Operations is

type LOCAL_MATRIX is private;

procedure Clear_Matrix(Matrix_To_Clear : in out LOCAL_MATRIX);

function Add_Matrices(M1, M2 : LOCAL_MATRIX) return LOCAL_MATRIX;

private type LOCAL_MATRIX is array(POSITIVE range 1..ROWS, POSITIVE range 1..COLUMNS) of ITEM;end Matrix_Operations;

Page 239: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

with Ada.Text_IO;use Ada.Text_IO;package body Matrix_Operations is

procedure Clear_Matrix(Matrix_To_Clear : in out LOCAL_MATRIX) is begin for Row in 1..Matrix_To_Clear'LAST(1) loop for column in 1.. Matrix_To_Clear'LAST(2) loop Matrix_To_Clear(Row,Column) := 0; end loop; end loop; Put_Line(" A matrix has been cleared"); end Clear_Matrix;

function Add_Matrices(M1, M2 : LOCAL_MATRIX) return LOCAL_MATRIX is Temp : LOCAL_MATRIX; begin for Row in 1..M1'LAST(1) loop for column in 1.. M1'LAST(2) loop Temp(Row, Column) := M1(Row, Column) + M2(Row, Column); end loop; end loop; Put_Line(" Two matrices have been summed"); return Temp; end Add_Matrices;

end Matrix_Operations;

with Ada.Text_IO, Matrix_Operations;use Ada.Text_IO;

procedure ObjGen is

package Cookie_Jar is new Matrix_Operations(NATURAL, 3, 5);use Cookie_Jar;package Puppies is new Matrix_Operations(INTEGER);package Animals is new Matrix_Operations(COLUMNS => 7, ROWS => 11, ITEM => INTEGER);use Animals;

Cookie_Matrix : Cookie_Jar.LOCAL_MATRIX;Dog_Matrix : Puppies.LOCAL_MATRIX;Cattle, Jerseys, Black_Angus : Animals.LOCAL_MATRIX;

begin Put_Line("Begin the matrix operations."); Clear_Matrix(Cookie_Matrix); Puppies.Clear_Matrix(Dog_Matrix); Clear_Matrix(Jerseys); Clear_Matrix(Black_Angus); Cattle := Add_Matrices(Jerseys, Black_Angus);

end ObjGen;

-- Result of execution

Page 240: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Begin the matrix operations-- A matrix has been cleared-- A matrix has been cleared-- A matrix has been cleared-- A matrix has been cleared-- Two matrices have been summed

Examine the program named e_c31_p3.ada for an example of using objects for formal generic parameters instead of just types. In this case the number of ROWS and the number of COLUMNS will be part of the instantiation, and the resulting procedure and function will be ready to work with the desired size matrices. In addition, the constant named ROWS, and the constant named COLUMNS are initialized to the values given in lines 4 and 5. If a value is not given with the instantiation, these values will be used in much the same way that we use default values with a procedure or function.

In this case, the package body uses the standard Ada.Text_IO package and outputs a string to the monitor each time one of the subprograms is called. This is only done to illustrate to you that there is nothing magic about the package Ada.Text_IO, and that it can be used within another package.

USING THE OBJECTS

In line 58, the package is instantiated using the positional aggregate notation with values of 3 and 5 for the matrix size. Line 60 illustrates the use of the defaults declared in the generic part above, and line 61 illustrates the use of the named aggregate notation used during package instantiation.

AN EXPORTED TYPE CAN BE USED

Referring back to line 8, we have the type named LOCAL_MATRIX declared in the package specification and therefore available to any calling program. If you refer to the private part of the package specification, you will see that the type LOCAL_MATRIX is declared to be a function of the formal generic objects. After we instantiate a copy of the package, we have the exported type available for use in the calling program. We use the types exported types in lines 66 through 68 to declare a few variables, but even though two of the instantiations make use of the use clause, we must explicitly declare the desired type by using the extended naming notation. This is because the compiler has no way of knowing which exported type we are interested in using for each variable.

With the above descriptions of the new concepts in this program, you should be able to understand the details of it. Be sure to compile and execute this program.

A PROCEDURE AS A GENERIC PARAMETER

Example program ------> e_c31_p4.ada

-- Chapter 31 - Program 4generic type ITEM is range <>; with procedure Funny_Average(A, B, C : ITEM; Result : in out ITEM);package Dis_Avg is procedure Display_Funny_Average(A, B, C : ITEM);end Dis_Avg;

with Ada.Text_IO;use Ada.Text_IO;

package body Dis_Avg is

Page 241: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

package Int_IO is new Ada.Text_IO.Integer_IO(ITEM); use Int_IO;

procedure Display_Funny_Average(A, B, C : ITEM) is Mean : ITEM; begin Funny_Average(A, B, C, Mean); Put("The funny average is "); Put(Mean, 5); New_Line; end Display_Funny_Average;end Dis_Avg;

with Dis_Avg;

procedure ProcPkg is

procedure R_Average(First, Second, Third : INTEGER; Weighted_Average : in out INTEGER) is begin Weighted_Average := (First + 2 * Second + Third) / 4; end R_Average;

package Averages is new Dis_Avg(INTEGER, R_Average);

begin Averages.Display_Funny_Average(1, 3, 5); Averages.Display_Funny_Average(1, 3, 17); Averages.Display_Funny_Average(3, 17, 5);end ProcPkg;

-- Result of execution

-- The funny average is 3-- The funny average is 6-- The funny average is 10

Examine the program named e_c31_p4.ada for an example of a procedure being used as a generic formal parameter. The syntax uses the reserved word with to begin the formal parameter in line 4, and the complete header for the procedure is given with the list of formal parameters and their types. This procedure can then be called from within the body of the package and will refer to a procedure that is actually outside of the generic package. Now we must define the procedure that will be called in order to satisfy a call to this formal parameter. We will see where this procedure is defined shortly.

Refer to the main program where the procedure named R_Average is declared. It has exactly the same formal parameter structure as that declared in the generic formal parameter, so it can be used in the instantiating call in line 43. A call to the procedure in the generic instantiation actually results in a call back to the procedure in the calling program to do some calculations.

WHAT CAN THIS BE USED FOR?

This capability gives you the ability to write a generic package that can be used in several places but with a slight difference in each place, because each instantiation can use a different procedure for

Page 242: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

the reference back to the calling program. This is simply another one of the available entities that add to the flexibility of Ada. After you understand the logic here, you should compile and execute this program.

A FUNCTION AS A GENERIC PARAMETER

Example program ------> e_c31_p5.ada

-- Chapter 31 - Program 5generic type ITEM is range <>; with function Funny_Average(A, B, C : ITEM) return ITEM;package Dis_Avg is procedure Display_Funny_Average(A, B, C : ITEM);end Dis_Avg;

with Ada.Text_IO;use Ada.Text_IO;

package body Dis_Avg is

package Int_IO is new Ada.Text_IO.Integer_IO(ITEM); use Int_IO;

procedure Display_Funny_Average(A, B, C : ITEM) is

Mean : ITEM;

begin Mean := Funny_Average(A, B, C); Put("The funny average is "); Put(Mean, 5); New_Line; end Display_Funny_Average;end Dis_Avg;

with Dis_Avg;

procedure FuncPkg is

function R_Average(First, Second, Third : INTEGER) return INTEGER is begin return (First + 2 * Second + Third) / 4; end R_Average;

package Averages is new Dis_Avg(INTEGER, R_Average);

begin Averages.Display_Funny_Average(1, 3, 5); Averages.Display_Funny_Average(1, 3, 17); Averages.Display_Funny_Average(3, 17, 5);end FuncPkg;

-- Result of execution

Page 243: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- The funny average is 3-- The funny average is 6-- The funny average is 10

Examine the program named e_c31_p5.ada for an example of using a function as a generic formal parameter. The logic is similar to that described in the last program except that a function is used for the reference back to the calling program. No further explanation will be given since it should not be needed. It should not come as much of a surprise to you that several procedures or functions, or a combination of them, can be used as generic formal parameters. Any of the isolated examples can be combined as needed to achieve the desired goal. Nesting of generic and normal packages can be done as needed. It will be up to you to decide how to modularize and package your program to best accomplish the stated goals. Ada gives you many options. Be sure to compile and execute this program.

PROGRAMMING EXERCISES

1. Add an enumerated type to e_c31_p1.ada, instantiate a copy of EasyPkg, and use it to swap some values of the enumerated type. You will have to supply a floating point type with the enumerated type when you instantiate it.(Solution)

-- Chapter 31 - Programming exercise 1generic type MY_INT_TYPE is (<>); type MY_REAL_TYPE is digits <>;package EasyPkg is procedure Trade_Values (X, Y : in out MY_INT_TYPE); function Average_Values (X, Y : MY_REAL_TYPE) return MY_REAL_TYPE;end EasyPkg;

package body EasyPkg is procedure Trade_Values (X, Y : in out MY_INT_TYPE) is Temp : MY_INT_TYPE; begin Temp := X; X := Y; Y := Temp; end Trade_Values;

function Average_Values (X, Y : MY_REAL_TYPE) return MY_REAL_TYPE is begin return (X + Y) / 2.0; end Average_Values;

end EasyPkg;

with Ada.Text_IO, EasyPkg;use Ada.Text_IO;

procedure CH31_1 is

type MY_NEW_TYPE is new INTEGER range -12..123;type MY_NEW_FLOAT is new FLOAT digits 6;type RESULT is (WIN, LOSE, DRAW, FORFEIT, CANCELLED);

package Funny_Stuff is new EasyPkg(MY_NEW_TYPE, MY_NEW_FLOAT);

Page 244: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

use Funny_Stuff;package Usual_Stuff is new EasyPkg(INTEGER, FLOAT);use Usual_Stuff;package Ball_Game is new EasyPkg(RESULT, FLOAT);use Ball_Game;

Int1 : INTEGER := 12;Int2 : INTEGER := 35;My_Int1 : MY_NEW_TYPE := 1;My_Int2 : MY_NEW_TYPE := 14;

Game1 : RESULT := WIN;Game2 : RESULT := FORFEIT;

Real1 : FLOAT;My_Real1 : MY_NEW_FLOAT;

begin Ball_Game.Trade_Values(Game1, Game2); Trade_Values(Game1, Game2);

Trade_Values(Int1, Int2); -- Uses Usual_Stuff.Trade_Values Trade_Values(My_Int1, My_Int2); -- Uses Funny_Stuff.Trade_Values Usual_Stuff.Trade_Values(Int1, Int2); Funny_Stuff.Trade_Values(My_Int1, My_Int2);-- Usual_Stuff.Trade_Values(My_Int1, My_Int2); -- Illegal-- Trade_Values(My_Int1, Int2); -- Illegal

Real1 := Usual_Stuff.Average_Values(2.71828, 3.141592); Real1 := Usual_Stuff.Average_Values(Real1, 2.0 * 3.141592); My_Real1 := Average_Values(12.3, 27.345); My_Real1 := Average_Values(My_Real1, 2.0 * 3.141592); My_Real1 := Funny_Stuff.Average_Values(12.3, 27.345);end CH31_1;

-- Result of execution

-- (There is no output from this program)

2. Convert e_c16_p1.ada from chapter 16 of this tutorial into a generic package that can be used with any discrete type. Modify e_c16_p2.ada from the same chapter to instantiate and use both an enumerated type and an INTEGER type.(Solution 1)

-- Chapter 31 - Programming exercise 2generic type ITEM is (<>);package CharStak is

procedure Push(In_Char : in ITEM); -- In_Char is added to the -- stack if there is room.

procedure Pop(Out_Char : out ITEM); -- Out_Char is removed from -- stack and returned if a -- character is on stack. -- else a blank is returned

function Is_Empty return BOOLEAN; -- TRUE if stack is empty

Page 245: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

function Is_Full return BOOLEAN; -- TRUE if stack is full

function Current_Stack_Size return INTEGER;

procedure Clear_Stack; -- Reset the stack to empty

end CharStak;

package body CharStak is

Maximum_Size : constant := 25;Stack_List : array(1..Maximum_Size) of ITEM; -- The stack itself, purposely -- defined very small.Top_Of_Stack : INTEGER := 0; -- This will always point to -- the top entry on the stack.

procedure Push(In_Char : in ITEM) isbegin if not Is_Full then Top_Of_Stack := Top_Of_Stack + 1; Stack_List(Top_Of_Stack) := In_Char; end if;end Push;

procedure Pop(Out_Char : out ITEM) isbegin if Is_Empty then null; -- Cannot return a blank for a generic routine. else Out_Char := Stack_List(Top_Of_Stack); Top_Of_Stack := Top_Of_Stack - 1; end if;end Pop;

function Is_Empty return BOOLEAN isbegin return Top_Of_Stack = 0;end Is_Empty;

function Is_Full return BOOLEAN isbegin return Top_Of_Stack = Maximum_Size;end Is_Full;

function Current_Stack_Size return INTEGER isbegin return Top_Of_Stack;end Current_Stack_Size;

procedure Clear_Stack isbegin

Page 246: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Top_Of_Stack := 0;end Clear_Stack;

end CharStak;

(Solution 2)

-- Chapter 31 - Programming exercise 2with Ada.Text_IO;use Ada.Text_IO;with CharStak;

procedure CH31_2B is

package Int_IO is new Ada.Text_IO.Integer_IO(INTEGER); use Int_IO; package Stack is new CharStak(CHARACTER); use Stack;

Example : constant STRING := "This is the first test."; Another : constant STRING := "This is another test and this should not fit.";

procedure Fill_The_Stack(Input_Line : STRING) is begin Clear_Stack; for Index in 1..Input_Line'LAST loop if Is_Full then Put_Line("The stack is full, no more added."); exit; else Push(Input_Line(Index)); end if; end loop; end Fill_The_Stack;

procedure Empty_The_Stack is Char : CHARACTER; begin loop if Is_Empty then New_Line; Put_Line("The stack is empty."); exit; else Pop(Char); Put(Char); end if; end loop; end Empty_The_Stack;

begin

Put_Line(Example); Fill_The_Stack(Example); Empty_The_Stack;

New_Line; Put_Line(Another);

Page 247: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Fill_The_Stack(Another); Empty_The_Stack;

end CH31_2B;

-- Result of execution

-- This is the first test.-- .tset tsrif eht si sihT-- The stack is empty.---- This is another test and should not fit.-- The stack is full, no more added.-- dna tset rehtona si sihT-- The stack is empty.

Page 248: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Ada Tutorial - Chapter 32

MACHINE DEPENDENT FEATURES

In this chapter we will cover some of the constructs available with Ada that give you the ability to get into real trouble, because we will be using the low level features of Ada. The low level features are those that allow us to get down to the inner workings of the computer, but we will be able to get to them by use of rather high level Ada abstractions.

OVERRIDING COMPILER DEFAULTS

Normally, the compiler will make many decisions for us about how to store data, and how to operate on it. Occasionally, we wish to tell the compiler that we are not satisfied with the way it defaults something, and we wish for it to use a different means of representation. The topics examined in this chapter will give us the ability to tell the compiler how to map something onto the underlying hardware. We gain control of how the compiler represents some things within the machine, but we also may make the resulting program nonportable. This can cause many problems if we ever wish to move our program to another computer. In addition, we may affect the size or speed of the resulting code, since the compiler writer will use a certain method of representing data because it results in some form of savings on the particular target machine.

USE REPRESENTATION CLAUSES SPARINGLY

In general, the use of the representation clauses which will be discussed in this chapter, should be used very sparingly if they are used at all. In any case, it would be best to delay using any of these constructs until the program is fairly well developed, because correct operation of the overall program is far more important than generating tight efficient code. After the program is debugged and operating as desired, it is usually a simple matter to go back and tighten up the size and speed of the most heavily used sections of code. You may find that after you get the program working in a macro sense, it is fast enough and compact enough that it is not necessary to resort to these techniques.

With all of these words telling you not to use these programming techniques, we will now take a look at how to use some of them. Keep in mind however, that when you use these Ada constructs, you may lose the ability to port your program to another implementation. You will also find that the example programs in this chapter are those that are most likely to cause problems with your compiler.

THE REQUIRED PACKAGE NAMED System

According to the Ada 95 reference Manual (ARM), your compiler must have a standard package named System which must be defined in Annex M of the documentation supplied with your compiler. Section 13.7 of the ARM contains a minimum list of items that must be defined for you in the package specification for System. It would be profitable for you to spend a few minutes studying the definition of the package named System in your compiler documentation and the required items listed in the ARM. You will find several constants defined there that you have been using throughout this tutorial, and now you know where they came from.

WHAT REPRESENTATION CONTROLS ARE THERE?

When we attempt to control the representation of data and the way it is stored, we are actually telling the Ada compiler how to define a type. It would be a little more precise to say we are telling the compiler how to modify its default method of storing a certain type, including which parameters we want it to change, and what to change them to.

There are four different areas of Ada typing that can be specified with representation clauses, and we will look at each area in succession. They are listed in no particular order as follows.

Length specification

Page 249: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Record type representation Enumeration type representation Address specification

You will note that in each example, we will first declare the basic type, then we will tell the Ada compiler what parameters we wish to modify to suit our purposes, and finally we will declare objects of the modified type. We will mention this order again in some of the following example programs.

THE LENGTH SPECIFICATION

Example program ------> e_c32_p1.ada

-- Chapter 32 - Program 1with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure SmallInt is

BITS : constant := 1; type SMALL_INTEGER is new INTEGER range -25..120; for SMALL_INTEGER'SIZE use 8*BITS; type BIG_ARRAY is array(1..1000) of SMALL_INTEGER;

Number : SMALL_INTEGER := 27; Big_List : BIG_ARRAY;

begin

Put("The type SMALL_INTEGER uses "); Put(INTEGER(SMALL_INTEGER'SIZE), 5); Put(" bits of memory."); New_Line;

Put("The type BIG_ARRAY uses "); Put(INTEGER(BIG_ARRAY'SIZE), 5); Put(" bits of memory."); New_Line;

end SmallInt;

-- Result of execution (as written)

-- The type SMALL_INTEGER uses 8 bits of memory.-- The type BIG_ARRAY uses 8000 bits of memory.

-- Result of execution (with line 9 commented out)

-- The type SMALL_INTEGER uses 32 bits of memory.-- The type BIG_ARRAY uses 32000 bits of memory.

The length specification is used to declare how many bits can be used to store data in a certain type. This representation clause is illustrated in the example program named SMe_c06_p1.ada, which you should examine at this time.

The only code that is of special interest in this program is found in lines 7 through 10. First we define a constant of value 1 named BITS to be used later for a very good reason. Next we declare a

Page 250: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

derived type which covers a range of -25 through 120, a range small enough to be represented with only 8 bits. Since we wish to declare a rather large array of this type, and we suspect that our compiler will simply assign a full word of 32 bits to each variable of this type, we tell the compiler that we want it to use only 8 bits to store a variable of this type. Line 9 is a representation clause to do this. It begins with the reserved word for followed by the type with a tick and the word SIZE. It looks like an attribute, and that is just what it is, because we are telling the compiler that we want the attribute named SIZE to have the value of 8.

The use of the constant should now be clear. It makes the expression extremely clear because when you read the expression it says just what it is doing. We told the compiler to use 8 bits for the size of this type. You should not be bothered that using this construct will slow down the program, because it will not. This constant will be evaluated only once, and that will be at compile time, not when the program is executing.

YOUR COMPILER MAY NOT LIKE THIS CLAUSE

The ARM does not require that an Ada compiler implement every representation clause. For this reason, even though you have a validated Ada compiler, it may not implement line 9. If it doesn't, it will give you a message during compilation that it cannot handle this representation clause and will fail to give you an object module. You will be required to remove the offending representation clause and recompile the program. If your compiler does accept it, when you execute this program you will see that the type SMALL_INTEGER requires only 8 bits of storage. After successfully compiling and running the program, comment out line 9 and compile and execute it again. You will probably find that your compiler requires more than 8 bits to store data of this type. If your compiler cannot handle line 9, comment it out and compile and execute the resulting program.

Remember that at the beginning of this chapter we stated that of all the programs in this tutorial, this chapter would contain the ones that were most likely to have problems with your compiler. This is the reason that so many of these programs are not transportable from compiler to compiler. Only three of the five compilers used to test these example programs implemented this particular clause.

THE STORAGE_SIZE REPRESENTATION CLAUSE

Another representation clause that is very similar to SIZE is the one named STORAGE_SIZE. This is used to tell the compiler how many storage units to use to store an access type variable or a task type variable. The ARM is not very specific on just what a storage unit is, so it must be defined by your compiler. Because it is not well defined, and is therefore different for each compiler, an explanation may be more confusing than simply not attempting to explain it. You will be left to study it on your own, remembering that it is similar to SIZE. With all of the Ada you have studied to this point, you should be able to easily decipher the notes on this topic in your compiler documentation.

THE RECORD TYPE REPRESENTATION

Example program ------> e_c32_p2.ada

-- Chapter 32 - Program 2with Ada.Text_IO, Ada.Integer_Text_IO, Unchecked_Conversion;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure BitField is

type BITS is record Lower : INTEGER range 0..3; Middle : INTEGER range 0..1; High : INTEGER range 0..3; end record;

for BITS use record Lower at 0 range 0..1;

Page 251: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Middle at 0 range 2..2; High at 0 range 3..4; end record;

BitData : BITS; IntData : INTEGER;

function Switch_To_Bits is new Unchecked_Conversion( Source => INTEGER, Target => BITS);

begin

BitData.Lower := 2; BitData.High := 3; BitData.Middle := 0;

for Index in 1..18 loop IntData := Index + 5; BitData := Switch_To_Bits(IntData); Put("IntData ="); Put(IntData, 4); Put(" BitData ="); Put(INTEGER(BitData.High), 3); Put(INTEGER(BitData.Middle), 3); Put(INTEGER(BitData.Lower), 3); New_Line; end loop;

end BitField;

-- Result of Execution

-- IntData = 6 BitData = 0 1 2-- IntData = 7 BitData = 0 1 3-- IntData = 8 BitData = 1 0 0-- IntData = 9 BitData = 1 0 1-- IntData = 10 BitData = 1 0 2-- IntData = 11 BitData = 1 0 3-- IntData = 12 BitData = 1 1 0-- IntData = 13 BitData = 1 1 1-- IntData = 14 BitData = 1 1 2-- IntData = 15 BitData = 1 1 3-- IntData = 16 BitData = 2 0 0-- IntData = 17 BitData = 2 0 1-- IntData = 18 BitData = 2 0 2-- IntData = 19 BitData = 2 0 3-- IntData = 20 BitData = 2 1 0-- IntData = 21 BitData = 2 1 1-- IntData = 22 BitData = 2 1 2-- IntData = 23 BitData = 2 1 3

Examine the program named e_c32_p2.ada for an example of two additional low level constructs, the record type representation and the unchecked conversion. We will begin with the record type representation.

First, we declare a record type named BITS with three fields of extremely limited range since we only wish to store one or two bits in each field. Because of the limited range, we would like to instruct the compiler to store the individual variables in very small memory units, and in addition, we would like it to store all three fields in a single word. We do this in lines 13 through 17 where

Page 252: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

we give the compiler the desired pattern for the three fields. It looks very much like a record except for the substitution of the reserved words for and use in place of type and is in the record type definition. Remember that we are modifying the type we have already declared to tell the compiler how to actually implement it.

Each of the fields is slightly different also since the reserved word at is used followed by the number 0 in all three cases. This is telling the system to store this variable in the word with an offset of zero from the first word, or in other words, we are telling the compiler to put this variable in the first word of the record. Also, after the reserved word range, we have another defined range which tells the compiler which bits of the word to store these in. The variable named Lower is therefore to be stored in the first word, and is to occupy bit positions 0 and 1 of that word. The variable named Middle is also to be stored in the first word, and will occupy bit position 2 of that word. The variable named High will occupy bit positions 3 and 4 of the same word.

WHAT DO THE BIT POSITIONS MEAN?

By this point, you are probably wondering what is bit position 0. Is it the least significant bit or the most significant bit? That question is entirely up to the compiler writer and you must consult your documentation for the answer to this question and nearly any other questions you may have about this new construct. One possible resulting bit pattern is illustrated in figure 32-1. The actual bit pattern for your compiler may be something entirely different.

The INTEGER type variable consists of a 32 bit number on most microcomputers and nearly all minicomputers, but even this is up to the implementor to define in any way he desires. Because 32 bits is fairly standard for the INTEGER type variable, and for a single word, the various fields of the record were declared to be in one word to illustrate the next low level programming construct in Ada. If your compiler is especially smart, you could continue the packing by telling the compiler to squeeze the entire record into as few as 5 bits, since that is all that would be needed to actually store the data. This would be done using the SIZE representation clause in a manner similar to the last example program.

THERE IS A LIMIT TO THE MODIFICATIONS

After declaring the type, then modifying it to suit our purposes, we declare a variable of the new type in line 19, which freezes the type and prevents any further type modifications. Note that it would be an error to attempt to further modify the type after we have declared a variable of that type. This is because it would allow declaring another variable of the newly modified type which would in fact be different from the type of the first variable.

If additional fields were added with at 1 in their representation clauses, they would be put in the word that was at an offset of 1 from the beginning of the record. This would be the second word of course. You can see that it is possible to very carefully control where and how the data is stored in a record of this data type.

A PROBLEM GENERATOR, USE WITH CAUTION

In line 22, we instantiate a copy of the generic function named Unchecked_Conversion to illustrate its use. This is a function that can really get you into trouble, but can be a real time saver if you need its capability. In this case, Switch_To_Bits will use an INTEGER for its source and a record of type BITS as the result or target. A call to the function with an INTEGER type variable as an argument will change the type of the variable and return the same value with a new type. In this

Page 253: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

case, because the individual bits are packed into a single word, the data in the INTEGER type variable is actually split up into the three fields of the record. The original data, as well as the three fields, are displayed for a small range of values. In this case the composite data in the integer variable is unpacked into the respective fields by the system.

Note that line 34 could have used the loop index named Index as the actual parameter since it is legal in Ada to use a universal_integer in the call.

The only real requirement for use of the unchecked type conversion is that both structures have the same number of program units or bits. The C programmer will recognize this as the union, and the Pascal programmer should see that this is the same as using a variant record for type conversion.

Be sure to compile and run this program to see if it really does what it should. Your compiler may not implement some or all of these features, in which case you can only study the result of execution given at the end of the example program. We said at the beginning of this chapter that there would be a few things you may not be able to do. Only two of the five compilers tested compiled this program completely, and only one stored the bits in the pattern depicted in figure 32-1. Note that the Unchecked_Conversion is not optional but required, and all five compilers tested by the author compiled it properly.

THE PRAGMA NAMED PACK

Example program ------> e_c32_p3.ada

-- Chapter 32 - Program 3with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;

procedure PackItIn is

type LITTLE1 is range 1..57;

type LITTLE_REC1 is record A, B, C : LITTLE1; end record;

type LIST1 is array(1..5) of LITTLE_REC1;

type LITTLE2 is range 1..57;

type LITTLE_REC2 is record A, B, C : LITTLE2; end record; pragma PACK(LITTLE_REC2);

type LIST2 is array(1..5) of LITTLE_REC2; pragma PACK(LIST2);

type LITTLE3 is range 1..57; for LITTLE3'SIZE use 8;

type LITTLE_REC3 is record A, B, C : LITTLE3; end record; pragma PACK(LITTLE_REC3);

type LIST3 is array(1..5) of LITTLE_REC3; pragma PACK(LIST3);

Page 254: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

begin Put("Type LITTLE1 uses "); Put(LITTLE1'SIZE, 5); Put_Line(" bits for its representation.");

Put("Type LITTLE_REC1 uses "); Put(LITTLE_REC1'SIZE, 5); Put_Line(" bits for its representation.");

Put("Type LIST1 uses "); Put(LIST1'SIZE, 5); Put_Line(" bits for its representation.");

Put("Type LITTLE2 uses "); Put(LITTLE2'SIZE, 5); Put_Line(" bits for its representation.");

Put("Type LITTLE_REC2 uses "); Put(LITTLE_REC2'SIZE, 5); Put_Line(" bits for its representation.");

Put("Type LIST2 uses "); Put(LIST2'SIZE, 5); Put_Line(" bits for its representation.");

Put("Type LITTLE3 uses "); Put(LITTLE3'SIZE, 5); Put_Line(" bits for its representation.");

Put("Type LITTLE_REC3 uses "); Put(LITTLE_REC3'SIZE, 5); Put_Line(" bits for its representation.");

Put("Type LIST3 uses "); Put(LIST3'SIZE, 5); Put_Line(" bits for its representation.");

end PackItIn;

-- Result of execution, Compiler 1. (Did not support Line 28)

-- Type LITTLE1 uses 16 bits for its representation.-- Type LITTLE_REC1 uses 48 bits for its representation.-- Type LIST1 uses 240 bits for its representation.-- Type LITTLE2 uses 16 bits for its representation.-- Type LITTLE_REC2 uses 32 bits for its representation.-- Type LIST2 uses 160 bits for its representation.-- Type LITTLE3 uses 16 bits for its representation.-- Type LITTLE_REC3 uses 32 bits for its representation.-- Type LIST3 uses 160 bits for its representation.

-- Result of execution, Compiler 2. (Did support Line 28)

-- Type LITTLE1 uses 6 bits for its representation.-- Type LITTLE_REC1 uses 24 bits for its representation.-- Type LIST1 uses 120 bits for its representation.-- Type LITTLE2 uses 6 bits for its representation.-- Type LITTLE_REC2 uses 22 bits for its representation.-- Type LIST2 uses 120 bits for its representation.-- Type LITTLE3 uses 8 bits for its representation.-- Type LITTLE_REC3 uses 24 bits for its representation.-- Type LIST3 uses 120 bits for its representation.

Page 255: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Examine the program named e_c32_p3.ada for an example of use of the pragma named PACK. This is an instruction to the compiler to pack the data as tightly as possible with no concern for how long it will take for the resulting program to execute. Three examples of packing are given here, with each resulting in a more tightly packed composite type.

NORMAL PACKING DENSITY

Line 7 contains a declaration of a type which only requires 6 bits to store, but will probably use a full word of 32 bits on most implementations. Lines 9 through 12 declare a record that may require 4 words because of alignment requirements in some compilers, and line 14 may even waste a few more words due to alignment considerations. Lines 40 through 50 are used to output the sizes of these three types for study. The results are given for two Ada compilers used with MS-DOS, running on an IBM-PC type microcomputer.

SOME PACKING TAKES PLACE

In line 16 the same type is repeated with a different name, and is used in the record in lines 18 through 22 where it is packed using the pragma PACK. Note that the packing only takes place at the record level, not at the lower level which is assumed to be the default packing density. In lines 24 and 25, the same array is declared with the pragma PACK used again at this level to achieve a better packing density than the previous example. The results of execution illustrate that compiler 2 did a little better at packing than compiler 1 did.

HIGHER PACKING DENSITY

Lines 27 through 37 illustrate an even higher packing density because of the representation clause in line 28 where we instruct the compiler to use only 8 bits for the type used as elements in the composite types. In this case, neither compiler supported the representation clause, so line 28 had to be commented out resulting in no additional packing density.

WHICH COMPILER IS BEST?

Simply because one compiler did a better packing job does not make it best between the two compared. There is a penalty to be paid here when it comes to executing the code because the data fields are not located in exactly the same places for "normal" or unpacked data fields. The compiler that packed the code very efficiently may take considerably longer to execute a program with data stored in this way than the other. The important thing to remember is that the two compilers, even though both are validated, handled the types slightly differently.

Keep in mind that the pragma named PACK only packs the data at the level at which it is mentioned. It does not pack the data at lower levels unless it is mentioned there also. Three of the five compilers were able to compile this program completely and correctly, except for line 28.

This is one example program that you should definitely compile and execute and do not depend on the results of execution. Your output could be significantly different than either of the two results illustrated.

THE ENUMERATED TYPE REPRESENTATION

Example program ------> e_c32_p4.ada

-- Chapter 32 - Program 4with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, ada.Integer_Text_IO;

procedure EnumRep is

type MOTION is (STOPPED, NORTH, EAST, SOUTH, WEST); for MOTION use (STOPPED => 0,

Page 256: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

NORTH => 1, EAST => 2, SOUTH => 4, WEST => 8);

Robot_Direction : MOTION := STOPPED;

begin

Robot_Direction := MOTION'LAST; -- WEST Robot_Direction := MOTION'PRED(Robot_Direction); -- SOUTH

end EnumRep;

-- Result of execution

-- (There is no output from this program)

The example program named e_c32_p4.ada illustrates the use of the representation clause used with the enumerated type variable to define the values of the enumerated values.

In this case we have declared a type named MOTION for use with some kind of a robot in which we wish to instruct the robot to move any one of four directions or stop. A zero indicates a stopped condition, and the four directions are actually four different bits of a binary code. Assuming that there is a different relay or electronic switch for each direction, we can output a single field to control the four relays or switches. The important thing is that the enumerated value is the pattern we wish to output, so it can be output directly.

The enumerated type will work in exactly the same manner as any other enumerated type. You can take the PRED, SUCC, VAL, or POS, and they will work the same way as if they were declared in order. We have only changed the underlying representation of the enumerated type. The values must be declared in ascending order or a compile error will be issued.

Be sure to compile and execute this program to ascertain that it is acceptable to your compiler. The enumerated representation clause was available with three of the five compilers tested.

THE ADDRESS SPECIFICATION

The address specification is used in such a nebulous way that it is very difficult to even illustrate its use in a general purpose program, so a program is not provided. The general form of its use is given in the next two lines which represents a fragment of a program.

Robot_Port : MOTION; -- The port to control -- direction for Robot_Port use at 16#0175#; -- Absolute address of -- the robot hardware

The first line of this sequence declares a variable named Robot_Port to be of type MOTION which was declared in the example program named e_c32_p4.ada. You will recall that this type was intended to be used to control the robot's direction. The second line tells the Ada compiler that this variable must be assigned the absolute address of 0175 (hexadecimal), because this is where the output port is located which controls the robot's direction. The reserved words for and use at tell the compiler where to locate this particular variable.

It should now be obvious why it is not practical to write a general purpose program to illustrate this concept. The location of a usable port will be different on every computer, and the means of addressing the entire memory space can be quite complex in the case of segmented memory or with

Page 257: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

some sort of memory management scheme. Consult the documentation that came with your compiler to find the method used with your compiler to address an absolute memory location. It will be listed in Annex M of your documentation, as required by the ARM.

UNCHECKED_DEALLOCATION

This is also a very low level routine that is mentioned here for completeness. Its use has been illustrated previously in this tutorial in chapters 13 and 25, where it was used to free up space that had been dynamically allocated.

PROGRAMMING EXERCISES

1. Write a program using Unchecked_Conversion to convert an INTEGER type variable into a CHARACTER array of as long as needed to represent an INTEGER on your system, probably two or four elements. Use the ORD attribute to convert the CHARACTER type data to numerical values and display the numerical value of the components on the monitor. This will identify the underlying representation of the INTEGER and the CHARACTER types.(Solution)

-- Chapter 32 - Programming exercise 1with Ada.Text_IO, Unchecked_Conversion;use Ada.Text_IO;

procedure CH32_1 is

package Int_IO is new Ada.Text_IO.Integer_IO(INTEGER); use Int_IO;

NUMBER_OF_BYTES : constant := 4;

type CHAR_ARRAY is array(1..NUMBER_OF_BYTES) of CHARACTER;

Split_Array : CHAR_ARRAY; Int_Data : INTEGER;

function Switch_To_Bits is new Unchecked_Conversion( Source => INTEGER, Target => CHAR_ARRAY);

begin

for Index in 253..260 loop Int_Data := Index; Split_Array := Switch_To_Bits(Int_Data); Put("Int_Data ="); Put(Int_Data, 6); Put(" Split_Array ="); for Count in 1..NUMBER_OF_BYTES loop Put(CHARACTER'POS(Split_Array(Count)), 4); end loop; New_Line; end loop;

end CH32_1;

-- Result of Execution

-- Int_Data = 253 Split_Array = 253 0 0 0-- Int_Data = 254 Split_Array = 254 0 0 0

Page 258: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Int_Data = 255 Split_Array = 255 0 0 0-- Int_Data = 256 Split_Array = 0 1 0 0-- Int_Data = 257 Split_Array = 1 1 0 0-- Int_Data = 258 Split_Array = 2 1 0 0-- Int_Data = 259 Split_Array = 3 1 0 0-- Int_Data = 260 Split_Array = 4 1 0 0

-- Note that the fields are apparently reversed because of the-- way the bytes are stored in the microprocessor used for this-- example program.

2. Repeat exercise 1 to convert the FLOAT type to an array of CHARACTER.(Solution)

-- Chapter 32 - Programming exercise 2with Ada.Text_IO, Unchecked_Conversion;use Ada.Text_IO;

procedure CH32_2 is

package Int_IO is new Ada.Text_IO.Integer_IO(INTEGER); use Int_IO; package Flt_IO is new Ada.Text_IO.Float_IO(FLOAT); use Flt_IO;

NUMBER_OF_BYTES : constant := 4;

type CHAR_ARRAY is array(1..NUMBER_OF_BYTES) of CHARACTER;

Split_Array : CHAR_ARRAY; Float_Data : FLOAT := 1.0;

function Switch_To_Bits is new Unchecked_Conversion( Source => FLOAT, Target => CHAR_ARRAY);

begin

for Index in 1..8 loop Split_Array := Switch_To_Bits(Float_Data); Put("Float_Data ="); Put(Float_Data, 6, 2, 0); Put(" Split_Array ="); for Count in 1..NUMBER_OF_BYTES loop Put(CHARACTER'POS(Split_Array(Count)), 4); end loop; New_Line; Float_Data := Float_Data * 2.0; end loop;

end CH32_2;

-- Result of Execution

-- Float_Data = 1.00 Split_Array = 0 0 128 63-- Float_Data = 2.00 Split_Array = 0 0 0 64-- Float_Data = 4.00 Split_Array = 0 0 128 64-- Float_Data = 8.00 Split_Array = 0 0 0 65

Page 259: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- Float_Data = 16.00 Split_Array = 0 0 128 65-- Float_Data = 32.00 Split_Array = 0 0 0 66-- Float_Data = 64.00 Split_Array = 0 0 128 66-- Float_Data = 128.00 Split_Array = 0 0 0 67

-- Note that the fields are apparently reversed because of the-- way the bytes are stored in the microprocessor used for this-- example program.

Page 260: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Ada Tutorial - Chapter 33

MORE EXAMPLE PROGRAMS

RANDOM NUMBERS

Example program ------> e_c33_p1.ada

-- Chapter 33 - Program 1-- This is a generic package to generate random numbers in the-- range of 0.00000 to just less than 1.00000 with as many-- significant digits as the type FLOAT_ITEM. This package uses-- the Linear Congruential Method of random number generation as-- discussed in "The Art of Computer Programming" volume 2 by-- Donald Knuth. The method used follows;---- X(n + 1) = (A * X(n) + C) mod M---- X(n + 1) is the new random number-- X(n) is the previous random number or the seed-- M is 1.0 for the floating point system-- A is 7.0 for the floating point system-- C is 13.0 / 31.0 for the floating point system-- X(0) is zero by default-- X(0) is the number provided if forced with Force_Seed-- X(0) is generated as follows when Set_Seed is called;-- 1. The real time clock is read from the system-- 2. The hours and minutes are stripped off-- 3. The resulting number of seconds are divided by-- 60.0 to get the fraction of a minute that has-- elapsed since midnight

generic type FLOAT_ITEM is digits <>;package Random is

-- This procedure uses the system clock to set the seed. procedure Set_Seed;

-- This procedure sets the seed to the input value. procedure Force_Seed(Start_Seed : FLOAT_ITEM);

-- This Function returns the current seed which is also -- the value of the previous random number returned. function Get_Seed return FLOAT_ITEM;

-- This function returns a random number from 0.0 to 1.0 function Random_Number return FLOAT_ITEM;

end Random;

with Ada.Text_IO, Calendar;use Ada.Text_IO, Calendar;

package body Random is

X_initial : FLOAT_ITEM := 0.0;M : FLOAT_ITEM := 1.0;A : FLOAT_ITEM := 7.0;C : FLOAT_ITEM := 13.0 / 31.0;

Page 261: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

procedure Set_Seed is Time_And_Date : TIME; All_Day : DAY_DURATION; Minutes : FLOAT_ITEM; Int_Minutes : INTEGER; Part_Of_A_Minute : FLOAT_ITEM;begin Time_And_Date := Clock; -- Get the time and date All_Day := Seconds(Time_And_Date); -- Seconds since midnight Minutes := FLOAT_ITEM(All_Day) / 60.0; -- Floating type Minutes Int_Minutes := INTEGER(Minutes - 0.5); -- Integer type minutes Part_Of_A_Minute := FLOAT_ITEM(All_Day) - 60.0 * FLOAT_ITEM(Int_Minutes); X_Initial := Part_Of_A_Minute / 60.0;end Set_Seed;

procedure Force_Seed(Start_Seed : FLOAT_ITEM) isTemp : FLOAT_ITEM;Natural_Temp : NATURAL;begin Natural_Temp := NATURAL(Start_Seed - 0.5); -- Subtract 0.5 because -- the type conversion -- rounds the result. Temp := Start_Seed - FLOAT_ITEM(Natural_Temp); X_Initial := Start_Seed;exception when Constraint_Error => Put_Line("Seed out of range, ignored");end Force_Seed;

function Get_Seed return FLOAT_ITEM isbegin return X_Initial;end Get_Seed;

function Random_Number return FLOAT_ITEM is Temp : FLOAT_ITEM; Natural_Temp : NATURAL; -- Cannot exceed (7 + 13/31)begin Temp := A * X_Initial + C; Natural_Temp := NATURAL(Temp - 0.5); -- Subtract 0.5 because -- the type conversion -- rounds the result. Temp := Temp - FLOAT_ITEM(Natural_Temp); X_Initial := Temp; return Temp;end Random_Number;

end Random;

Occasionally when writing a computer program, you will have a need to use a random number generator. Very few compilers have a random number generator as a part of the system, so it is necessary for you to either write one yourself or find one that has been written and debugged by someone else. The example package named e_c33_p1.ada is presented to you for several reasons. The first reason is to provide you with a useful example of a complete generic package to serve as

Page 262: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

an illustration of how to write one. Secondly, it is a useful package that you can use as a part of your own programs when you have a need to use a random number generator. Finally, it is an illustration of good formatting style and it illustrates the inclusion of enough comments in the package specification to completely define the method used, and how to utilize this package as part of another program.

When Ada 83 was upgraded to Ada 95, a random number generator was defined as part of the standard library making it a required component of every Ada 95 compilation system. The random number generator in e_c33_p1.ada was not removed from the tutorial because it is a good example of a well commented Ada utility package. No attempt has been made to prove that it generates truly random numbers, so no claim can be made for it. It would be of benefit to you to study it, then use the random number generator supplied with your compiler for any production work you do,

No other comments need to be given about the operation of this package, so you will be left on your own to study the listing then compile this package in preparation for use with the next example program.

TESTING THE RANDOM NUMBERS

Example program ------> e_c33_p2.ada

-- Chapter 33 - Program 2with Ada.Text_IO, Random;use Ada.Text_IO;

procedure TestRan is

package My_Random is new Random(FLOAT); use My_Random;

package Int_IO is new Ada.Text_IO.Integer_IO(INTEGER); use Int_IO; package Flt_IO is new Ada.Text_IO.Float_IO(FLOAT); use Flt_IO;

SIZE : constant := 100; type MY_ARRAY is array(1..SIZE) of INTEGER; Events : MY_ARRAY; Int_Rand : INTEGER;

begin Set_Seed; Put("The starting value of the seed is "); Put(Get_Seed, 3, 6, 0); New_Line; for Index in 1..12 loop Put("The random number is "); Put(Random_Number, 3, 6, 0); New_Line; end loop;

for Index in 1..SIZE loop Events(Index) := 0; end loop;

for Index in 1..10000 loop Int_Rand := INTEGER(0.5 + (FLOAT(SIZE) * Random_Number)); Events(Int_Rand) := Events(Int_Rand) + 1; end loop; New_Line(2);

for Index in 1..Size loop

Page 263: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Put(Events(Index), 4); end loop;end TestRan;

-- Result of execution

-- The starting value of the seed is 0.195333-- The random number is 0.786685-- The random number is 0.926148-- The random number is 0.902392-- The random number is 0.736096-- The random number is 0.572030-- The random number is 0.423565-- The random number is 0.384310-- The random number is 0.109521-- The random number is 0.186004-- The random number is 0.721385-- The random number is 0.469050-- The random number is 0.702704

-- 109 97 102 96 107 98 85 87 104 111 113 115 108 99 112 ...-- 97 93 107 91 101 85 91 103 95 101 102 98 95 118 110 ...-- 87 106 103 102 93 129 112 94 102 89 95 104 98 94 98 ...-- 93 106 96 104 119 95 82 97 112 82 104 103 97 107 112 ...-- 93 96 101 98 109 95 94 99 80 74 99 85 76 117 124 ...

-- (Note; only fifteen are listed in each row to stay within-- 70 columns.)

Examine the program named e_c33_p2.ada which was written solely to test the random number generator in the package named Random. It instantiates a copy of the generic package using the type FLOAT in line 7, then declares a few objects and an array type. In the executable part of the program the random number generator is initialized with the Set_Seed procedure in line 21, and 12 random numbers are read and printed to see that they do cover the range of 0.0 to 1.0 as defined in the header of the package named Random.

The real test of the random number generator is in the loop beginning in line 35 where ten thousand random numbers are generated and converted into integer type values by multiplying by 100. The integer values will therefore cover a range of 1 to 100, and they are counted in the array named Events. The count in each element of the array should be about 100 since there are ten thousand cases distributed over 100 elements. Execution of the program will reveal that the count in each array element is about 100 as expected, so we declare the random number generator to be at least reasonably random. A mathematician may decide that this method is too crude to be called a good random number generator, but for our purposes it is good enough.

Compile and execute this program, and you will find that each time you run it, you should get different results because it uses the system clock to set the seed, resulting in a new starting seed for each execution.

A NEW DYNAMIC STRING PACKAGE

Example program ------> e_c33_p3.ada

-- Chapter 33 - Program 3-- This is a dynamic string package which can be used as an aid

Page 264: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- in writing string intensive programs. Ada only has a static-- string capability, so this package was written as an example of-- how the Ada programming language can be expanded.

-- A dynamic string is defined as an array of characters of maximum-- length of 255. The dynamic length of the dynamic string is-- stored in the Max_Length field of the record. So the string-- must be defined with a lower limit of 1 and an upper limit of-- whatever the desired maximum length of the string is to be. The-- subtype STRING_SIZE limits the string length when it is defined.

-- Put Outputs a dynamic string to the monitor-- ConCat Concatenates two dynamic strings and puts the result-- into a third dynamic string-- Copy Copies a dynamic string to another dynamic string-- Copy Copies a static string to a dynamic string-- Delete Deletes a group of characters from a dynamic string-- Insert Inserts a group of characters into a dynamic string-- Length Returns the dynamic length of a dynamic string-- Size_Of Returns the static length of a dynamic string-- Pos Returns the first location of a dynamic string within-- another dynamic string

with Ada.Text_IO; use Ada.Text_IO;

package DynStrng is

subtype STRING_SIZE is INTEGER range 0..255; type STRING_ARRAY is array(STRING_SIZE range <>) of CHARACTER;

type DYNAMIC_STRING(Maximum_Length : STRING_SIZE) is record Dynamic_Length : INTEGER range 0..255; String_Data : STRING_ARRAY(1..Maximum_Length); end record;

-- Put : Display a dynamic string on the monitor.procedure Put(Input_String : in DYNAMIC_STRING);

-- ConCat : Concatenation - The First_String is copied into the-- Result_String, then the Second_String is copied-- into the Result_String following the First_String.-- If all characters fit, Result is set to TRUE.-- If Result_String will not hold all characters,-- only as many as will fit are copied and Result-- is set to FALSE.-- Result = TRUE, complete copy done.-- Result = FALSE, some or all not copiedprocedure ConCat(First_String : in DYNAMIC_STRING; Second_String : in DYNAMIC_STRING; Result_String : in out DYNAMIC_STRING; Result : out BOOLEAN);

-- Copy : The String contained in Input_String is copied into-- the string Output_String. This procedure is-- overloaded to include copying from either dynamic-- strings or static strings.-- Result = TRUE, complete copy done-- Result = FALSE, some or all not copied

Page 265: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

procedure Copy(Input_String : in DYNAMIC_STRING; Output_String : in out DYNAMIC_STRING; Result : out BOOLEAN);procedure Copy(Input_String : in STRING; Output_String : out DYNAMIC_STRING; Result : out BOOLEAN);

-- Delete : Beginning at First_Position, as many characters as are-- indicated by Number_Of_Characters are deleted from-- String_To_Modify. If there are not that many, only-- as many as are left are deleted.-- Result = TRUE, deletion was complete-- Result = FALSE, only a partial deletion was doneprocedure Delete(String_To_Modify : in out DYNAMIC_STRING; First_Position : in STRING_SIZE; Number_Of_Characters : in STRING_SIZE; Result : out BOOLEAN);

-- Insert : The string String_To_Insert is inserted into the string-- String_To_Modify begining at location Place_To_Insert-- if there is enough room.-- Result = TRUE, insert completed in full-- Result = FALSE, not enough room for full insertprocedure Insert(String_To_Modify : in out DYNAMIC_STRING; String_To_Insert : in DYNAMIC_STRING; Place_To_Insert : in STRING_SIZE; Result : out BOOLEAN);

-- Length : Returns the dynamic length of the string defined by-- String_To_Measure.function Length(String_To_Measure : in DYNAMIC_STRING) return STRING_SIZE;

-- Size_Of : Returns the static length of the string, the actual-- upper limit of the string definition.function Size_Of(String_To_Measure : in DYNAMIC_STRING) return STRING_SIZE;

-- Pos : Position of substring - The string String_To_Search is-- searched for the string Required_String beginning-- at Place_To_Start.-- Result = TRUE, a search was possible-- Result = FALSE, no search could be made-- Location_Found = 0, no string found-- Location_Found = N, start of matching stringprocedure Pos(String_To_Search : in DYNAMIC_STRING; Required_String : in DYNAMIC_STRING; Place_To_Start : in STRING_SIZE; Location_Found : out STRING_SIZE; Result : out BOOLEAN);

end DynStrng;

Page 266: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

package body DynStrng is

-- The display procedure overloads the existing -- Put procedures to output a dynamic string. Note -- that the existing Put is used in this new Put.procedure Put(Input_String : in DYNAMIC_STRING) isbegin for Index in 1..Input_String.Dynamic_Length loop Put(Input_String.String_Data(Index)); end loop;end Put;

procedure ConCat(First_String : in DYNAMIC_STRING; Second_String : in DYNAMIC_STRING; Result_String : in out DYNAMIC_STRING; Result : out BOOLEAN) isIntermediate_Result : BOOLEAN;Character_Count : STRING_SIZE;Room_Left : STRING_SIZE;begin -- Copy the first into the result string Copy(First_String,Result_String,Intermediate_Result); if Intermediate_Result then Character_Count := Second_String.Dynamic_Length; Room_Left := Result_String.String_Data'LAST - Result_String.Dynamic_Length; Result := TRUE; if Character_Count > Room_Left then Character_Count := Room_Left; Result := FALSE; end if; for Index in 1..Character_Count loop Result_String.String_Data (Index + Result_String.Dynamic_Length) := Second_String.String_Data(Index); end loop; Result_String.Dynamic_Length := Result_String.Dynamic_Length + Character_Count; else Result := FALSE; end if;end ConCat;

-- This procedure overloads the name Copy to -- copy from one dynamic string to another.procedure Copy(Input_String : in DYNAMIC_STRING; Output_String : in out DYNAMIC_STRING; Result : out BOOLEAN) isCharacter_Count : STRING_SIZE;begin -- First pick the smallest string size Character_Count := Input_String.Dynamic_Length; if Character_Count > Output_String.String_Data'LAST then Character_Count := Output_String.String_Data'LAST;

Page 267: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Result := FALSE; -- The entire string didn't fit else Result := TRUE; -- The entire string fit end if;

for Index in 1..Character_Count loop Output_String.String_Data(Index) := Input_String.String_Data(Index); end loop; Output_String.Dynamic_Length := Character_Count;end Copy;

-- This routine overloads the copy procedure name -- to copy a static string into a dynamic string.procedure Copy(Input_String : in STRING; Output_String : out DYNAMIC_STRING; Result : out BOOLEAN) isCharacter_Count : STRING_SIZE;begin -- First pick the smallest string size Character_Count := Input_String'LAST; if Character_Count > Output_String.String_Data'LAST then Character_Count := Output_String.String_Data'LAST; Result := FALSE; -- The entire string didn't fit else Result := TRUE; -- The entire string fit end if;

for Index in 1..Character_Count loop Output_String.String_Data(Index) := Input_String(Index); end loop; Output_String.Dynamic_Length := Character_Count;end Copy;

procedure Delete(String_To_Modify : in out DYNAMIC_STRING; First_Position : in STRING_SIZE; Number_Of_Characters : in STRING_SIZE; Result : out BOOLEAN) isNumber_To_Delete : STRING_SIZE;Number_To_Move : STRING_SIZE;Last_Dynamic_Character : STRING_SIZE := String_To_Modify.Dynamic_Length;begin -- can we delete any at all? if First_Position > Last_Dynamic_Character then Result := FALSE; return; end if; -- Decide how many to delete if (First_Position + Number_Of_Characters) > Last_Dynamic_Character then Number_To_Delete := Last_Dynamic_Character - First_Position + 1; Result := FALSE; else

Page 268: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Number_To_Delete := Number_Of_Characters; Result := TRUE; end if;

-- find out how many to move back if (Last_Dynamic_Character - (First_Position + Number_To_Delete)) >= 0 then Number_To_Move := 1 + Last_Dynamic_Character - (First_Position + Number_To_Delete); else Number_To_Move := 0; end if;

-- now delete the characters by moving them back for Index in First_Position.. (First_Position + Number_To_Move - 1) loop String_To_Modify.String_Data(Index) := String_To_Modify.String_Data(Index + Number_To_Delete); end loop; String_To_Modify.Dynamic_Length := String_To_Modify.Dynamic_Length - Number_To_Delete;end Delete;

procedure Insert(String_To_Modify : in out DYNAMIC_STRING; String_To_Insert : in DYNAMIC_STRING; Place_To_Insert : in STRING_SIZE; Result : out BOOLEAN) isNumber_To_Add : STRING_SIZE;Number_To_Move : STRING_SIZE;Room_Left : STRING_SIZE;begin -- Can we add any at all? if (Place_To_Insert > String_To_Modify.String_Data'LAST) or (Place_To_Insert > String_To_Modify.Dynamic_Length + 1) then Result := FALSE; return; end if; Result := TRUE;

-- How many can we add? Number_To_Add := String_To_Modify.String_Data'LAST - Place_To_Insert + 1; if Number_To_Add > String_To_Insert.Dynamic_Length then Number_To_Add := String_To_Insert.Dynamic_Length; end if;

-- Find how many to move forward Number_To_Move := String_To_Modify.Dynamic_Length - Place_To_Insert + 1; Room_Left := String_To_Modify.String_Data'LAST - Place_To_Insert + 1; if Room_Left < Number_To_Move then Number_To_Move := Room_Left; end if;

-- Move them forward for Index in reverse Place_To_Insert..Place_To_Insert + Number_To_Move - 1 loop String_To_Modify.String_Data(Index + Number_To_Add) :=

Page 269: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

String_To_Modify.String_Data(Index); end loop; for Index in 1..Number_To_Add loop String_To_Modify.String_Data(Index + Place_To_Insert - 1) := String_To_Insert.String_Data(Index); end loop;

-- Increase the length of the string String_To_Modify.Dynamic_Length := String_To_Modify.Dynamic_Length + Number_To_Add; if String_To_Modify.Dynamic_Length > String_To_Modify.String_Data'LAST then String_To_Modify.Dynamic_Length := String_To_Modify.String_Data'LAST; end if;

end Insert;

-- This returns the dynamic length of a stringfunction Length(String_To_Measure : in DYNAMIC_STRING) return STRING_SIZE isbegin return String_To_Measure.Dynamic_Length;end Length;

-- This returns the static length of a stringfunction Size_Of(String_To_Measure : in DYNAMIC_STRING) return STRING_SIZE isbegin return String_To_Measure.String_Data'LAST;end Size_Of;

procedure Pos(String_To_Search : in DYNAMIC_STRING; Required_String : in DYNAMIC_STRING; Place_To_Start : in STRING_SIZE; Location_Found : out STRING_SIZE; Result : out BOOLEAN) isEnd_Search : STRING_SIZE;Characters_All_Compare : BOOLEAN;begin Location_Found := 0; -- can we search the string at all? if (Place_To_Start < String_To_Search.Dynamic_Length) and (Place_To_Start < String_To_Search.String_Data'LAST) then Result := TRUE; else Result := FALSE; return; end if;

-- search the loop for the string now End_Search := String_To_Search.Dynamic_Length - Required_String.Dynamic_Length + 1;

Page 270: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

for Index in Place_To_Start..End_Search loop -- search loop Characters_All_Compare := TRUE; for Count in 1..Required_String.Dynamic_Length loop if Required_String.String_Data(Count) /= String_To_Search.String_Data(Count + Index - 1) then Characters_All_Compare := FALSE; exit; -- exit loop, a character did not match end if; end loop; if Characters_All_Compare then Location_Found := Index; return; -- string match found, return location end if; end loop; -- end search loop

end Pos;

end DynStrng;

Examine the program named e_c16_p3.ada, which is included in part 2 of this tutorial for a better dynamic string package. You will recall that when we studied the dynamic string package in chapter 16 of part 1 of this tutorial, we found a problem when using string constants in the Copy procedure calls. This was because the system found ambiguous procedures. It could not tell if the string constant was of type STRING or of our own declared type which we named DYNAMIC_STRING. Since we had not studied the discriminated record at that time, we could not properly fix the problem. The new DynStrng package, using a discriminated record, is offered as a better package for the problem of using dynamic strings.

The DYNAMIC_STRING type is declared in lines 33 through 37 and is declared as a record this time so there is no confusion as to whether it is a string or a record, and the overloading ambiguity problem is gone. The package specification is essentially unchanged from the last dynamic string package, except for the type of course, but the body is changed considerably to reflect the new data structure. You will be left on your own to compare the bodies of these two packages if you so desire.

THE STRING CONSTANT PROBLEM IS FIXED

Example program ------> e_c33_p4.ada

-- Chapter 33 - Program 4with Ada.Text_IO; use Ada.Text_IO;with DynStrng; use DynStrng;

procedure TestStrn is

package Int_IO is new Ada.Text_IO.Integer_IO(INTEGER); use Int_IO;

Stuff : DYNAMIC_STRING(35); Result : BOOLEAN;

begin

Copy("ABCDEFGHIJKL$", Stuff, Result); Put(Size_Of(Stuff), 4); Put(Length(Stuff), 4); Put(Stuff); New_Line;

Copy("ABCD$", Stuff, Result);

Page 271: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Put(Size_Of(Stuff), 4); Put(Length(Stuff), 4); Put(Stuff); New_Line;

Copy("", Stuff, Result); Put(Size_Of(Stuff), 4); Put(Length(Stuff), 4); Put(Stuff); New_Line;

end TestStrn;

The program named e_c33_p4.ada is designed to test the new package with a few string constants to prove that it really does work as advertised. You can compile and execute this file to see that it really does work with string constants in a Copy procedure call.

Example program ------> e_c16_p4.ada

You can return to the program named e_c16_p4.ada from chapter 16 to prove that the new package still works with this old program. You will find that a couple of changes must be made to reflect the different data type. Lines 11 and 12 must be modified to reflect only the upper limit on the static length of the dynamic string variables. They will read as follows;

Name : DYNAMIC_STRING(15); Stuff : DYNAMIC_STRING(35);

In addition, because the type is changed, lines 21 and 22 must also be modified as follows;

Name.Dynamic_Length := 3; Stuff.Dynamic_Length := 7;

After making these two changes, this program should execute exactly as it did when it used the old dynamic string package.

HOW OLD ARE YOU IN DAYS

Example program ------> e_c33_p5.ada

-- Chapter 33 - Program 5-- This program will calculate the number of days old you are.-- It is a rather dumb program, but illustrates some interesting-- programming techniques. It checks all input to see that they-- are in the correct range before continuing. Since the number-- of days can easily exceed the limits of type INTEGER, and we-- cannot count on LONG_INTEGER being available, a fixed point-- variable is used for the total number of days since Jan 1, 1880.-- This program also passes a record to a procedure, where it is-- modified and returned.

-- This is a repeat of the program named AGE.ADA from chapter 16-- of this tutorial. This program uses the CALENDAR package for-- the current date so the user does not have to enter today's-- date. It also uses some of the subtypes from the CALENDAR-- package, but not the YEAR_NUMBER, because it does not follow-- our desired range.

with Ada.Text_IO, Calendar;use Ada.Text_IO, Calendar;

procedure Age2 is

Page 272: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

LOW_YEAR : constant := 1880; MAX : constant := 365.0 * (2100 - LOW_YEAR);

type AGES is delta 1.0 range -MAX..MAX;

Days_Since_1880 : AGES; Present_Age : AGES;

Today : TIME; -- Present date and time This_Month : MONTH_NUMBER; This_Day : DAY_NUMBER; This_Year : INTEGER range LOW_YEAR..2100; Seconds : DAY_DURATION;

type DATE is record Month : MONTH_NUMBER; Day : DAY_NUMBER; Year : INTEGER range LOW_YEAR..2100; Days : AGES; end record;

Birth_Day : DATE;

package Int_IO is new Ada.Text_IO.Integer_IO(INTEGER); use Int_IO; package Fix_IO is new Ada.Text_IO.Fixed_IO(AGES); use Fix_IO;

procedure Get_Date(Date_To_Get : in out DATE) is Temp : INTEGER; begin Put(" month --> "); loop Get(Temp); if Temp in 1..12 then Date_To_Get.Month := Temp; exit; -- month OK else Put_Line(" Month must be in the range of 1 to 12"); Put(" "); Put(" month --> "); end if; end loop;

Put(" "); Put(" day ----> "); loop Get(Temp); if Temp in 1..31 then Date_To_Get.Day := Temp; exit; -- day OK else Put_Line(" Day must be in the range of 1 to 31"); Put(" "); Put(" day ----> "); end if; end loop;

Put(" "); Put(" year ---> ");

Page 273: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

loop Get(Temp); if Temp in LOW_YEAR..2100 then Date_To_Get.Year := Temp; exit; -- year OK else Put_Line(" Year must be in the range of 1880 to 2100"); Put(" "); Put(" year ---> "); end if; end loop; Date_To_Get.Days := 365 * AGES(Date_To_Get.Year - LOW_YEAR) + AGES(31 * Date_To_Get.Month + Date_To_Get.Day);

end Get_Date;

begin -- Get todays date Today := Clock; Split(Today, This_Year, This_Month, This_Day, Seconds); Days_Since_1880 := 365 * AGES(This_Year - LOW_YEAR) + AGES(31 * This_Month + This_Day);

Put("Enter your birthday;"); Get_Date(Birth_Day); New_Line(2);

Present_Age := Days_since_1880 - Birth_Day.Days; if Present_Age < 0.0 then Put("You will be born in "); Present_Age := abs(Present_Age); Put(Present_Age,6,0,0); Put_Line(" days."); elsif Present_Age = 0.0 then Put_Line("Happy birthday, you were just born today."); else Put("You are now "); Put(Present_Age,6,0,0); Put_Line(" days old."); end if;

end Age2;

This program is a repeat of the program given in chapter 16, but it is improved somewhat here. Since we now know how to use the Calendar package, we can use it to get today's date for us, and we do this in the new program named e_c33_p5.ada. Notice especially the way the data is read in and checked for validity before continuing on. If the data were read into the corresponding variables, an invalid entry would cause an exception, but since the data is read into an INTEGER type variable with a wide range, it can be checked for validity before being assigned to the correct variable with a much smaller range. The program should be very simple for you to understand, but it would be good for you to spend a little time studying it before compiling and executing it.

THE DINING PHILOSOPHERS

Example program ------> e_c33_p6.ada

-- Chapter 33 - Program 6

Page 274: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

package One_Man is

type AVAILABILITY is (AVAILABLE, IN_USE); Fork_Usage : array(1..5) of AVAILABILITY;

type ACTIVITY is (THINKING, HAS_LEFT_FORK, HAS_BOTH_FORKS); Philosopher_Activity : array(1..5) of ACTIVITY;

task type EATING_OR_THINKING is entry Start(Left_Fork, Right_Fork : INTEGER); end EATING_OR_THINKING;

end One_Man;

with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Calendar, Random;use Ada.Text_IO, Ada.Integer_Text_IO;

package body One_Man is

package My_Random is new Random(FLOAT); use My_Random;

procedure Get_Fork(Identifier : INTEGER; Left_Or_Right : INTEGER) is begin Fork_Usage(Identifier) := IN_USE; end Get_Fork;

procedure Return_Fork(Identifier : INTEGER) is begin Fork_Usage(Identifier) := AVAILABLE; end Return_Fork;

task body EATING_OR_THINKING is Left, Right : INTEGER; Ident : INTEGER renames Left; begin accept Start(Left_Fork, Right_Fork : INTEGER) do Left := Left_Fork; Right := Right_Fork; Philosopher_Activity(Ident) := THINKING; end Start; loop Put("Philosopher"); Put(Ident, 2); Put_Line(" is thinking."); delay Calendar.DAY_DURATION(Random_Number); loop delay 0.10; exit when Fork_Usage(Left) = AVAILABLE; end loop; Get_Fork(Ident, Left); Philosopher_Activity(Ident) := HAS_LEFT_FORK; Put("Philosopher"); Put(Ident, 2); Put_Line(" has his left fork"); delay Calendar.DAY_DURATION(Random_Number); loop

Page 275: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

delay 0.10; exit when Fork_Usage(Right) = AVAILABLE; end loop; Get_Fork(Ident,Right); Philosopher_Activity(Ident) := HAS_BOTH_FORKS; Put("Philosopher"); Put(Ident, 2); Put_Line(" has his right fork and is eating"); delay Calendar.DAY_DURATION(Random_Number); Return_Fork(Left); Return_Fork(Right); Philosopher_Activity(Ident) := THINKING; end loop; end EATING_OR_THINKING;

begin

Set_Seed; -- Initialize the random number generator

for Index in 1.. 5 loop Fork_Usage(Index) := AVAILABLE; Philosopher_Activity(Index) := THINKING; end loop;

end One_Man;

with Ada.Text_IO, One_Man;use Ada.Text_IO, One_Man;

procedure Philos is

-- Declare all 5 tasks Philosopher_1 : One_Man.EATING_OR_THINKING; Philosopher_2 : One_Man.EATING_OR_THINKING; Philosopher_3 : One_Man.EATING_OR_THINKING; Philosopher_4 : One_Man.EATING_OR_THINKING; Philosopher_5 : One_Man.EATING_OR_THINKING;

begin

-- Assign forks to Philosophers & start Philosopher_1.Start(Left_Fork => 1, Right_Fork => 2); Philosopher_2.Start(Left_Fork => 2, Right_Fork => 3); Philosopher_3.Start(Left_Fork => 3, Right_Fork => 4); Philosopher_4.Start(Left_Fork => 4, Right_Fork => 5); Philosopher_5.Start(Left_Fork => 5, Right_Fork => 1);

loop -- Watch for deadlock to occur delay 0.01; if Philosopher_Activity(1) = HAS_LEFT_FORK and Philosopher_Activity(2) = HAS_LEFT_FORK and Philosopher_Activity(3) = HAS_LEFT_FORK and Philosopher_Activity(4) = HAS_LEFT_FORK and Philosopher_Activity(5) = HAS_LEFT_FORK then exit; end if; end loop;

Put_Line("Deadlock detected, program operation aborted.");

Page 276: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

abort Philosopher_1, Philosopher_2, Philosopher_3, Philosopher_4, Philosopher_5;

end Philos;

Most books and articles on tasking or concurrency at least mention the problem of the dining philosophers, so it would not be good to leave this tutorial without a little discussion of this problem. In fact, the program named e_c33_p6.ada is a program you can study and execute to see this problem illustrated.

The problem is stated that five philosophers sit down to eat. They like to eat for awhile then think for awhile and repeat the pattern forever. In order to eat, they require a fork in both their left and their right hands, and the table is set with a fork on each side of their plates. The problem occurs when we state that there is only one fork between each adjacent philosopher, and he is therefore required to share each fork with his adjacent colleague.

Each philosopher sits down, waits a random length of time, then picks up the fork on his left, waits another random length of time, and picks up the fork on his right. He then proceeds to eat for a random length of time, then returns both forks to the table and thinks for a random length of time. Once he picks up the left fork, he stubbornly hangs on to it until he gets the right fork. If we ever reach the condition where each philosopher has his left fork, then none will ever return it and none can ever therefore pick up his right fork. The entire system is said to be deadlocked because nothing else will ever be accomplished. All five of the uncooperative philosophers will eventually starve since none can eat.

THE PROGRAM ILLUSTRATES DEADLOCK

The program uses a package to define a task type for one philosopher with the required delays and the logic to acquire each fork, eat, then return the forks to the table. Following our study of tasking, you should have no problem understanding the logic presented here.

The main program named Philos, which starts in line 93 of the file simply declares the five philosophers, starts them through their cycles in lines 108 through 112, then loops while it is watching for deadlock. When deadlock is detected, a message is output to the monitor, and the entire system is aborted.

Compile and execute this program, so you can observe deadlock occurring and the system aborting operation. If you run it several times, you will see that quite often deadlock occurs immediately, but at other times it will run for several seconds before deadlock is detected.

This is an interesting problem, but the more interesting point is the fact that this program, which begins in line 93 uses Ada.Text_IO and One_Man, and One_Man in turn uses Ada.Text_IO, Ada.Calendar, and Random. This program uses quite a bit of the resources available in Ada, and uses several packages to accomplish its intended mission. Since we are using Random, which was developed for an earlier project, we are actually illustrating the reusability of Ada software.

THE GENERIC STACK

Example program ------> e_c33_p7.ada

-- Chapter 33 - Program 7generic type ITEM is private;package GenStack is

procedure Push(In_Item : in ITEM); -- In_Item is added to the -- stack if there is room.

procedure Pop(Out_Item : out ITEM); -- Out_Item is removed from

Page 277: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

-- stack and returned if a -- character is on stack. -- else a blank is returned

function Is_Empty return BOOLEAN; -- TRUE if stack is empty

function Is_Full return BOOLEAN; -- TRUE if stack is full

function Current_Stack_Size return INTEGER;

procedure Clear_Stack; -- Reset the stack to empty

end GenStack;

package body GenStack is

Maximum_Size : constant := 25;type ITEM_ARRAY is array(1..Maximum_Size) of ITEM;Stack_List : ITEM_ARRAY; -- The stack itself, purposely -- defined very small.Top_Of_Stack : INTEGER := 0; -- This will always point to -- the top entry on the stack.

procedure Push(In_Item : in ITEM) isbegin if not Is_Full then Top_Of_Stack := Top_Of_Stack + 1; Stack_List(Top_Of_Stack) := In_Item; end if;end Push;

procedure Pop(Out_Item : out ITEM) isbegin if Is_Empty then null; -- Nothing to return else Out_Item := Stack_List(Top_Of_Stack); Top_Of_Stack := Top_Of_Stack - 1; end if;end Pop;

function Is_Empty return BOOLEAN isbegin return Top_Of_Stack = 0;end Is_Empty;

function Is_Full return BOOLEAN isbegin return Top_Of_Stack = Maximum_Size;end Is_Full;

function Current_Stack_Size return INTEGER isbegin return Top_Of_Stack;

Page 278: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

end Current_Stack_Size;

procedure Clear_Stack isbegin Top_Of_Stack := 0;end Clear_Stack;

end GenStack;

We promised we would include a generic stack when we studied the character stack in chapter 16, and e_c33_p7.ada is a generic stack to fulfill that promise. It is really only a copy of e_c16_p1.ada from chapter 16 made into a generic package according to the rules studied in this tutorial.

Example program ------> e_c33_p8.ada

-- Chapter 33 - Program 8with Ada.Text_IO, Ada.Integer_Text_IO;use Ada.Text_IO, Ada.Integer_Text_IO;with GenStack;

procedure TryStak is

package Char_Stack is new GenStack(CHARACTER); use Char_Stack;

Example : constant STRING := "This is the first test."; Another : constant STRING := "This is another test and this should not fit.";

procedure Fill_The_Stack(Input_Line : STRING) is begin Clear_Stack; for Index in 1..Input_Line'LAST loop if Is_Full then Put_Line("The stack is full, no more added."); exit; else Push(Input_Line(Index)); end if; end loop; end Fill_The_Stack;

procedure Empty_The_Stack is Char : CHARACTER; begin loop if Is_Empty then New_Line; Put_Line("The stack is empty."); exit; else Pop(Char); Put(Char); end if; end loop; end Empty_The_Stack;

begin

Put_Line(Example);

Page 279: ADA95 Tutorial Part 2 - Aix-Marseille Universityfrancois.touchard.perso.luminy.univ-amu.fr › ... › ada › AdaTutorialP2.… · significant programs in Ada, but completion of

Fill_The_Stack(Example); Empty_The_Stack;

New_Line; Put_Line(Another); Fill_The_Stack(Another); Empty_The_Stack;

end TryStak;

-- Result of execution

-- This is the first test.-- .tset tsrif eht si sihT-- The stack is empty.---- This is another test and this should not fit.-- The stack is full, no more added.-- dna tset rehtona si sihT-- The stack is empty.

The program e_c16_p2.ada is nearly identical to the program of the same name from chapter 16, the only difference being the instantiation of the generic package so it can be used in the main program.