Is there a set of general rules/guidelines that can help to understand when to prefer pragma Pure, pragma Preelaborate, or something else entirely? The rules and definitions presented in the standard (Ada 2012), are a little heavy-going and I'd be grateful to read something that's a little more clear and geared towards the average case.
If I wanted to be thorough without fully understanding the "why" of it, can I simply try:
- Mark the package spec with
pragma Pure; - If it doesn't compile, try
pragma Preelaborate; - If that fails, then I've done something tricky and either need to
pragma Elaborateunits on awith-by-withbasis, or rethink the package layout.
While this might work (does it?), because it's recommended to mark a package as Pure whenever possible (likewise with Preelaborate), however it seems a bit brain damaged and I'd prefer to understand the process a bit better.
pragma PureYou should use this on any package which does not have an internal state. It tells the user of the package that calls to any subprograms cannot have side effects, because there is no internal state they could change. So a function declared at library level inside a pure package will always return the same result when called with the same parameters.
The Ada implementation is allowed to cache return values of functions of a pure package, and to omit calls to subroutines if their return values won't be used because of these requirements. However, you can violate the constraints by calling imported subroutines (e.g. from a C library) inside your pure package (these may change some internal state which the Ada compiler doesn't know of). If you're evil, you can even import Ada subroutines from other parts of the software with
pragma Importto bypass the requirements ofpragma Pure. Needless to say: If you're doing anything like this, don't usepragma Pure.Edit: To clarify the circumstances when calls may be omitted, let me quote the ARM:
GNAT, for example, additionally defines that any subroutines that take a parameter of type
System.Addressor a type derived from it are not considered pure even if they are defined in a pure package, because the location the address points to may be altered, but GNAT does not know what kind of structure the address points to and therefore cannot run any checks about whether the referenced value of the parameter has been changed.pragma PreelaborateThis tells the compiler that the package won't execute any code at elaboration time (i.e. before the main procedure starts executing). At elaboration time, the following constructs will execute:
begin ... endblock at library levelYou generally should avoid these things if you don't need them. Use
pragma Preelaboratewherever possible, it tells the caller that he can safely use the package without executing anything at elaboration time.If something doesn't compile with one of these pragmas when you think it should, look into why it doesn't compile. It may help you discover problems with your package implementation or structure. Don't just drop the pragma when it doesn't compile. As the constraint affects possible constraints on any packages that depend on yours, you should always choose the strictest applicable pragma.