Use of Alternative Implementations (GNAT User’s Guide for Native Platforms)

#### 3.10.1.4 Use of Alternative Implementations ¶

In some cases, none of the approaches described above are adequate. This can occur, for example, if the set of declarations required is radically different for two different configurations.

In this situation, the official Ada way of dealing with conditionalizing such code is to write separate units for the different cases. As long as this doesn’t result in excessive duplication of code, you can do this without creating maintenance problems. The approach is to share common code as far as possible and then isolate the code and declarations that are different. Subunits are often a convenient method for breaking out a piece of a unit that you need to be conditionalized, with separate files for different versions of the subunit for different targets, where the build script selects the right one to give to the compiler.

As an example, consider a situation where a new feature in Ada 2005 allows something to be done in a really nice way. But your code must be able to compile with an Ada 95 compiler. Conceptually you want to say:

    if Ada_2005 then
       ... neat Ada 2005 code
    else
       ... not quite as neat Ada 95 code
    end if;

where `Ada_2005` is a Boolean constant.

But this won’t work when `Ada_2005` is set to `False`, since the `then` clause will be illegal for an Ada 95 compiler. (Recall that although such unreachable code would eventually be deleted by the compiler, it still needs to be legal. If it uses features introduced in Ada 2005, it’s still illegal in Ada 95.)

So instead, we write

    procedure Insert is separate;

Then we have two files for the subunit `Insert`, with the two sets of code. If the package containing this is called `File_Queries`, then we might have two files

- `file_queries-insert-2005.adb`
- `file_queries-insert-95.adb`

and the build script renames the appropriate file to `file_queries-insert.adb` and then carries out the compilation.

This can also be done with project files’ naming schemes. For example:

    for body ("File_Queries.Insert") use "file_queries-insert-2005.ada";

Note also that with project files, you should use a different extension than `ads` / `adb` for alternative versions. Otherwise, a naming conflict may arise through another commonly used feature: declaring as part of the project a set of directories containing all the sources obeying the default naming scheme.

The use of alternative units is certainly feasible in all situations, and for example the Ada part of the GNAT run-time is conditionalized based on the target architecture using this approach. As a specific example, consider the implementation of the AST feature in VMS. There is one spec: `s-asthan.ads` which is the same for all architectures, and three bodies:

- `s-asthan.adb`

    used for all non-VMS operating systems

- `s-asthan-vms-alpha.adb`

    used for VMS on the Alpha

- `s-asthan-vms-ia64.adb`

    used for VMS on the ia64

The dummy version `s-asthan.adb` simply raises exceptions noting that this operating system feature is not available and the two remaining versions interface with the corresponding versions of VMS to provide VMS-compatible AST handling. The GNAT build script knows the architecture and operating system, and automatically selects the right version, renaming it if necessary to `s-asthan.adb` before the run-time build.

Another style for arranging alternative implementations is through Ada’s access-to-subprogram facility. In case some functionality is to be conditionally included, you can declare an access-to-procedure variable `Ref` that is initialized to designate a ‘do nothing’ procedure, and then invoke `Ref.all` when appropriate. Then, in, some library package, set `Ref` to `Proc'Access` for some procedure `Proc` that performs the relevant processing. The initialization only occurs if the library package is included in the program. The same idea can also be implemented using tagged types and dispatching calls.