160 likes | 313 Views
Even More Ada Constructs 9 Oct. 2002. Today’s Constructs. Generics Private types Child library units Tasking Pragmas Elaboration. Generics. Generic units are used to make Ada’s strong types less painful Possible to create general unit once, but use it for many different declared types
E N D
Today’s Constructs • Generics • Private types • Child library units • Tasking • Pragmas • Elaboration
Generics • Generic units are used to make Ada’s strong types less painful • Possible to create general unit once, but use it for many different declared types • Has types, variables, subprograms, and packages as parameters • When each instance is made of a generic unit, it might be a separate copy, or it might share code
Generic Example* generic type My_Float is digits <>; package Generic_Complex_Numbers is type Complex is private; . . . function “+” (X, Y: Complex) return Complex; . . . end; generic type index is (<>); with package Complex_Numbers is new Generic_Complex_Numbers (<>); Package Generic_Complex_Vectors is use Complex_Numbers; type Vector is array (Index range <>) of Complex; . . . end;
Private Types • Private types allow designers to hide information (sort of) • Why is that good? • Coupling is bad; Cohesion is good • Coupling: Complex systems need simple boundaries among its constituent parts; otherwise, a change in one place can affect many others, even those that don’t need to be affected • Cohesion: Put everything having to do with some aspect of the system in a single place; that makes it easy to make changes, easy to understand the implementation, and easy to find things • Items declared in the private part of a package specification can not be used anywhere else except • In the package itself • In child packages Taken from “Programming in Ada95”, 2nd Edition, John Barnes
Private Parts Example with MATHEMATICS_EXCEPTIONS; with GENERIC_LINEAR_ALGEBRA_MATHEMATICS; generic type FLOAT_TYPE is digits <>; type INDEX_TYPE is (<>); type COLUMN_VECTOR_TYPE is array(INDEX_TYPE range <>) of FLOAT_TYPE; type MATRIX_TYPE is array(INDEX_TYPE range <>, INDEX_TYPE range <>) of FLOAT_TYPE; package GENERIC_QUATERNION_MATHEMATICS is type QUATERNION is private; type TRANSPOSE_SUPERSCRIPT_TYPE is (T); function SCALAR_PART_OF(X : QUATERNION) return FLOAT_TYPE; function QUATERNION_FROM_PARTS(SCALAR_PART : FLOAT_TYPE; VECTOR_PART : COLUMN_VECTOR_TYPE) return QUATERNION; function "**" (LEFT : QUATERNION; RIGHT : TRANSPOSE_SUPERSCRIPT_TYPE) return QUATERNION; ... private ... – Define Scalar_Type and Vector_Type type QUATERNION is record SCALAR : SCALAR_TYPE; VECTOR : VECTOR_TYPE; end record; end GENERIC_LINEAR_ALGEBRA_MATHEMATICS;
Operations Available for Private Types • The only operations available to users of private types are • Assignment • Test for equality and inequality (i.e. if X = Y) • Procedures and functions that have an argument of the type • Attributes defined for all types (such as Size) • Limited types are even more restrictive • Assignment, equality tests not defined • Note that assignment cannot be redefined, so it is never possible to use assignment for limited types type My_Limited_type is limited private;
Child Library Units • Suppose you have a nice, cohesive package, with private types • Suppose you want to create another package that needs access to those private types, but is not quite relevant enough to put in the currently existing package package Complex_Number_Pack is type Complex_type is private function Construct (R, I, My_Float) return Complex_type; ... private ... end Complex_Number_pack; • Now, you want to create a set of functions to work with polar coordinates package Complex_Number_Pack.Polar is type Angle_type is digits 6 range 0.0 .. 360.0; function Construct (R : My_Float, Theta : Angle_type) return Complex; ... end Complex_Number_Pack.Polar; Then, if you want to use either polar or Cartesian versions, simply use with Complex_Number_Pack.Polar; -- Get both packages
Tasking • Multitasking (what UNIX people call Threads) is built in to the Ada language • Model is parallel activities • Easily implemented to use multiple processors • Implementation can use any scheduling mechanism • Time slicing, where each task (of the same priority) gets a bit of time, then the next task runs • One task runs until it hits a blocking point, then the next task (of the same priority or lower) runs • Priority can be used (if supported) to force the system to run tasks in a certain order (if running on a single processor system)
Rendezvous Example with TASKING_PRIORITY; package FIVE_HZ_TASKING is FIVE_HZ_TASK_SIZE : integer := 30_000; task type FIVE_HZ_TASK_TYPE is -- start execution of cyclic iteration entry START_EXECUTION; pragma PRIORITY(TASKING_PRIORITY.FIVE_HZ_PRIORITY); pragma Storage_Size (FIVE_HZ_TASK_SIZE); end FIVE_HZ_TASK_TYPE; FIVE_HZ_TASK : FIVE_HZ_TASK_TYPE; end FIVE_HZ_TASKING;
Task Body package body Five_Hz_Tasking is task body Five_Hz_Task_type is ... -- Local declarations begin ... -- Initialize the task accept Start_Execution; -- Wait for signal to start loop ... -- Perform cyclic processing end loop; end Five_Hz_Tasking; • Calling a task entry is just like calling a procedure Five_Hz_Task.Start_Execution;
Protected Object Example package Variable is type My_Type is range 0 .. 9_000_000; protected P_Variable is function Read return My_Type; -- Many callers procedure Write ( -- One caller X : in My_Type ); private V : My_Type := 0; end P_Variable; end Variable; • Any number of tasks may call protected functions at the same time • Protected functions can not write to protected objects • Not possible for a task to call Write while another calls Read (the second will block)
Pragmas • Pragmas are compiler directives • Many defined; few should be needed for project • Pragmas Elaborate, Elaborate_All, Elaborate_Body might be needed • Example is pragma Priority, which sets the priority of a task • Another example is pragma Pack, which tells the compiler not to leave gaps between elements of an array or record
Elaboration • Ada programs perform work before the first executable statement after the “begin” of the main procedure is run • “Elaboration” code is executed first • Gives data initial values (which might call functions) • Creates objects (if dynamic) • Runs package initialization code • To be completely precise, the whole program is one big elaboration • Package specifications are elaborated before their bodies • Package bodies are elaborated before any of their subprograms can be called • Package bodies can have task bodies, which start running after the package body has been elaborated • When all packages have been elaborated, the main procedure runs
Elaboration Example with Ada.Text_IO; procedure Elab_Test is package Random_Pack is -- Elaboration of this is first type My_Type is digits 6 range 0.0 .. 1.0; function Random return My_Type; end Random_Pack; package body Random_Pack is -- Elaboration of this is second Seed : My_Type; function Random return My_Type is begin return Seed; -- For now, return a constant end Random; task My_Task; task body My_Task is X : My_Type := Random; begin Ada.Text_IO.Put ("Made it"); end My_Task; begin Seed := 0.5; -- Need this before Random can be called end Random_Pack; -- After last statement after "begin", My_Task can run begin null; end Elab_Test;
Watch Out for Circular Dependencies package My_Pack is type My_type is range 1_000 .. 1_000_000; procedure Do_Something (Parameter : in out My_type); end My_Pack; package Other_Pack is type Other_type is range 0 .. 1_000; function Do_Something_Else return Other_type; end Other_Pack; with Other_Pack; pragma Elaborate (Other_Pack); package body My_Pack is X : Other_Pack.Other_type := Do_Something_Else; -- Needs Other_Pack body elab ... end My_Pack; with My_Pack; package body Other_Pack is X : My_Pack.My_type; ... end Other_Pack;