何时使用Pragma Pure / Preelaborate

时间:2013-10-14 03:44:57

标签: ada pragma elaboration

是否有一套通用规则/指南有助于了解何时首选pragma Purepragma Preelaborate或其他内容? standard (Ada 2012)中提出的规则和定义有点沉重,我很感激阅读一些更为清晰的内容并针对普通案例。

如果我想彻底了解它的“原因”,我可以试试:

  • 使用pragma Pure;
  • 标记包规范
  • 如果无法编译,请尝试pragma Preelaborate;
  • 如果失败了,那我就做了一些棘手的事情,要么pragma Elaborate - 依据 - with with单位,要么重新考虑包装布局。

虽然这个可能工作(是吗?),因为建议尽可能将包标记为Pure(同样使用Preelaborate),但是它看起来有点脑损伤,我更愿意理解这个过程好一点。

2 个答案:

答案 0 :(得分:18)

pragma Pure

您应该在没有内部状态的任何包上使用它。它告诉用户包调用任何子程序不能有副作用,因为没有内部状态可以改变。因此,在使用相同参数调用时,在纯类包内的库级声明的函数将始终返回相同的结果。

允许Ada实现缓存纯包的函数的返回值,并且如果由于这些要求而不能使用它们的返回值,则省略对子例程的调用。但是,您可以通过调用纯包中的导入子例程(例如,从C库中)来违反约束(这些可能会改变Ada编译器不知道的某些内部状态)。如果您是邪恶的,您甚至可以使用pragma Import从软件的其他部分导入Ada子例程,以绕过pragma Pure的要求。不用说:如果您正在做这样的事情,请不要使用pragma Pure

修改:为了澄清可能省略来电的情况,让我引用ARM

  

如果库单元被声明为纯,那么如果在调用后不需要结果,则允许实现省略对库单元的库级子程序的调用。类似地,它可以省略这样的调用并简单地重用先前调用在同一子程序上产生的结果,前提是没有任何参数是有限类型的,并且所有参考实际参数的地址和值,以及所有by-copy-in实际参数的值与之前调用时的值相同。即使子程序在调用时产生其他副作用,此权限也适用。

例如,GNAT还定义了任何采用类型System.Address的参数或从其派生的类型的子例程都不被认为是纯的,即使它们是在纯包中定义的,因为地址指向的位置可能会被更改,但GNAT不知道地址指向哪种结构,因此无法检查参数的引用值是否已更改。

pragma Preelaborate

这告诉编译器程序包在精化时(即主程序开始执行之前)不会执行任何代码。在详细说明时,将执行以下构造:

  • 库级变量的初始化(可以是函数调用)
  • 在库级声明的任务的初始化(它们可能在主程序之前开始执行)
  • 图书馆级begin ... end区块中的语句

如果你不需要,你通常应该避免这些事情。尽可能使用pragma Preelaborate,它告诉调用者他可以安全地使用包,而无需在精心制作时执行任何操作。

如果某些内容在您认为不应该使用其中一个编译指示进行编译,请查看它为什么不编译。它可以帮助您发现包实现或结构的问题。当它没有编译时,不要放弃编译。由于约束会影响依赖于您的任何包的可能约束,因此您应始终选择最严格的适用编译指示。

答案 1 :(得分:4)

Elaboration Order Handling in GNAT是一本有用的指南。理想情况下,标准规则足以满足大多数程序的要求。编译指示告诉编译器替换您的精化顺序。它们应该用于解决具体问题,而不是凭经验使用。

附录:@ajb强调了pragma之间的一个重要区别。 article cited同意问题中概述的方法(第一和第二项内容):“因此,如果可能的话,一个好的规则是将单位标记为PurePreelaborate,如果不是如果可能,请将它们标记为Elaborate_Body。“它继续讨论情况(子弹三)“这三个pragma都不能使用。”