• No se han encontrado resultados

CUADRO DE DISTRIBUCIÓN DEL PERSONAL Y ÁREA DE TRABAJO DE LA EMPRESA ZOPIC S.A.

1.6.4. Descripción de las actividades de producción

§3.9.2

2 A call on a dispatching operation is a call whose name or prefix denotes the decla- ration of a primitive subprogram of a tagged type, that is, a dispatching operation. Acontrolling operandin a call on a dispatching operation of a tagged typeTis one whose corresponding formal parameter is of typeT. . . ; the corresponding formal parameter is called acontrolling formal parameter. . . .

20 For the execution of a call on a dispatching operation, the body executed is the one for the corresponding primitive subprogram of the specific type identified by the controlling tag value. . . .

We wrote the dispatching callRoot_Event.Simulateas anexpanded name§4.1.3(4) rather than depending on the ‘use’-clause. This emphasizes that to dispatch with a controlling operand of a class-typeRoot_Event.Event’Class, you (syntactically) call the primitive operation of the corre- sponding tagged typeRoot_Event.Event§3.9.2(2). Similarly, given a controlling operand of type

Root_Event.Engine.Engine_Event’Class, the callRoot_Event.Engine.Simulatewould dispatch according to the specific engine type. Note that the function

function "<"(Left, Right: Event’Class)returnBoolean;

isnotdispatching. If aformalparameter is of class-wide type, the actual parameter may be of any type within the class. The function is not primitive, cannot be overridden and does not dispatch. To summarize:

• The primitive operations of a tagged type are dispatching.

• Formal parameters of the tagged type are controlling.

• If a dispatching operation is called with controlling operands (actual parameters) of aspecific

type, the call is bound at compile-time.

• If a dispatching operation is called with controlling operands (actual parameters) of aclass-wide

type, the call is dispatched at run-time to the subprogram whose controlling (formal) parameter matches thetagof the operand.

6.7

Encapsulation and child packages

There are no particular encapsulation requirements for tagged types (except that primitive op- erations be declared in the same package specification). The following specification compiles correctly, and the resulting rocket simulation needs only minor modifications that are due to the change in the package name.

- - File: ROCKETC

1 - -

2 - - Discrete event simulation of a rocket.

3 - - Child packages are not used.

4 - -

5 packageEvent_Packageis

6.7 Encapsulation and child packages 85

7 type Eventis abstract tagged

8 record

9 Time: Simulation_Time; - - Common component of all events

10 end record;

11 functionCreate returnEventis abstract;

12 procedure Simulate(E:in Event) is abstract;

13 function"<"(Left, Right: Event’Class) returnBoolean;

14

15 type Engine_Eventis new Eventwith

16 record

17 Fuel, Oxygen: Natural;

18 end record;

19 functionCreate returnEngine_Event;

20 procedure Simulate(E:in Engine_Event); 21

22 type Main_Engine_Eventis new Engine_Eventwith

23 null record;

24 functionCreate returnMain_Engine_Event;

25

26 type Aux_Engine_IDis(Left, Right);

27 type Aux_Engine_Eventis new Engine_Eventwith

28 record

29 Side: Aux_Engine_ID;

30 end record;

31 functionCreate returnAux_Engine_Event;

32 procedure Simulate(E:in Aux_Engine_Event);

33

34 type Commandsis(Roll, Pitch, Yaw);

35 subtypeDegreesis Integerrange -90 .. 90;

36 type Steering_Eventis new Event with

37 record

38 Command: Commands;

39 Degree: Degrees;

40 end record;

41 functionCreate returnSteering_Event;

42 procedure Simulate(E:in Steering_Event);

43

44 type Subsystemsis(Engines, Guidance, Communications);

45 type Statesis (OK, Failed);

46 type Telemetry_Eventis new Event with

47 record

48 ID: Subsystems;

49 Status: States;

50 end record;

6.7 Encapsulation and child packages 86

52 procedure Simulate(E:in Telemetry_Event);

53 endEvent_Package;

Recall that in previous simulation program, the dispatching call was to Root_Event.Simulate. Here, all versions of Simulate are declared in the same package Event_Package. The type of the actual parameter in the call to Simulate ‡69is Event’Class, so the call is dispatched to the appropriate version ofSimulatein the derivation classfor the typeEvent.

54 withEvent_Queue;

55 withEvent_Package; use Event_Package;

56 procedure RocketCis

57 Q: Event_Queue.Queue_Ptr :=newEvent_Queue.Queue;

58 begin 59 forIin 1..15loop 60 Event_Queue.Put(Main_Engine_Event’(Create), Q.all); 61 Event_Queue.Put(Aux_Engine_Event’(Create), Q.all); 62 Event_Queue.Put(Telemetry_Event’(Create), Q.all); 63 Event_Queue.Put(Steering_Event’(Create), Q.all); 64 end loop; 65

66 - - Get event from queue and dispatch to Simulate procedure.

67 while not Event_Queue.Empty(Q.all)loop

68 Simulate(Event_Queue.Get(Q));

69 end loop;

70 endRocketC;

This encapsulation of the entire class of types in a single package is legal, though almost certainly not the optimal solution, because too many types are contained in one module and a modification of any one of them will cause too much recompilation and testing. However, it does emphasize that in Ada, decisions relating to encapsulation (packages) areindependentof decisions relating to derivation classes of types. This point is worth emphasizing because many languages for object- oriented programming identify a type with its encapsulation in a ‘class’.

The limitation that a client can only see the visible part of a package is too inflexible for some applications. Consider the declaration ofRoot_Event.Event: on one hand we wish to restrict the accessibility of the implementation of the type (the componentTime), while on the other hand, derived types should be able to access this component because computations withinSimulatemay be time-dependent.

The solution is to usechild packagesto form asubsystemof packages §10.1(9). (Libraryproce- duresmay have child procedures §6.1(4,7).) Packages within the subsystem share abstractions by granting child packages visibility of the private parts of their specifications. Child packages are denoted syntactically by concatenating the child name to the parent’s name using dotted notation. The hierarchy of descendants may be carried to any depth.

The accessibility rules are determined by assuming that a child package is declared after the par- ent’s specification, but before its body (Figure 6.5). The figure is intended to show that since the children come between the specification and the body, they are can access the private part but

6.7 Encapsulation and child packages 87 package body Root_Event package Root_Event. Engine package Root_Event. Steering package Root_Event. Telemetry private package Root_Event. Random_Time package Root_Event

Figure 6.5: Child packages

not the body. Any package can ‘with’ a child package; for example, sibling packages such as

Root_Event.Steering‘with’Root_Event.Random_Time. The parent body has no special privi- leges and must also ‘with’ the child if it needs to. Note that ‘with’ing a child package automatically ‘with’s the parent package §10.1.2(6).

§8.1

7 The declarative region includes the text of the construct together with additional text determined (recursively), as follows:

9 If the declaration of a library unit . . . is included, so are the declarations of any child units . . . . The child declarations occur after the declaration.

16 The children of a parent library unit are inside the parent’s declarative region, even though they do not occur inside the parent’s declaration or body. This implies that one can use (for example) "P.Q" to refer to a child of P whose defining name is Q, and that after "useP;" Q can refer (directly) to that child.

In the main subprogram of the simulation, we ‘with’ all the child packages: with Event_Queue;

with Root_Event.Engine, Root_Event.Telemetry, Root_Event.Steering;

use Root_Event;

procedure Rocket is

. . .

endRocket;

The context clause containsuseRoot_Event, so we can directly refer to the child package (for exampleTelemetry.Create ‡300) as noted in §8.1(16) above.

Since child packages have access to the private parts of its ancestors, we must prevent exportation of declarations from the private part:

package Root_Event.Exportis

subtypeExport_Timeis Simulation_Time; - - Error!

endRoot_Event.Export;

There is a special rule that excludes the visible part of a child package specification from accessing the private part of its parent.

6.7 Encapsulation and child packages 88

§8.2

4 The immediate scope of a declaration in the private part of a library unit does not include the visible part of any public descendant of that library unit.

Consider, however, the random number generator package Root_Event.Random_Time. The package is declared as a generic instantiation of a package from the standard libraries, but in the context of our simulation program, an equivalent specification is as follows:

private packageRoot_Event.Random_Time is type Generatoris limited private;

function Random (Gen: Generator)returnSimulation_Time;

procedure Reset (Gen: in Generator);

private

. . .

endRoot_Event.Random_Time;

The function Random in the visible part of the specification returns a value of type Simula- tion_Time that is declared in the private part of its parent, in effect exporting the type. This is not normally acceptable, becauseSimulation_Timewas intentionally made private to prevent its exportation from the simulation subsystem.

Ada defines two types of child packages: publicandprivate. The rule in §8.2(4) holds only for public children; the visible part of aprivatechildisallowed access to the private part of a parent; however, to prevent unwanted exportation, a client of a private child must be within the family that already has access to the private part.

§10.1.2

8 If a with_clause of a given compilation_unit mentions a private child of some library unit, then the given compilation_unitshall be either the declaration of a private descendant of that library unit or the body or subunit of a (public or private) descendant of that library unit.

In the simulation,Root_Event.Random_Timeis declared to be a private child and is used only within the bodies of the packages rooted atRoot_Event.

Freezing*

Could we rearrange the declarations inEvent_Packageso that all the type derivations are declared before the primitive operations?

package Event_Packageis

type Eventis abstract tagged . . .

type Engine_Eventis new Eventwith . . .

type Main_Engine_Event is newEngine_Eventwith . . .

type Steering_Event is new Eventwith . . .

type Telemetry_Event is newEvent with . . .

procedure Simulate(E: in Event)is abstract;

procedure Simulate(E: in Engine_Event);

6.8 Type conversion* 89

procedure Simulate(E: in Steering_Event);

procedure Simulate(E: in Telemetry_Event);

endEvent_Package;

The answer is no. For reasons that will become clear in Section 6.11, implementing extension requires that the entire set of primitive operations for a type be known when the type is extended. Thus the declarations of primitive operations must be ‘close to’ the declaration of the tagged type or extension. The rule is expressed in terms of a concept calledfreezing§13.14: once an entity is frozen, any declaration that would change its representation is forbidden.

§13.14

7 The declaration of a record extension causes freezing of the parent subtype.

16 The explicit declaration of a primitive subprogram of a tagged type shall occur before the type is frozen (see 3.9.2).

6.8

Type conversion*

A value of a type in a derivation class can be converted to a value of another type in the class subject to the following rule.

§4.6

21 . . . if the target type is tagged, then either:

22 The operand type shall be covered by or descended from the target type; or

23 The operand type shall be a class-wide type that covers the target type.

This rule can be easily understood by examining Figure 6.2. We can convert a value of type

Aux_Engineto a value of typeEngineor to a value of typeEventsimply by ignoring the extra components. However, we cannot convert a value of typeEnginetoAux_Engine, because it has noSidecomponent. Similarly, a specific type can be converted to a class-wide type thatcoversit §3.4.1(9).

Consider now converting a class-wide type to a specific type:

E_CL:Event’Class := . . . ; Eng: Engine := Engine(E_CL);

E_CLcontains a value of some specific type withinEvent’Class. If we are ‘lucky’ and the value of

E_CLis in fact of typeEngine(orMain_EngineorAux_Engine), the conversion will succeed; otherwise, the conversion will fail and raiseConstraint_Error§4.6(42,57). You would not do such a conversion unless you have reason to believe that the conversion will succeed. Alternatively, you can use a membership test §4.5.2(30) to check the type of the class-wide value at run-time:

ifE_CL inEngine’Class then

Eng := Engine(E_CL); - - OK

else

. . . - - Do something else

6.8 Type conversion* 90

Extension aggregates

Though a value of a parent type cannot be converted to a value of a type derived from it, it is possible to create a value of the derived type by supplying the additional components that are ‘missing’ from the parent type.

§4.3.2

1 Anextension_aggregatespecifies a value for a type that is a record extension by specifying a value or subtype for an ancestor of the type, followed by associations for any components not determined by theancestor_part.

2 extension_aggregate ::=

(ancestor_partwith record_component_association_list) 3 ancestor_part ::= expression | subtype_mark

6 For therecord_component_association_listof anextension_aggregate, the only componentsneededare those of the composite value defined by the aggregate that are not inherited from the type of theancestor_part, . . .

7 For the evaluation of an extension_aggregate, the record_component_associ- ation_listis evaluated. If theancestor_partis anexpression, it is also evaluated; if theancestor_partis asubtype_mark, the components of the value of the aggregate not given by therecord_component_association_listare initialized by default as for an object of the ancestor type. . . .

9 If all components of the value of theextension_aggregate are determined by the

ancestor_part, then the record_component_association_list is required to be simplynull record.

10 If theancestor_partis asubtype_mark, then its type can be abstract. . . . Here are two examples based on expressions from the case study:

(Engine_Event’(Create)with Left);

- - Extend Engine_Event to create Aux_Engine_Event aggregate (Engine_Event’(Create)with null record);

- - Extend Engine_Event to create Main_Engine_Event aggregate

Even thoughMain_Engine_Eventdoes not add any components during the extension ofEngine_- Event, it is still derived fromMain_Engine_Eventand an extension aggregate must be used with null record.

Extension aggregates built from subtype marks are intended to be used when the ancestor is ab- stract. For example, an aggregate forEnginecan be written:

return( Event with

Fuel => Random_Time.Random(G) mod100, Oxygen => Random_Time.Random(G)mod500);

Normally, the declaration of Eventwould contain a meaningful default value forTime.

View conversion and redispatching

Type conversion of tagged types is quite different from what you normally think of as type con- version. A new value is not created; instead, you get a new view of the original value which hides

6.8 Type conversion* 91

components that are not part of the target type.

§4.6

5 Atype_conversionwhose operand is the name of an object is called aview con- version if its target type is tagged, . . . ; other type_conversions are called value conversions.

42 The tag of the result is the tag of the operand. . . .

55 If the target type is tagged, then an assignment to the view assigns to the corre- sponding part of the object denoted by the operand; . . .

Since neither the tag §4.6(42) nor the value of the operand is changed by the conversion, you can always recover the original value and type.

During an assignment, both the source and the target objects retain their tags §5.2(15), and only the relevant components are copied §5.2(13), §4.6(56):

E: Engine_Event := . . . ; - - Tagged as Engine_Event A: Aux_Engine_Event := . . . ; - - Tagged as Aux_Engine_Event E := Engine_Event(A); - - Side component ignored

Engine_Event(A) := E; - - Side component not assigned to

View conversions can be used forredispatching. Consider the following tagged typeParentwhere the derived typeDerivedinherits the primitive procedureProc1but overridesProc2, and suppose thatP_CLis a class-wide object containing a value of typeDerived.

type Parentis tagged. . . ;

procedure Proc1(V:in Parent);

procedure Proc2(V:in Parent);

type Derivedis new Parentwith. . . ;

procedure Proc2(V:in Derived); D: Derived := . . . ;

P_CL: Parent’Class := Parent’Class(D); Proc1(P_CL);

WhenProc1is called, the value of class-wide type will be converted to the specific typeParent

of the formal parameterV§4.6(23). However, the conversion is only a view conversion andVre- mains tagged asDerived. WithinProc1, the following statement will redispatch to the overridden

Proc2:

Proc2(Parent’Class(V));

because the tag of the result is taken from the tag of the operand, namely Derived. This works because tagged types are passed by-reference §6.2(5) so that within Proc1 the tag of its actual parameter exists unchanged.

Documento similar