Annotated Ada Reference ManualLegal Information
Contents   Index   References   Search   Previous   Next 

 3.9.4 Interface Types

{AI95-00251-01} {AI95-00345-01} [An interface type is an abstract tagged type that provides a restricted form of multiple inheritance. A tagged type, task type, or protected type may have one or more interface types as ancestors.]
Glossary entry: An interface type is a form of abstract tagged type which has no components or concrete operations except possibly null procedures. Interface types are used for composing other interfaces and tagged types and thereby provide multiple inheritance. Only an interface type can be used as a progenitor of another type.

Language Design Principles

{AI95-00251-01} {AI95-00345-01} The rules are designed so that an interface can be used as either a parent type or a progenitor type without changing the meaning. That's important so that the order that interfaces are specified in a derived_type_definition is not significant. In particular, we want: 
type Con1 is new Int1 and Int2 with null record;
type Con2 is new Int2 and Int1 with null record;
to mean exactly the same thing. 


{AI95-00251-01} {AI95-00345-01} interface_type_definition ::= 
    [limited | task | protected | synchronizedinterface [and interface_list]
{AI95-00251-01} {AI95-00419-01} interface_list ::= interface_subtype_mark {and interface_subtype_mark}

Static Semantics

{AI95-00251-01} An interface type (also called an interface) is a specific abstract tagged type that is defined by an interface_type_definition.
{AI95-00345-01} An interface with the reserved word limited, task, protected, or synchronized in its definition is termed, respectively, a limited interface, a task interface, a protected interface, or a synchronized interface. In addition, all task and protected interfaces are synchronized interfaces, and all synchronized interfaces are limited interfaces. 
Glossary entry: A synchronized entity is one that will work safely with multiple tasks at one time. A synchronized interface can be an ancestor of a task or a protected type. Such a task or protected type is called a synchronized tagged type.
{AI95-00345-01} {AI95-00443-01} [A task or protected type derived from an interface is a tagged type.] Such a tagged type is called a synchronized tagged type, as are synchronized interfaces and private extensions whose declaration includes the reserved word synchronized.
Proof: The full definition of tagged types given in 3.9 includes task and protected types derived from interfaces. 
Ramification: The class-wide type associated with a tagged task type (including a task interface type) is a task type, because “task” is one of the language-defined classes of types (see 3.2). However, the class-wide type associated with an interface is not an interface type, as “interface” is not one of the language-defined classes (as it is not closed under derivation). In this sense, “interface” is similar to “abstract”. The class-wide type associated with an interface is a concrete (nonabstract) indefinite tagged composite type.
“Private extension” includes generic formal private extensions, as explained in 12.5.1.
{AI95-00345-01} A task interface is an [abstract] task type. A protected interface is an [abstract] protected type. 
Proof: The “abstract” follows from the definition of an interface type. 
Reason: This ensures that task operations (like abort and the Terminated attribute) can be applied to a task interface type and the associated class-wide type. While there are no protected type operations, we apply the same rule to protected interfaces for consistency. 
{AI95-00251-01} [An interface type has no components.] 
Proof: This follows from the syntax and the fact that discriminants are not allowed for interface types. 
{AI95-00419-01} An interface_subtype_mark in an interface_list names a progenitor subtype; its type is the progenitor type. An interface type inherits user-defined primitive subprograms from each progenitor type in the same way that a derived type inherits user-defined primitive subprograms from its progenitor types (see 3.4).
Glossary entry: A progenitor of a derived type is one of the types given in the definition of the derived type other than the first. A progenitor is always an interface type. Interfaces, tasks, and protected types may also have progenitors.

Legality Rules

 {AI95-00251-01} All user-defined primitive subprograms of an interface type shall be abstract subprograms or null procedures.
 {AI95-00251-01} The type of a subtype named in an interface_list shall be an interface type.
 {AI95-00251-01} {AI95-00345-01} A type derived from a nonlimited interface shall be nonlimited.
 {AI95-00345-01} An interface derived from a task interface shall include the reserved word task in its definition; any other type derived from a task interface shall be a private extension or a task type declared by a task declaration (see 9.1).
 {AI95-00345-01} An interface derived from a protected interface shall include the reserved word protected in its definition; any other type derived from a protected interface shall be a private extension or a protected type declared by a protected declaration (see 9.4).
 {AI95-00345-01} An interface derived from a synchronized interface shall include one of the reserved words task, protected, or synchronized in its definition; any other type derived from a synchronized interface shall be a private extension, a task type declared by a task declaration, or a protected type declared by a protected declaration.
Reason: We require that an interface descendant of a task, protected, or synchronized interface repeat the explicit kind of interface it will be, rather than simply inheriting it, so that a reader is always aware of whether the interface provides synchronization and whether it may be implemented only by a task or protected type. The only place where inheritance of the kind of interface might be useful would be in a generic if you didn't know the kind of the actual interface. However, the value of that is low because you cannot implement an interface properly if you don't know whether it is a task, protected, or synchronized interface. Hence, we require the kind of the actual interface to match the kind of the formal interface (see 12.5.5). 
 {AI95-00345-01} No type shall be derived from both a task interface and a protected interface.
Reason: This prevents a single private extension from inheriting from both a task and a protected interface. For a private type, there can be no legal completion. For a generic formal derived type, there can be no possible matching type (so no instantiation could be legal). This rule provides early detection of the errors. 
 {AI95-00251-01} In addition to the places where Legality Rules normally apply (see 12.3), these rules apply also in the private part of an instance of a generic unit.
Ramification: This paragraph is intended to apply to all of the Legality Rules in this clause. We cannot allow interface types which do not obey these rules, anywhere. Luckily, deriving from a formal type (which might be an interface) is not allowed for any tagged types in a generic body. So checking in the private part of a generic covers all of the cases. 

Dynamic Semantics

 {AI95-00251-01} {AI05-0070-1} The elaboration of an interface_type_definition creates the interface type and its first subtype has no effect. 
Discussion: There is no other effect. An interface_list is made up of subtype_marks, which do not need to be elaborated, so the interface_list does not either. This is consistent with the handling of discriminant_parts.
83  {AI95-00411-01} Nonlimited interface types have predefined nonabstract equality operators. These may be overridden with user-defined abstract equality operators. Such operators will then require an explicit overriding for any nonabstract descendant of the interface. 


 {AI95-00433-01} Example of a limited interface and a synchronized interface extending it:
type Queue is limited interface;
procedure Append(Q : in out Queue; Person : in Person_Name) is abstract;
procedure Remove_First(Q      : in out Queue;
                       Person : out Person_Name) is abstract;
function Cur_Count(Q : in Queue) return Natural is abstract;
function Max_Count(Q : in Queue) return Natural is abstract;
-- See 3.10.1 for Person_Name.
{AI05-0004-1} Queue_Error : exception;
-- Append raises Queue_Error if 
Cur_Count Count(Q) = Max_Count(Q)
-- Remove_First raises Queue_Error if 
Cur_Count Count(Q) = 0
type Synchronized_Queue is synchronized interface and Queue; -- see 9.11
procedure Append_Wait(Q      : in out Synchronized_Queue;
                      Person : in Person_Name) is abstract;
procedure Remove_First_Wait(Q      : in out Synchronized_Queue;
                            Person : out Person_Name) is abstract;
procedure Transfer(From   : in out Queue'Class;
                   To     : in out Queue'Class;
                   Number : in     Natural := 1) is
   Person : Person_Name;
   for I in 1..Number loop
      Remove_First(From, Person);
      Append(To, Person);
   end loop;
end Transfer;
 This defines a Queue interface defining a queue of people. (A similar design could be created to define any kind of queue simply by replacing Person_Name by an appropriate type.) The Queue interface has four dispatching operations, Append, Remove_First, Cur_Count, and Max_Count. The body of a class-wide operation, Transfer is also shown. Every non-abstract extension of Queue must provide implementations for at least its four dispatching operations, as they are abstract. Any object of a type derived from Queue may be passed to Transfer as either the From or the To operand. The two operands need not be of the same type in any given call.
 The Synchronized_Queue interface inherits the four dispatching operations from Queue and adds two additional dispatching operations, which wait if necessary rather than raising the Queue_Error exception. This synchronized interface may only be implemented by a task or protected type, and as such ensures safe concurrent access.
 {AI95-00433-01} Example use of the interface:
{AI05-0004-1} type Fast_Food_Queue is new Queue with record ...;
procedure Append(Q : in out Fast_Food_Queue; Person : in Person_Name);
procedure Remove_First(Q : in out Fast_Food_Queue; Person : 
out in Person_Name);
function Cur_Count(Q : in Fast_Food_Queue) return Natural;
function Max_Count(Q : in Fast_Food_Queue) return Natural;
Cashier, Counter : Fast_Food_Queue;
-- Add George (see 3.10.1) to the cashier's queue:
Append (Cashier, George);
-- After payment, move George to the sandwich counter queue:
Transfer (Cashier, Counter);
 An interface such as Queue can be used directly as the parent of a new type (as shown here), or can be used as a progenitor when a type is derived. In either case, the primitive operations of the interface are inherited. For Queue, the implementation of the four inherited routines must be provided. Inside the call of Transfer, calls will dispatch to the implementations of Append and Remove_First for type Fast_Food_Queue.
 {AI95-00433-01} Example of a task interface:
type Serial_Device is task interface;  -- see 9.1
procedure Read (Dev : in Serial_Device; C : out Character) is abstract;
procedure Write(Dev : in Serial_Device; C : in  Character) is abstract;
 The Serial_Device interface has two dispatching operations which are intended to be implemented by task entries (see 9.1).

Extensions to Ada 95

{AI95-00251-01} {AI95-00345-01} Interface types are new. They provide multiple inheritance of interfaces, similar to the facility provided in Java and other recent language designs. 

Wording Changes from Ada 2005

{AI05-0070-1} Correction: Corrected the definition of elaboration for an interface_type_definition to match that of other type definitions. 

Contents   Index   References   Search   Previous   Next 
Ada-Europe Ada 2005 and 2012 Editions sponsored in part by Ada-Europe