Ада-95. Компилятор GNAT

       

Управление порядком элаборации


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

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

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

Однако, при разработке больших систем, такое вмешательство бывает необходимым.

Во-первых, существует несколько способов указать компилятору, что данный модуль потенциально не содержит никаких проблем связанных с элаборацией:

Пакеты, которые не требуют наличия тела.

Ада не допускает наличие тела для библиотечного пакета, который не требует наличия тела.

Это подразумевает, что можно иметь пакет подобный следующему:

package Definitions is

generic

type m is new integer; package Subp is

type a is array (1 .. 10) of m; type b is array (1 .. 20) of m; end Subp; end Definitions;

В данном случае, пакет, который указывает в спецификаторе with пакет Definitions, может безопасно конкретизировать пакет Definitions.Subp

поскольку компилятор способен определить очевидное отсутствие тела

Директива компилятора pragma Pure

Данная директива накладывает строгие ограничения на модуль, гарантируя, что обращение к любой подпрограмме модуля не повлечет за собой никаких проблем элаборации.

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

Директива компилятора pragma Preelaborate

Данная директива накладывает несколько менее строгие ограничения на модуль чем директива Pure.

Однако эти ограничения остаются достаточно значительными, чтобы гарантировать отсутствие проблем при вызове подпрограмм модуля.

Директива компилятора pragma Elaborate_Body




Данная директива требует, чтобы элаборация тела модуля была осуществлена сразу после элаборации спецификации.

Предположим, что модуль A содержит данную директиву, а модуль B указывает в спецификаторе with модуль A.

Напомним, что стандартные правила требуют, чтобы элаборация спецификации модуля A

была выполнена перед элаборацией модуля, который указывает модуль A в спецификаторе with.

Указание этой директивы компилятора в модуле A

говорит о том, что элаборация тела модуля A

будет выполнена перед элаборацией B.

Таким образом, обращения к модулю A будут безопасны и не нуждаются в дополнительных проверках.

Примечательно, что в отличие от директив Pure и Preelaborate

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

Вернемся к примеру с модулями Unit_1 и Unit_2.

Если поместить директиву Elaborate_Body в Unit_1, а модуль Unit_2 оставить без изменений, то порядок элаборации будет следующий:

Spec of Unit_2 Spec of Unit_1 Body of Unit_1 Body of Unit_2

В этом случае подразумевается, что нет необходимости проверять вызов Func_1 из модуля Unit_2

поскольку он должен быть безопасным.

Вызов Func_2 из модуля Unit_1, когда результат вычисления выражения Expression_1 равен 1, может быть ошибочным.

Следовательно, ответственность за отсутствие подобной ситуации возлагается на программиста.

Когда все модули содержат директиву компилятора Elaborate_Body, то проблемы порядка элаборации отсутствуют, кроме случаев когда вызовы подпрограмм осуществляются из тела, забота о котором в любом случае возлагается на программиста.

Следует заметить, что повсеместное использование этой директивы не всегда возможно.

В частности, для показанного ранее примера с модулями Unit_1 и Unit_2, когда оба модуля содержат директиву Elaborate_Body, отсутствие возможности определить правильный порядок элаборации очевидно.

Показанные выше директивы компилятора позволяют гарантировать клиентам безопасное использование серверов, и такой подход является предпочтительным.



Следовательно, маркирование модулей как Pure или Preelaborate, если это возможно, является хорошим правилом.

В противном случае следует маркировать модули как Elaborate_Body.

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

Таким образом, для клиентов предусмотрены дополнительные методы управления порядком элаборации серверов от которых эти клиенты зависят:

Директива компилятора pragma Elaborate (unit)

Эта директива помещается после указания спецификатора with, и она требует, чтобы элаборация тела указанного модуля unit осуществлялась перед элаборацией модуля в котором указывается эта директива.

Эта директива используется когда в процессе элаборации текущий модуль вызывает, прямо или косвенно, какую-либо подпрограмму модуля unit.

Директива компилятора pragma Elaborate_All (unit)

Это более строгая версия директивы Elaborate.

Рассмотрим следующий пример:

Модуль A указывает в with модуль B и вызывает B.Func в процессе элаборации Модуль B указывает в with модуль C, и B.Func вызывает C.Func

Если поместить директиву Elaborate (B) в модуль A, то это гарантирует, что элаборация тела B будет выполнена перед вызовом, однако элаборация тела C перед вызовом не выполняется.

Таким образом, вызов C.Func может стать причиной возбуждения исключения Program_Error.

Результат действия директивы Elaborate_All более строгий.

Она требует, чтобы была осуществлена предварительная элаборация не только тела того модуля, который указан в директиве (и в спецификаторе with), но и тела всех модулей, которые используются указанным модулем (для поиска используемых модулей используется транзитивная цепочка спецификаторов with).

Например, если поместить директиву Elaborate_All (B) в модуль A, то она потребует, чтобы перед элаборацией модуля A

была осуществлена элаборация не только тела модуля B, но и тела модуля C, поскольку модуль B указывает модуль C в спецификаторе with.

Теперь можно продемонстрировать использование этих правил для предупреждения проблем связанных с элаборацией, в случаях, когда не используются динамическая диспетчеризация и ссылочные значения для подпрограмм.



Такие случаи будут рассмотренны несколько позже.

Суть правила проста.

Если модуль содержит код элаборации, который может прямо или косвенно осуществить вызов подпрограммы модуля указанного в спецификаторе with, или конкретизировать настраиваемый модуль расположенный внутри модуля указанного в спецификаторе with, то в случае, когда модуль, который указан в спецификаторе with, не содержит директиву Pure, Preelaborate или Elaborate_Body, клиент должен указывать директиву Elaborate_All для модуля, который указан в спецификаторе with.

Соблюдение этого правила гарантирует клиенту, что вызовы и конкретизация настраиваемых модулей могут быть выполнены без риска возбуждения исключения.

Если это правило не соблюдается, то программа может попасть в одно из четырех состояний:

Правильный порядок элаборации отсутствует:

Отсутствует порядок элаборации, который соблюдает правила учитывающие использование любой из директив Pure, Preelaborate или Elaborate_Body.

В этом случае, компилятор Ada95 обязан распознать эту ситуацию на этапе связывания, и отвергнуть построение исполняемой программы.

Порядок элаборации, один или более, существует, но все варианты не правильны:

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

Существует несколько вариантов порядка элаборации, некоторые правильны, некоторые - нет:

Подразумевается, что программист не управляет порядком элаборации.

Таким образом, редактор связей может выбрать или не выбрать один из правильных вариантов порядка элаборации, и программа может не возбуждать или возбуждать исключение Program_Error

во время выполнения.

Этот случай является наихудшим, поскольку программа может перестать выполняться после переноса в среду другого компилятора, или даже в среду другой версии того же самого компилятора.

Порядок элаборации, один или более, существует, все варианты правильны:

В этом случае программа выполняется успешно.



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

Следует обратить внимание на одно дополнительное преимущество соблюдения правила Elaborate_All, которое заключается в том, что программа продолжает оставаться в идеальном состоянии даже в случае изменения тел некоторых подпрограмм.

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

Введение ссылочных типов для подпрограмм усложняется обработкой элаборации.

Трудность заключается в том, что не возможно определить вызываемую подпрограмму на этапе компиляции.

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

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

Это может быть достигнуто соответствующей организацией последовательности описаний, если подпрограмма расположена в текущем модуле, или использованием директивы Pure, Preelaborate или Elaborate_Body, когда подпрограмма расположена в другом модуле, который указан в спецификаторе with.

Если тело используемой подпрограммы, для которого необходимо выполнить элаборацию, не известно в точке создания ссылочного значения, то любое используемое ссылочное значение должно подвергаться динамической проверке, и такая динамическая проверка должна возбуждать исключение Program_Error

в случае, когда элаборация тела не осуществлена.

При использовании динамической диспетчеризации для тэговых типов необходимые динамические проверки генерируются аналогичным образом.

Это означает, что в случае вызова любой примитивной операции тэгового типа, который выполняется до элаборации тела примитивной операции, будет возбуждаться исключение Program_Error.

Copyright (C) А.Гавва V-0.4w май 2004

Содержание раздела