Когда использовать Pragma Pure/Preelaborate

Существует ли набор общих правил/рекомендаций, которые могут помочь понять, когда следует отдавать предпочтение pragma Pure, pragma Preelaborate или еще что-то еще? Правила и определения, представленные в стандарте (Ada 2012), немного тяжелые, и я был бы благодарен за чтение чего-то более понятного и ориентированного на средний случай.

Если бы я хотел быть основательным, не до конца поняв "почему", могу ли я просто попробовать:

  • Отметьте спецификацию пакета pragma Pure;
  • Если он не компилируется, попробуйте pragma Preelaborate;
  • Если это не удается, то я сделал что-то сложное и либо должен быть pragma Elaborate единиц на основе with -by- with, либо переосмыслить макет пакета.

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

Ответы

Ответ 1

pragma Pure

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

Реализации Ada разрешено кэшировать возвращаемые значения функций чистого пакета и опускать вызовы подпрограмм, если их возвращаемые значения не будут использоваться из-за этих требований. Однако вы можете нарушить ограничения, вызвав импортированные подпрограммы (например, из библиотеки C) внутри вашего чистого пакета (это может изменить некоторое внутреннее состояние, которое компилятор Ada не знает). Если вы злы, вы можете даже импортировать подпрограммы Ada из других частей программного обеспечения с помощью pragma Import, чтобы обойти требования pragma Pure. Излишне говорить: если вы делаете что-то вроде этого, не используйте pragma Pure.

Изменить: Чтобы прояснить обстоятельства, когда вызовы могут быть опущены, позвольте мне привести ARM:

Если модуль библиотеки объявлен чистым, тогда реализации разрешено пропустить вызов подпрограммы библиотечного уровня библиотечного модуля, если результаты не нужны после вызова. Точно так же он может опустить такой вызов и просто повторно использовать результаты, полученные более ранним вызовом в той же подпрограмме, при условии, что ни один из параметров не имеет ограниченного типа, а также адреса и значения всех фактических параметров для ссылки, а также значения всех фактических параметров by-copy-in, такие же, как и при предыдущем вызове. Это разрешение применяется, даже если подпрограмма вызывает другие побочные эффекты при вызове.

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

pragma Preelaborate

Это сообщает компилятору, что пакет не будет выполнять какой-либо код во время разработки (т.е. до начала выполнения основной процедуры). Во время разработки будут выполнены следующие конструкции:

  • Инициализация переменных уровня библиотеки (это может быть вызов функции)
  • Инициализация задач, объявленных на уровне библиотеки (они могут запускаться до выполнения основной процедуры)
  • Заявления в блоке begin ... end на уровне библиотеки

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

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

Ответ 2

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

Приложение: @ajb подчеркивает важное различие между прагмами. цитированная статья согласуется с подходом, изложенным в вопросе (пули один и два): "Следовательно, хорошим правилом является отметка единиц как Pure или Preelaborate, если это возможно, и если это невозможно, пометьте их как Elaborate_Body, если это возможно." Далее обсуждаются ситуации (пуля три) ", где ни одна из этих трех прагм не может быть использована.