Rationale for Ada 2005: Exceptions, generics etc
RUSTOPBACKNEXT
ENG |
4. Pragmas and Restrictions
@ Ada 2005 introduces a number of new pragmas and Restrictions identifiers. Many of these were described in the previous paper when discussing tasking and the Real-Time and High Integrity annexes. For convenience here is a complete list giving the annex if appropriate. @ The new pragmas are
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Rationale for Ada 2005: Exceptions, generics etc
@ENGRUSTOPBACKNEXT4. Прагмы и Ограничения
@ Ада 2005 вводит много новых прагм и идентификаторов Ограничений. Большинство их было описано в предыдущей статье при обсуждении управления задачами, приложений реального времени и высоко интегрированных приложений. Для удобства приведём их полный список.
@ Новые прагмы:
@ Новые идентификаторы Ограничений:
@ Теперь обсудим подробно прагмы и идентификаторы Ограничений базового языка и не обсуждаемые ранее.
@ Сначала рассмотрим прагму Assert и связанную с ней прагму Assertion_Policy. Их синтаксис следующий:
|
@ Первый параметр прагмы Assert - булево выражение, второй (опциональный) параметр - строка. Здесь возможно любое выражение имеющее своим результатом тип Boolean.
@ Параметр прагмы Assertion_Policy определяет политику прагмы Assert. Возможны два варианта Check и Ignore. Дальнейшая политика может быть определена реализацией.
@ Имеется также пакет Ada.Assertions:
|
@ Прагма Assert может использоваться везде, где объявления или операторы разрешены. Таким образом, она может использоваться в списке объявлений:
|
@ и в последовательности операторов:
|
@ Если политика Assertion_Policy установлена в Check тогда вышеупомянутые прагмы эквивалентны следующим проверкам:
|
@ и
|
@ Напомним, что raise оператор без явного сообщения не одно и то же что оператор с явным нулевым сообщением. В первом случае запрос выдаёт Exception_Message, тогда как во втором случае выдаётся пустая строка. Такое же самое поведение происходит и с прагмой Assert - отсутствие сообщения не одно и тоже что пустое сообщение.
@ Если политика в Assertion_Policy установлена в Ignore тогда прагма Assert игнорируется во время выполнения - но конечно синтаксис параметров проверяется во время трансляции.
@ Две процедуры Assert в пакете Ada.Assertions имеют аналогичный прагме Assert эффект за исключением того, что их поведение не зависит от политики Assertions_Policy. Таким образом запрос
|
@ всегда эквивалентен
|
@ Другими словами, мы могли определить поведение
|
@ как эквивалент
|
@ Обратите внимание, что есть две процедуры Assert, одна с и одна без сообщения.
@ Они поддерживают raise операторы с и без явного сообщения.
@ Прагма конфигурации Assertion_Policy управляет поведением Assert в каждом модуле в которым она применяется. Это позволяет определять различную политику в различных частях программы.
@ Реализация могла бы определить другую политику такую как Assume, которая может означать, что компилятор свободен сделать некоторую оптимизацию при условии, если булевы выражения - истина, хотя не было бы никакого кода, чтобы проверить, что они были истиной. Небрежное использование такой политики может привести к ошибочному поведению.
@ Было некоторое беспокойство, что прагмы такие как Assert могли бы быть неправильно поняты, ибо мргли заставить подразумевать, что выполняется статический анализ. Так на языке SPARK [2], выражение - # assert N /= 0 является действительно статическим утверждением, и соответствующие инструментальные средства могут использоваться, чтобы проверить это.
@ Однако, другие языки, такие как язык Eiffel использовали это утверждение динамическим способом как и в Аде 2005, к тому же, много реализаций Ады уже поддерживают прагму Assert, и таким образом ожидается, что не будет никаких проблем с её включением в стандарт.
@ Другая прагма связанная с последовательностью выполнения операторов - No_Return. Она может быть применена к процедуре (не к функции) и утверждает, что процедура никогда не возвращает управление.
|
@ Таким образом, у нас могла бы быть процедура Fatal_Error, которая выводит некоторое сообщение и затем размножает исключение, которое может быть обработано в основной подпрограмме. Например:
|
@ Есть два последствия применения прагмы No_Return. На этапе компиляции проверяется что у соответствующей процедуры нет никаких явных операторов возвращения. На этапе выполнения проверяются попытки процедуры завершиться нормальным способом, в этом случае возбуждается исключение Program_Error. Реализация в состоянии предположить, что вызов процедуры никогда не возвращает управление и, таким образом, определённая оптимизация может быть сделана.
@ У нас мог бы быть вызов Fatal_Error как в
|
@ Если прагма No_Return относится к Fatal_Error, тогда компилятор не должен предусматривать действия после вызова Fatal_Error и не должен выдавать предупреждение в случае непредвиденного конца Pop без положенного в таких случаях оператора return.
@ Прагма No_Return теперь относится к предопределенной процедуре Raise_Exception. Чтобы сделать это возможным, его поведение с Null_Id должно было быть изменено. На Аде 95 мы пишем:
|
@ не делает ничего вообще (и так возвращается в этом случае), тогда как в Аде 2005 это будет вызывать исключение Constraint_Error, и так теперь никогда не возвращается.
@ Мы могли переделать процедуру Fatal_Error чтобы использовать Raise_Exception таким образом:
|
@ Т.к. прагма No_Return относится к Fatal_Error мы также знаем, что Raise_Exception также не может возвратиться.
@ Обработчик исключения для Death в основной подпрограмме может теперь использовать Exception_Message, чтобы выдать сообщение.
@ Напомним, что мы можем теперь также написать:
|
@ вместо того, чтобы называть Raise_Exception.
@ Прагма No_Return является прагмой представления. Если у подпрограммы нет никакой отличной спецификации тогда прагма No_Return помещается в теле (как показано выше). Если у подпрограммы есть отличная спецификация тогда, прагма должна следовать за спецификацией в той же самой трансляции или декларативной области. Таким образом, одна прагма No_Return может относиться к нескольким подпрограммам, объявленным в той же самой спецификации пакета.
@ Важно, что диспетчеризация работает корректно с процедурами, которые не возвращаются. Процедура диспетчеризации невозвращения может быть отменена (overriden) только процедурой невозвращения и, таким образом, у процедуры отмены тоже должна быть прагма No_Return как показано ниже:
|
@ Обратное, естественно, не верно. Процедура, которая действительно возвращается, может быть отменена (overriden) процедурой, который не возвращается.
@ Возможно дать прагму No_Return для абстрактной процедуры, но очевидно не для нулевой процедуры. Прагма No_Return может также быть дана для настраиваемой (generic) процедуры. Она тогда применяется ко всем её экземплярам.
@ Следующая новая прагма - Preelaborable_Initialization.
|
@ Эта прагма касается классификации библиотечных модулей и связана с такими прагмами как Pure и Preelaborate. Она используется с приватным типом и обещает, что у полного типа, указанного параметром, действительно будет preelaborable инициализация. Подробности её использования будут объяснены в следующей статье.
@ Другая новая прагма - Unchecked_Union.
|
@ Параметр должен обозначить беспрепятственный дискриминантный рекордный подтип с вариантной частью. Цель этой прагмы состоит в том, чтобы обеспечить интерфейсную совместимость с объединениями языка C. Следующий пример был дан во Введении:
|
@ Определение прагмы Unchecked_Union гарантирует что: 1) Представление типа не выделяет пространство для каких бы то ни было дискриминантов. 2) Не осуществляется проверка Discriminant_Check. 3) Есть неявное назначение прагмы Convention (C).
@ Вышеупомянутый текст на Аде обеспечивает отображение следующего объединения на C:
|
@ Основная идея здесь состоит в том, что программист C создал тип, который может быть использован для представления числа с плавающей запятой одним из двух способов согласно требуемой точности. Первый использует стандартый тип double (единственный элемент), другой представляет число состоящее из нескольких элементов, количество которых обеспечивает необходимую точность. Последний вариант представлен как структура, состоящая из целого числа задающего число элементов и указателем на первый из них. Эти две различных формы - две альтернативы для объединения.
@ На Аде выбор точности управляется дискриминантным типом Kind, который имеет перечислимый тип:
|
@ В случае единственной точности компонент SP_Value типа Long_Float накладывается на C компонент spvalue типа double.
@ Случай множественной точности более замысловат. Адовский компонент MP_Value_Length накладывается на C компонент length а адовский компонент MP_Value_First ссылочного типа на тип Long_Float накладывается на C компонент типа double*.
@ В нашей Ада программе мы можем объявить переменную:
|
@ пусть мы получаем значение X вызывая некоторую C подпрограмму. В этом случае мы можем объявить массив и отобразить его на последовательность C значений типа double таким образом:
|
@ Элементы A - теперь необходимые значения. Отметим, что мы не используем адовский массив при объявлении Number, потому что могли бы быть проблемы с dope информацией.
@ У адовского типа может быть также невариантная часть, предшествующая вариантной части, и вариантные части могут быть вложены. У него могут быть несколько дискриминантов.
@ Когда объект типа объединения без контроля типов создан, значения должны поставляться для всех его дискриминантов даже при том, что они не сохранены. Это гарантирует, что соответствующие значения по умолчанию могут поставляться и что агрегат содержит правильные компоненты. Однако, так как дискриминанты не сохранены, они не могут читаться. Таким образом мы можем написать:
|
@ У переменной Y, как говорят, есть выводимый дискриминант, тогда как у X нет. Понятно, что игра с объединениями без контроля типов потенциально опасна, однако Ада 2005 вволит определенные правила, которые позволяют избежать некоторых опасностей. Первое правило состоит в том, что предопределенное равенство может использоваться только с операндами с выводимыми дискриминантами; в противном случае возбуждается исключение Program_Error. Так:
|
@ Важно знать, что типы безконтрольных объединений введены в Аду 2005 с единственной целью связать с их помощью интерфейс с программами на C а не для того, чтобы жить опасно. Таким образом рассмотрим:
|
@ Тип T может рассматриваться как Integer или как Float. Но мы не должны использовать типы объединений без контроля как альтернативу преобразованию без контроля типов. Рассмотрим:
|
@ Объект X имеет дискриминантное значение False по умолчанию и, таким образом, имеет нулевое значение типа Integer. В отсутствии прагмы Unchecked_Union попытка читать X.F2 вызовет исключение Constraint_Error из-за дискриминантной проверки. Использование прагмы Unchecked_Union подавляет дискриминантную проверку и, таким образом, присваивание произойдет вполне корректно. Но отметим, что ARM ясно говорит (11.5 (26)), что, если проверка подавляется и соответствующая ошибочная ситуация возникает тогда, программа ошибочна.
@ Однако, присваивание значения типа Float объекту типа Integer, используя Unchecked_Conversion не является ошибочными если не нарушается условие Float'Size = Integer'Size.
@ Последняя прагма, которую мы рассмотрим - Unsuppress:
|
@ Здесь identifier - идентификатор для проверки или возможно All_Checks. Прагма Unsupress, по существу, противоположность существующей прагмы Suppress и может использоваться в тех же самых местах с аналогичными правилами обзора данных.
@ Напомним, что прагма Suppress, дает приложению разрешение опустить проверки, но не требует, чтобы проверки были опущены (они могут быть сделаны аппаратными средствами). Прагма Unsuppress просто отменяет это разрешение. Одна прагма может отменить другую вложенным способом.
@ Если обе даны в одной и той же области, тогда они применяются от одного пункта до другого одна отненяя другую.
@ Наиболее вероятный сценарий их применеия такой, Suppress относится к большой области программы (возможно ко всей), а Unsuppress относится к меньшей области в некоторых пределах. Обратное тоже возможно, но гораздо менее вероятно.
@ Отметим, что Unsuppress, не отменяет неявное подавление проверки Discriminant_Check, предоставленное прагмой Unchecked_Union обсуждаемой выше.
@ Очевидное приложение Unsuppress было бы в операциях с фиксированной точкой, упомянутых в Секции 3 таким образом:
|
@ Использование Unsuppress гарантирует, что проверка переполнения не подавляется, даже если есть глобальный Suppress для всей программы (или пользователь её выключил через командную строку компилятора). Таким образом, исключение Constraint_Error будет происходить там где и положено, и код будет работать правильно.
@ В Аде 95 прагма Suppress имеет синтаксис:
|
@ Второй и дополнительный параметр дает имя объекта, к которому применяется разрешение.
@ Никаких ясных соглашений на этот счёт не имелось и варьировалось в зависимости от реализации. Соответственно в Аде 2005 второй параметр выслан в Приложение J как анахронизм, и синтаксис в базовом языке теперь:
|
@ Для симметрии Приложение J разрешает устаревший параметр On для Unsuppress. Может показаться любопытным, что эта особенность должна родиться уже устаревшей.
@ Много новых идентификаторов Ограничений добавлены в Аде 2005. Один из них - No_Dependence:
|
@ Он указывает, что нет никакой зависимости от библиотечного модуля с указанным именем.
@ Параметр name может быть названием предопределенного модуля, но фактически это может быть любой модуль. Например, возможно было бы быть полезно знать, что нет никакой зависимости от частности определенного реализацией модуля, такого как пакет Superstring таким образом:
|
@ Необходимо быть внимательным и записать по буквам название правильно; если мы напишем Supperstring по ошибке тогда, то компилятор не будет в состоянии помочь нам.
@ Введение No_Dependence означает, что существующий идентификатор Ограничений No_Asynchronous_Control перемещен в Приложение J, так как мы можем теперь написать:
|
@ Точно так же идентификаторы No_Unchecked_Conversion и No_Unchecked_Deallocation также перемещены в Приложение J.
@ Отметим, что идентификатор No_Dynamic_Attachment, который обращается к использованию подпрограмм в пакете Ada.Interrupts не может быть обработан таким образом из-за дочернего пакета Ada.Interrups.Names. Отключение зависимости от Ada.Interrupts исключили бы использование дочернего пакета Name также.
@ Идентификатор ограничений No_Dynamic_Priorities не может быть обработан таким образом по другой причине. В Аде 2005 расширен этот идентификатор так, чтобы это также исключило использование атрибута Priority, и это не было бы исключено, только не говоря зависимости от Ada.Dynamic_Priorities.
@ Два следующих идентификатора Ограничений введены, чтобы поощрить мобильность. Мы можем написать:
|
@ Они не относятся к целому разделению, но только к трансляции или соответствующей среде.
@ Это помогает нам гарантировать, что реализация зависящие от выполнения области программы.
@ Последний новый идентификатор ограничений так же предотвращает нас от небрежного использования особенностей Приложения J таким образом:
|
@ Снова это не относится к целому разделению, но только к трансляции или соответствующей среде. (Это конечно не непосредственно определено в Приложении J.), читатель напомнит, что в Аде 83 у предопределенных пакетов были названия, такие как Text_IO, тогда как в Аде 95 они - Ада.Text_IO и так далее. Чтобы ослабить переход из Ады 83, многие переименования были объявлены в Приложении J таким образом:
|
@ Некоторая проблема состоит в том, что пользователь может написать эти переименования так или иначе, а мы не хотим, чтобы ограничение No_Obsolescent_Features предотвратило это. Кроме того, реализации могли бы фактически осуществить переименования в Приложении J только компилируя их и мы не хотим вынудить реализации использовать некоторый обман, чтобы разрешить пользователю делать это, но не реализацию.
@ Соответственно, ограничение No_Obsolescent_Features относится к этими переименованиями или не является определенной реализацией.
2010-10-24 00:26:56
. .