Rationale for Ada 2005: Structure and visibility

RUSTOP
BACKNEXT

ENG

2. Mutually dependent types

@ For many programmers the solution of the problem of mutually dependent types will be the single most important improvement introduced in Ada 2005.

@ This topic was discussed in the Introduction using an example of two mutually dependent types, Point and Line. Each type needed to refer to the other in its declaration and of course the solution to this problem is to use incomplete types. In Ada 95 there are three stages. We first declare the incomplete types

  1        type Point; -- incomplete types
  2        type Line;

@ Suppose for simplicity that we wish to study patterns of points and lines such that each point has exactly three lines through it and that each line has exactly three points on it. (This is not so stupid.

@ The two most fundamental theorems of projective geometry, those of Pappus and Desargues, concern such structures and so does the simplest of finite geometries, the Fano plane.) Using the incomplete types we can then declare

  1        type Point_Ptr is access Point; -- use incomplete types
  2        type Line_Ptr is access Line;

@ and finally we can complete the type declarations thus

  1        type Point is -- complete the types
  2                record
  3                        L, M, N : Line_Ptr;
  4                end record;
  5        type Line is
  6                record
  7                        P, Q, R : Point_Ptr;
  8                end record;

@ Of course, in Ada 2005, as discussed in the previous paper, we can use anonymous access types more freely so that the second stage can be omitted in this example. As a consequence the complete declarations are simply

  1        type Point is -- complete the types
  2                record
  3                        L, M, N : access Line;
  4                end record;
  5        type Line is
  6                record
  7                        P, Q, R : access Point;
  8                end record;

@ This has the important advantage that we do not have to invent irritating identifiers such as Point_Ptr.

@ But we will stick to Ada 95 for the moment. In Ada 95 there are two rules ? the incomplete type can only be used in the definition of access types; ? the complete type declaration must be in the same declarative region as the incomplete type.

@ The first rule does actually permit

  1        type T;
  2        type A is access procedure (X : in out T);

@ Note that we are here using the incomplete type T for a parameter. This is not normally allowed, but in this case the procedure itself is being used in an access type. The additional level of indirection means that the fact that the parameter mechanism for T is not known yet does not matter.

@ Apart from this, it is not possible to use an incomplete type for a parameter in a subprogram in Ada 95 except in the case of an access parameter. Thus we cannot have

  1        function Is_Point_On_Line (P : Point; L : Line) return Boolean;

@ before the complete type declarations.

@ It is also worth pointing out that the problem of mutually dependent types (within a single unit) can often be solved by using private types thus

  1                type Point is private;
  2                type Point_Ptr is access Point;
  3                type Line is private;
  4                type Line_Ptr is access Line;
  5        private
  6                type Point is
  7                        record
  8                                L, M, N : Line_Ptr;
  9                        end record;
 10                type Line is
 11                        record
 12                                P, Q, R : Point_Ptr;
 13                        end record;

@ But we need to use incomplete types if we want the user to see the full view of a type so the situation is somewhat different.

@ As an aside, remember that if an incomplete type is declared in a private part then the complete type can be deferred to the body (this is the so-called Taft Amendment in Ada 83). In this case neither the user nor indeed the compiler can see the complete type and this is the main reason why we cannot have parameters of incomplete types whereas we can for private types.

@ We will now introduce what has become a canonical example for discussing this topic. This concerns employees and the departments of the organization in which they work. The information about employees needs to refer to the departments and the departments need to refer to the employees. We assume that the material regarding employees and departments is quite large so that we naturally wish to declare the two types in distinct packages Employees and Departments. So we would like to say

  1        with Departments; use Departments;
  2        package Employees is
  3                type Employee is private;
  4                procedure Assign_Employee (E : in out Employee; D : in out Department);
  5                type Dept_Ptr is access all Department;
  6                function Current_Department (E : Employee) return Dept_Ptr;
  7                ...
  8        end Employees;
  9        with Employees; use Employees;
 10        package Departments is
 11                type Department is private;
 12                procedure Choose_Manager (D : in out Department; M : in out Employee);
 13                ...
 14        end Departments;

@ We cannot write this because each package has a with clause for the other and they cannot both be declared (or entered into the library) first.

@ We assume of course that the type Employee includes information about the Department for whom the Employee works and the type Department contains information regarding the manager of the department and presumably a list of the other employees as well – note that the manager is naturally also an Employee.

@ So in Ada 95 we are forced to put everything into one package thus

  1        package Workplace is
  2                type Employee is private;
  3                type Department is private;
  4                procedure Assign_Employee (E : in out Employee; D : in out Department);
  5                type Dept_Ptr is access all Department;
  6                function Current_Department (E : Employee) return Dept_Ptr;
  7                procedure Choose_Manager (D : in out Department; M : in out Employee);
  8        private
  9                ...
 10        end Workplace;

@ Not only does this give rise to huge cumbersome packages but it also prevents us from using the proper abstractions. Thus the types Employee and Department have to be declared in the same private part and so are not protected from each other's operations.

@ Ada 2005 solves this by introducing a variation of the with clause – the limited with clause. A limited with clause enables a library unit to have an incomplete view of all the visible types in another package. We can now write

  1        limited with Departments;
  2        package Employees is
  3                type Employee is private;
  4                procedure Assign_Employee (E : in out Employee; D : access Departments.Department);
  5                type Dept_Ptr is access all Departments.Department;
  6                function Current_Department (E : Employee) return Dept_Ptr;
  7                ...
  8        end Employees;
  9        limited with Employees;
 10        package Departments is
 11                type Department is private;
 12                procedure Choose_Manager (D : in out Department; M : access Employees.Employee);
 13                ...
 14        end Departments;

@ It is important to understand that a limited with clause does not impose a dependence. Thus if a package A has a limited with clause for B, then A does not depend on B as it would with a normal with clause, and so B does not have to be compiled before A or placed into the library before A.

@ If we have a cycle of packages we only have to put limited with on one package since that is sufficient to break the cycle of dependences. However, for symmetry, in this example we have made them both have a limited view of each other.

@ Note the terminology: we say that we have a limited view of a package if the view is provided through a limited with clause. So a limited view of a package provides an incomplete view of its visible types. And by an incomplete view we mean as if they were incomplete types.

@ In the example, because an incomplete view of a type cannot generally be used as a parameter, we have had to change one parameter of each of Assign_Employee and Choose_Manager to be an access parameter.

@ There are a number of rules necessary to avoid problems. A natural one is that we cannot have both a limited with clause and a normal with clause for the same package in the same context clause (a normal with clause is now officially referred to as a nonlimited with clause). An important and perhaps unexpected rule is that we cannot have a use package clause with a limited view because severe surprises might happen.

@ To understand how this could be possible it is important to realise that a limited with clause provides a very restricted view of a package. It just makes visible ? the name of the package and packages nested within, ? an incomplete view of the types declared in the visible parts of the packages.

@ Nothing else is visible at all. Now consider

  1        package A is
  2                X : Integer := 99;
  3        end A;
  4        package B is
  5                X : Integer := 111;
  6        end B;
  7        limited with A, B;
  8        package P is
  9                ... -- neither X visible here
 10        end P;

@ Within package P we cannot access A.X or B.X because they are not types but objects. But we could declare a child package with its own with clause thus

  1        with A;
  2        package P.C is
  3                Y : Integer := A.X;
  4        end P.C;

@ The nonlimited with clause on the child "overrides" the limited with clause on the parent so that A.X is visible.

@ Now suppose we were allowed to add a use package clause to the parent package; since a use clause on a parent applies to a child this means that we could refer to A.X as just X within the child so we would have

  1        limited with A, B;
  2        use A, B; -- illegal
  3        package P is
  4                ... -- neither X visible here
  5        end P;
  6        with A;
  7        package P.C is
  8                Y : Integer := X; -- A.X now visible as just X
  9        end P.C;

@ If we were now to change the with clause on the child to refer to B instead of A, then X would refer to B.X rather than A.X. This would not be at all obvious because the use clause that permits this is on the parent and we are not changing the context clause of the parent at all. This would clearly be unacceptable and so use package clauses are forbidden if we only have a limited view of the package.

@ Here is a reasonably complete list of the rules designed to prevent misadventure when using limited with clauses ? a use package clause cannot refer to a package with a limited view as illustrated above,

  1        limited with P; use P; -- illegal
  2        package Q is ...

@ the rule also prevents

  1        limited with P;
  2        package Q is
  3                use P; -- illegal

@ ? a limited with clause can only appear on a specification – it cannot appear on a body or a subunit,

  1        limited with P; -- illegal
  2        package body Q is ...

@ ? a limited with clause and a nonlimited with clause for the same package may not appear in the same context clause,

  1        limited with P; with P; -- illegal

@ ? a limited with clause and a use clause for the same package or one of its children may not appear in the same context clause,

  1        limited with P; use P.C; -- illegal

@ ? a limited with clause may not appear in the context clause applying to itself,

  1        limited with P; -- illegal
  2        package P is ...

@ ? a limited with clause may not appear on a child unit if a nonlimited with clause for the same package applies to its parent or grandparent etc,

  1        with Q;
  2        package P is ...
  3        limited with Q; -- illegal
  4        package P.C is ...

@ but note that the reverse is allowed as mentioned above

  1        limited with Q;
  2        package P is ...
  3        with Q; -- OK
  4        package P.C is ...

@ ? a limited with clause may not appear in the scope of a use clause which names the unit or one of its children,

  1        with A;
  2        package P is
  3                package R renames A;
  4        end P;
  5        with P;
  6        package Q is
  7                use P.R; -- applies to A
  8        end Q;
  9        limited with A; -- illegal
 10        package Q.C is ...

@ without this specific rule, the use clause in Q which actually refers to A would clash with the limited with clause for A.

@ Finally note that a limited with clause can only refer to a package declaration and not to a subprogram, generic declaration or instantiation, or to a package renaming.

@ We will now return to the rules for incomplete types. As mentioned above the rules for incomplete types are quite strict in Ada 95 and apart from the curious case of an access to subprogram type it is not possible to use an incomplete type for a parameter other than in an access parameter.

@ Ada 2005 enables some relaxation of these rules by introducing tagged incomplete types. We can write

  1        type T is tagged;

@ and then the complete type must be a tagged type. Of course the reverse does not hold. If we have just

  1        type T;

@ then the complete type T might be tagged or not.

@ A curious feature of Ada 95 was mentioned in the Introduction. In Ada 95 we can write

  1        type T;
  2        ...
  3        type T_Ptr is access all T'Class;

@ By using the attribute Class, this promises in a rather sly way that the complete type T will be tagged. This is strictly obsolescent in Ada 2005 and moved to Annex J. In Ada 2005 we should write

  1        type T is tagged;
  2        ...
  3        type T_Ptr is access all T'Class;

@ The big advantage of introducing tagged incomplete types is that we know that tagged types are always passed by reference and so we are allowed to use tagged incomplete types for parameters.

@ This advantage extends to the incomplete view obtained from a limited with clause. If a type in a package is visibly tagged then the incomplete view obtained is tagged incomplete and so the type can then be used for parameters.

@ Returning to the packages Employees and Departments it probably makes sense to make both types tagged since it is likely that the types Employee and Department form a hierarchy. So we can write

  1        limited with Departments;
  2        package Employees is
  3                type Employee is tagged private;
  4                procedure Assign_Employee (E : in out Employee; D : in out Departments.Department'Class);
  5                type Dept_Ptr is access all Departments.Department'Class;
  6                function Current_Department (E : Employee) return Dept_Ptr;
  7                ...
  8        end Employees;
  9
 10        limited with Employees;
 11        package Departments is
 12                type Department is tagged private;
 13                procedure Choose_Manager (D : in out Department; M : in out Employees.Employee'Class);
 14                ...
 15        end Departments;

@ The text is a bit cumbersome now with Class sprinkled liberally around but we can introduce some subtypes in order to shorten the names. We can also avoid the introduction of the type Dept_Ptr since we can use an anonymous access type for the function result as mentioned in the previous paper. So we get

  1        limited with Departments;
  2        package Employees is
  3                type Employee is tagged private;
  4                subtype Dept is Departments.Department;
  5                procedure Assign_Employee(E: in out Employee; D: in out Dept'Class);
  6                function Current_Department(E: Employee) return access Dept'Class;
  7                ...
  8        end Employees;
  9
 10        limited with Employees;
 11        package Departments is
 12                type Department is tagged private;
 13                subtype Empl is Employees.Employee;
 14                procedure Choose_Manager(D: in out Department; M: in out Empl'Class);
 15                ...
 16        end Departments;

@ Observe that in Ada 2005 we can use a simple subtype as an abbreviation for an incomplete type thus

  1        subtype Dept is Departments.Department;

@ but such a subtype cannot have a constraint or a null exclusion. In essence it is just a renaming.

@ Remember that we cannot have a use clause with a limited view. Moreover, many projects forbid use clauses anyway but permit renamings and subtypes for local abbreviations. It would be a pain if such abbreviations were not also available when using a limited with clause.

@ It's a pity we cannot also write

  1        subtype A_Dept is Departments.Department'Class;

@ but then you cannot have everything in life.

@ A similar situation arises with the names of nested packages. They can be renamed in order to provide an abbreviation.

@ The mechanism for breaking cycles of dependences by introducing limited with clauses does not mean that the implementation does not check everything thoroughly in a rigorous Ada way. It is just that some checks might have to be deferred. The details depend upon the implementation.

@ For the human reader it is very helpful that use clauses are not allowed in conjunction with limited with clauses since it eliminates any doubt about the location of types involved. It probably helps the poor compilers as well.

@ Readers might be interested to know that this topic was one of the most difficult to solve satisfactorily in the design of Ada 2005. Altogether seven different versions of AI-217 were developed. This chosen solution is on reflection by far the best and was in fact number 6.

@ A number of loopholes in Ada 95 regarding incomplete types are also closed in Ada 2005.

@ One such loophole is illustrated by the following (this is Ada 95)

  1        package P is
  2                ...
  3        private
  4                type T; -- an incomplete type
  5                type ATC is access all T'Class; -- it must be tagged
  6                X : ATC;
  7                procedure Op (X : access T); -- primitive operation
  8                ...
  9        end P;

@ The incomplete type T is declared in the private part of the package P. The access type ACT is then declared and since it is class wide this implies that the type T must be tagged (the reader will recall from the discussion above that this odd feature is banished to Annex J in Ada 2005). The full type T is then declared in the body. We also declare a primitive operation Op of the type T in the private part.

@ However, before the body of P is declared, nothing in Ada 95 prevents us from writing a private child thus

  1        private package P.C is
  2                procedure Naughty;
  3        end P.C;
  4        package body P.C is
  5                procedure Naughty is
  6                begin
  7                        Op (X); -- a dispatching call
  8                end Naughty;
  9        end P.C;

@ and the procedure Naughty can call the dispatching operation Op. The problem is that we are required to compile this call before the type T is completed and thus before the location of its tag is known.

@ This problem is prevented in Ada 2005 by a rule that if an incomplete type declared in a private part has primitive operations then the completion cannot be deferred to the body.

@ Similar problems arise with access to subprogram types. Thus, as mentioned above, Ada 95 permits

  1        type T;
  2        type A is access procedure (X : in out T);

@ In Ada 2005, the completion of T cannot be deferred to a body. Nor can we declare such an access to subprogram type if we only have an incomplete view of T arising from a limited with clause.

@ Another change in Ada 2005 can be illustrated by the Departments and Employees example. We can write

  1        limited with Departments;
  2        package Employees is
  3                type Employee is tagged private;
  4                procedure Assign_Employee (E : in out Employee; D : in out Departments.Department'Class);
  5                type Dept_Ptr is access all Departments.Department'Class;
  6                ...
  7        end Employees;
  8        with Employees; use Employees;
  9        procedure Recruit (D : Dept_Ptr; E : in out Employee) is
 10        begin
 11                Assign_Employee (E, D.all);
 12        end Recruit;

@ Ada 95 has a rule that says "thou shalt not dereference an incomplete type". This would prevent the call of Assign_Employee which is clearly harmless. It would be odd to require Recruit to have a nonlimited with clause for Departments to allow the call of Assign_Employee. Accordingly the rule is changed in Ada 2005 so that dereferencing an incomplete view is only forbidden when used as a prefix as, for example, in D'Size.

Rationale for Ada 2005: Structure and visibility

@ENGRUSTOPBACKNEXT

2. Взаимно-зависимые типы

@ Для многих программистов решение проблемы взаимно зависимых типов будет одним из самых важных усовершенствований, введенным в Аде 2005.

@ Эта тема обсуждалась во Введении при обсуждении примера о двух взаимно зависимых типах Point и Line. Там каждый тип ссылался на другой при объявлении. Решение этой проблемы состоит в том, чтобы использовать неполные определения типов. В Аде 95 это делается в три шага. Сначала мы объявляем неполные типы:

  1        type Point; -- (1)
  2        type Line;

@ Пусть каждая точка ассоциируется с тремя линиями проходящии через неё, а каждая линия ассоциируется с тремя точками через которые она проходит. (Это не настолько глупо. Две самых фундаментальных теоремы проективной геометрии, такие как Pappus и Desargues касаются таких структур, и их используют самые простые из конечных геометрий, поверхность Fano.)

@ Далее мы определяем ссылочные типы на неполные типы:

  1        type Point_Ptr is access Point; -- (2)
  2        type Line_Ptr is access Line;

@ и только теперь получаем окончательный результат:

  1        type Point is -- (3)
  2                record
  3                        L, M, N : Line_Ptr;
  4                end record;
  5        type Line is
  6                record
  7                        P, Q, R : Point_Ptr;
  8                end record;

@ В Аде 2005, как обсуждалось в предыдущей статье, мы можем использовать анонимные ссылочные типы более свободно и можем опустить второй шаг в этом примере. Таким образом, полные объявления будут:

  1        type Point is -- (2')
  2                record
  3                        L, M, N : access Line;
  4                end record;
  5        type Line is
  6                record
  7                        P, Q, R : access Point;
  8                end record;

@ Это даёт важное преимущество, т.к. мы избавлены от необходимости изобретать раздражающие идентификаторы, типа Point_Ptr.

@ Но в настоящий момент мы будем придерживаться ada в которой имеется два правила: 1) неполный тип может использоваться только для определения ссылочных типов; и 2) полное объявление типа должно быть в той же самой декларативной области где и неполный тип.

@ Первое правило фактически разрешает:

  1        type T;
  2        type A is access procedure (X : in out T);

@ Отметим, что мы здесь используем неполный тип T в качестве параметра процедуры. Это обычно не позволяется, но в данном случае определяется не сама процедура а ссылочный тип на неё. Это задаёт дополнительный уровень косвенности для параметра T который на данном этапе не известен, и пока не имеет значения.

@ Кроме этого, нельзя использовать неполный тип в качестве параметра в подпрограмме на Аде 95 кроме как в ссылочном параметре. Таким образом, мы не можем написать:

  1        function Is_Point_On_Line (P : Point; L : Line) return Boolean;

@ перед полными объявлениями типа.

@ Стоит упомянуть, что проблема взаимно зависимых типов (в пределах единственного модуля) может решаться использованием частных типов так:

  1                type Point is private;
  2                type Point_Ptr is access Point;
  3                type Line is private;
  4                type Line_Ptr is access Line;
  5        private
  6                type Point is
  7                        record
  8                                L, M, N : Line_Ptr;
  9                        end record;
 10                type Line is
 11                        record
 12                                P, Q, R : Point_Ptr;
 13                        end record;

@ Но мы должны использовать неполные типы, если хотим, чтобы пользователь видел полное представление типа, таким образом, ситуация несколько отличается.

@ Как в стороне, помните, что, если неполный тип объявлен в частной секции тогда, полный тип может быть задержан к телу (это - так называемая Поправка Taft в Аде 83). В этом случае ни пользователь, ни действительно компилятор не может видеть полный тип, и это - основная причина, почему мы не можем иметь параметры неполных типов, тогда как мы можем это для частных типов.

@ Рссмотрим канонический пример иллюстрирующий эту тему. Пусть имеется некая организация состоящая из отделов в которых работают служащие. Информация о служащих должна обратиться к отделам, и отделы должны обратиться к служащим. Мы предполагаем, что материал относительно служащих и отделов является весьма большим так, чтобы мы естественно желали объявить два типа в отличных пакетах Employees и Department. Попытка определить это следующим образом:

  1        with Departments; use Departments;
  2        package Employees is
  3                type Employee is private;
  4                procedure Assign_Employee (E : in out Employee; D : in out Department);
  5                type Dept_Ptr is access all Department;
  6                function Current_Department (E : Employee) return Dept_Ptr;
  7                ...
  8        end Employees;
  9        with Employees; use Employees;
 10        package Departments is
 11                type Department is private;
 12                procedure Choose_Manager (D : in out Department; M : in out Employee);
 13                ...
 14        end Departments;

@ не корректна, потому что каждый пакет имеет with-утверждение с ссылкой на другой, и они не могут оба быть объявлены (или введены из библиотеки) сначала.

@ Мы предполагаем конечно, что тип Employee включает информацию об Department в котором работает Employee, и тип Department содержит информацию относительно менеджера отдела, и по-видимому список других служащих также - отметим, что менеджер - естественно также Employee (Служащий).

@ Так в Аде 95 мы вынуждены поместить всё в один пакет:

  1        package Workplace is
  2                type Employee is private;
  3                type Department is private;
  4                procedure Assign_Employee (E : in out Employee; D : in out Department);
  5                type Dept_Ptr is access all Department;
  6                function Current_Department (E : Employee) return Dept_Ptr;
  7                procedure Choose_Manager (D : in out Department; M : in out Employee);
  8        private
  9                ...
 10        end Workplace;

@ Мало того, что это дает начало огромным громоздким пакетам, но и это также препятствует нам использовать надлежащие абстракции. Таким образом, типы Employee (Служащий) и Department (Отдел) должны быть объявлены в той же самой частной секции и не защищены от операций друг друга.

@ Ада 2005 решает эту проблему, вводя разновидность with утверждения - limited with утверждение. Такое утверждение дает возможность модулю библиотеки иметь неполное представление всех видимых типов в другом пакете. Теперь мы можем написать:

  1        limited with Departments;
  2        package Employees is
  3                type Employee is private;
  4                procedure Assign_Employee (E : in out Employee; D : access Departments.Department);
  5                type Dept_Ptr is access all Departments.Department;
  6                function Current_Department (E : Employee) return Dept_Ptr;
  7                ...
  8        end Employees;
  9        limited with Employees;
 10        package Departments is
 11                type Department is private;
 12                procedure Choose_Manager (D : in out Department; M : access Employees.Employee);
 13                ...
 14        end Departments;

@ Важно понять, что утверждение limited with не налагает зависимость. Таким образом, если пакет A имеет limited with для B, то A не зависит от B как при нормальном with утверждении, и таким образом, B не должен быть собран прежде A или помещен в библиотеку прежде A.

@ Если мы имеем цикл пакетов, мы только должны поместить limited with утверждение в один пакет, и этого достаточно, чтобы нарушить цикл зависимостей. Однако, для симметрии, в этом примере мы заставили их обоих иметь ограниченное представление друг друга.

@ Отметим терминологию: мы говорим, что мы имеем ограниченное представление пакета, если представление обеспечивается через limited with утверждение. Таким образом, ограниченное представление пакета обеспечивает неполное представление его видимых типов. И неполным представлением мы подразумеваем, как будто они были неполными типами.

@ В примере, потому что неполное представление типа не может вообще использоваться как параметр, мы должны были изменить один параметр каждого из Assign_Employee и Choose_Manager, чтобы быть ссылочным параметром.

@ Есть множество правил, необходимых для того чтобы избежать проблем. Одно из них состоит в том, что мы не можем иметь и limited with утверждение и нормальное with утверждение для одного и того же пакета в одном и том же контекстном пункте (нормальное with утверждение теперь официально упоминается как неограниченное with утверждение). Важное и, возможно, неожиданное правило состоит в том, что мы не можем иметь use утверждение пакета с ограниченным представлением, потому что при этом могут случиться серьезные неожиданности.

@ Т.о. важно понять, что limited with утвверждение обеспечивает очень ограниченное представление пакета. Оно делает видимым только название пакета и пакетов, вложенных в пределах неполного представления типов, объявленных в видимых частях пакетов.

@ Ничто иное не видимо вообще. Теперь рассмотрим:

  1        package A is
  2                X : Integer := 99;
  3        end A;
  4        package B is
  5                X : Integer := 111;
  6        end B;
  7        limited with A, B;
  8        package P is
  9                ... -- neither X visible here
 10        end P;

@ В пределах пакета P мы не можем обратиться ни к A.X ни к B.X, потому это объекты а не типы. Но мы могли бы объявить дочерний пакет с его собственным with выражением следующим образом:

  1        with A;
  2        package P.C is
  3                Y : Integer := A.X;
  4        end P.C;

@ Неограниченное with утверждение на ребенке "отменяет" limited with утверждение на родителе так, что A.X стал видим.

@ Теперь предположим, что нам разрешали добавить пакетное use утверждение к родительскому пакету; так как use утверждение на родителе обращается к ребенку, это означает, что мы могли обратиться к A.X когда X в пределах ребенка, таким образом мы будем иметь:

  1        limited with A, B;
  2        use A, B; -- illegal
  3        package P is
  4                ... -- neither X visible here
  5        end P;
  6        with A;
  7        package P.C is
  8                Y : Integer := X; -- A.X now visible as just X
  9        end P.C;

@ Если мы должны были теперь изменить with утверждение на ребенке, чтобы обратиться к B вместо A, то X обратится к B.X, а не A.X. Это не было бы вообще очевидно, потому что use утверждение, которое разрешает это, находится на родителе, и мы не изменяем контекстный пункт родителя вообще. Ясно что это было бы недопустимым и, таким образом, пакетное use утверждения запрещаются, если мы имеем только ограниченное представление пакета.

@ Вот полный список правил, проектирования, чтобы предотвратить несчастный случай когда использование limited with утверждений, пакетных use утверждений не может обратиться к пакету с ограниченным представлением как проиллюстрировано выше:

  1        limited with P; use P; -- illegal
  2        package Q is ...

@ правило также предотвращает:

  1        limited with P;
  2        package Q is
  3                use P; -- illegal

@ - limited with утверждение может только появиться в спецификации - оно не может появиться в теле или субмодуле

  1        limited with P; -- illegal
  2        package body Q is ...

@ - limited with утверждение и неограниченное with утверждение для того же самого пакета не может появляется в том же самом контекстном пункте,

  1        limited with P; with P; -- illegal

@ - limited with утверждение и use утверждение для того же самого пакета или одного из его потомков, не может появляется в том же самом контекстном пункте,

  1        limited with P; use P.C; -- illegal

@ - limited with утверждение не может появляется в контекстном пункте, обращающемся к себе,

  1        limited with P; -- illegal
  2        package P is ...

@ - limited with утверждение, не может появляется в дочернем модуле, если неограниченный with для того же самого пакета обращается к его родителю или прародителю и т.д,

  1        with Q;
  2        package P is ...
  3        limited with Q; -- illegal
  4        package P.C is ...

@ но заметим, что обратное разрешено

  1        limited with Q;
  2        package P is ...
  3        with Q; -- OK
  4        package P.C is ...

@ - limited with утверждение не может появляется в пределах пункта использования, который называет модуль или одного из его потомков,

  1        with A;
  2        package P is
  3                package R renames A;
  4        end P;
  5        with P;
  6        package Q is
  7                use P.R; -- applies to A
  8        end Q;
  9        limited with A; -- illegal
 10        package Q.C is ...

@ без этого определенного правила, use утверждение в Q, которое фактически обращается к A, столкнулся бы с limited with утверждением для A.

@ Наконец отметим, что limited with утверждение может только обратиться к объявлению пакета а не к подпрограмме, родовому объявлению или реализации, или к переименовывающему пакету.

@ Возвратимся теперь к правилам для неполных типов. Как было упомянуто выше правила для неполных типов весьма строги в Аде 95, и кроме любопытного случая ссылки к типу подпрограммы не возможно использовать неполный тип в качестве параметра кроме как в ссылочном параметре.

@ Ада 2005 допускает некоторое послабление этих правил, вводя теговые неполные типы. Мы можем написать:

  1        type T is tagged;

@ и тогда полный тип должен быть тэговым типом. Конечно обратное не допустимо. Если мы имеем только

  1        type T;

@ тогда законченный тип T мог бы быть теговым или нет.

@ Любопытная особенность ada была упомянута во Введении. В Аде 95 мы можем написать:

  1        type T;
  2        ...
  3        type T_Ptr is access all T'Class;

@ При использовании атрибута Class это обещает довольно хитрым способом, которым будет отмечен законченный тип T. Это является анахронизмом в Аде 2005 и перемещено в Приложение J. В Аде 2005 мы должны написать:

  1        type T is tagged;
  2        ...
  3        type T_Ptr is access all T'Class;

@ Большое преимущество представления теговых неполных типов состоит в том, что мы знаем, что с теговыми типами всегда передается справочная информация и, таким образом, нам разрешается использовать теговые неполные типы в качестве параметров.

@ Это преимущество простирается на неполное представление, полученное из limited with выражения. Если тип в пакете явно теговый тогда, неполное полученное представление неполное теговое и таким образом тип может использоваться для параметров.

@ Возвращаясь к пакетам Employess и Departments, вероятно имеет смысл сделать оба типа теговыми, так как вероятно, что типы Employee (Служащий) и Department (Отдел) формируют иерархию. Таким образом, мы можем написать:

  1        limited with Departments;
  2        package Employees is
  3                type Employee is tagged private;
  4                procedure Assign_Employee (E : in out Employee; D : in out Departments.Department'Class);
  5                type Dept_Ptr is access all Departments.Department'Class;
  6                function Current_Department (E : Employee) return Dept_Ptr;
  7                ...
  8        end Employees;
  9
 10        limited with Employees;
 11        package Departments is
 12                type Department is tagged private;
 13                procedure Choose_Manager (D : in out Department; M : in out Employees.Employee'Class);
 14                ...
 15        end Departments;

@ Текст является неcколько громоздким теперь with Классом, опрыснутым подробно вокруг, но мы можем ввести некоторые подтипы, чтобы сократить названия. Мы можем также избежать введения типа Dept_Ptr, так как мы можем использовать анонимный ссылочный тип для функционального результата упомянуто в предыдущей статье. Таким образом мы имеем:

  1        limited with Departments;
  2        package Employees is
  3                type Employee is tagged private;
  4                subtype Dept is Departments.Department;
  5                procedure Assign_Employee(E: in out Employee; D: in out Dept'Class);
  6                function Current_Department(E: Employee) return access Dept'Class;
  7                ...
  8        end Employees;
  9
 10        limited with Employees;
 11        package Departments is
 12                type Department is tagged private;
 13                subtype Empl is Employees.Employee;
 14                procedure Choose_Manager(D: in out Department; M: in out Empl'Class);
 15                ...
 16        end Departments;

@ Заметим, что в Аде 2005 мы можем использовать простой подтип как сокращение для неполного типа таким образом:

  1        subtype Dept is Departments.Department;

@ но у такого подтипа не может быть ограничения или нулевого исключения. В основном это - только переименовывание.

@ Помните, что у нас не может быть use выражения с ограниченным представлением. Кроме того, много проектов запрещают use выражения так или иначе, но разрешают переименования и подтипы для местных сокращений. Это создавало бы большие трудности, если бы такие сокращения не были также доступны при помощи limited with выражений.

@ Жаль, что мы не можем также написать:

  1        subtype A_Dept is Departments.Department'Class;

@ но тогда у Вас не может быть всего в жизни.

@ Подобная ситуация возникает с названиями вложенных пакетов. Они могут быть переименованы, чтобы обеспечить сокращение.

@ Механизм для того, чтобы ломать циклы зависимостей, вводя limited with выражения не означает, что реализация не проверяет все полностью в строгой Аде путь. Это только, что некоторые проверки, возможно, придется задержать. Детали зависят от реализации.

@ Для человеческого читателя это очень полезно, которые используют выражения, не позволены вместе с ограниченным с выражениями, так как это устраняет любое сомнение относительно местоположения вовлеченных типов. Это вероятно помогает плохим компиляторам также.

@ Читатели могли бы заинтересоваться, чтобы знать, что эта тема была одним из самых трудных, чтобы решить удовлетворительно в дизайне Ады 2005. В целом семь различных версий искусственного интеллекта 217 были разработаны. Это выбранное решение находится на отражении безусловно лучшее и было фактически номер 6.

@ Многие обходы цикла в Аде 95 относительно неполных типов также закрыты в Аде 2005.

@ Один такой обход цикла иллюстрирован следующим (это - Ада 95)

  1        package P is
  2                ...
  3        private
  4                type T; -- an incomplete type
  5                type ATC is access all T'Class; -- it must be tagged
  6                X : ATC;
  7                procedure Op (X : access T); -- primitive operation
  8                ...
  9        end P;

@ Неполный тип T объявлен в частной части пакета P. Ссылочный тип ACT тогда объявлен и так как это - класс, широкий, это подразумевает, что тип T должен быть теговый (читатель знает из обсуждения выше об этой особенности, помещён в Приложение J в Аде 2005). Полный тип T тогда объявлен в теле. Мы также объявляем примитивную операцию Op типа T в частной части.

@ Однако, прежде, чем тело P будет объявлено, ничто в Аде 95 не препятствует тому, чтобы мы написали частному потомку таким образом:

  1        private package P.C is
  2                procedure Naughty;
  3        end P.C;
  4        package body P.C is
  5                procedure Naughty is
  6                begin
  7                        Op (X); -- a dispatching call
  8                end Naughty;
  9        end P.C;

@ и процедура Naughty может вызвать операцию Op диспетчеризации. Проблема состоит в том, что мы обязаны компилировать этот запрос прежде, чем тип T будет закончен и таким образом прежде, чем местоположение его тэга будет известно.

@

@ Эта проблема предотвращена в Аде 2005 по правилу, что, если у неполного типа, объявленного в частной части, есть примитивные операции тогда, завершение не может быть задержано к телу.

@

@ Подобные проблемы возникают со ссылками к типам подпрограммы. Таким образом, как упомянуто выше, Ада 95 пропусков

  1        type T;
  2        type A is access procedure (X : in out T);

@ В Аде 2005 завершение T не может быть задержано к телу. И при этом мы не можем объявить такуюссылку к типу подпрограммы, если у нас только есть неполное представление T, вляющегося результатом limited with выражение.

@ Другое изменение в Аде 2005 может быть иллюстрировано примером Отделов и Служащих. Мы можем написать:

  1        limited with Departments;
  2        package Employees is
  3                type Employee is tagged private;
  4                procedure Assign_Employee (E : in out Employee; D : in out Departments.Department'Class);
  5                type Dept_Ptr is access all Departments.Department'Class;
  6                ...
  7        end Employees;
  8        with Employees; use Employees;
  9        procedure Recruit (D : Dept_Ptr; E : in out Employee) is
 10        begin
 11                Assign_Employee (E, D.all);
 12        end Recruit;

@ В Аде 95 есть правило, которое говорит, что "Вы не должны разыменовывать неполный тип". Это предотвратило бы запрос Assign_Employee, который ясно безопасен. Было бы странным потребовать, чтобы у Новичка был неограниченный with- выражением для Отделов, чтобы позволить запрос Assign_Employee. Соответственно правило изменено в Аде 2005 так, чтобы разыменование неполного представления было только запрещено когда используется как префикс как, например, в D'Size.

@ ENG RUS

TOP BACK NEXT

2010-10-24 00:26:54

. .