[an error occurred while processing this directive]

HP OpenVMS Systems Documentation

Content starts here

HP Pascal for OpenVMS
User Manual


Previous Contents Index

2.2 Interfaces and Implementations

If your application requires, you can use a method of creating and inheriting environment files that minimizes the number of times you have to recompile compilation units. This method involves the division of module declarations into two separate modules: an interface module and an implementation module. The interface module contains data that is not likely to change: constant definitions, variable declarations, and external routine declarations. The implementation module contains data that may change: bodies of the routines declared in the interface module, and private types, variables, routines, and so forth.

The interface module creates the environment file that is inherited by both the implementation module and by the program. Figure 2-2 shows the inheritance process.

Figure 2-2 Inheritance Path of an Interface, an Implementation, and a Program


Consider this code fragment from the interface module in Example 2-1 (see Section 2.4):


[ENVIRONMENT( 'interface' )]
MODULE Graphics_Interface( OUTPUT );

   {Globally accessible type}

{Provide routines that manipulate the shapes:}
PROCEDURE Draw( s : Shape ); EXTERNAL;
PROCEDURE Rotate( s : Shape ); EXTERNAL;
PROCEDURE Scale( s : Shape ); EXTERNAL;
PROCEDURE Delete( s : Shape ); EXTERNAL;

   {Module initialization section}

END.

The code contained in the interface is not likely to change often. The implementation code can change without requiring recompilation of the other modules in the application. Consider this code fragment from the implementation module in Example 2-2 (see Section 2.4):


[INHERIT( 'Interface' )]   {Predeclared graphics types and routines}
MODULE Graphics_Implementation( OUTPUT );

[GLOBAL] PROCEDURE Rotate( s : Shape );
   BEGIN
   WRITELN( 'Rotating the shape :', s.t );
   END;

To compile, link, and run the code in Examples 2-1, 2-2, and 2-3 (the main program), use the following commands:


$ PASCAL GRAPHICS_INTERFACE
$ PASCAL GRAPHICS_IMPLEMENTATION
$ PASCAL GRAPHICS_MAIN_PROGRAM
$ LINK GRAPHICS_MAIN_PROGRAM, GRAPHICS_IMPLEMENTATION,-
_$ GRAPHICS_INTERFACE
$ RUN GRAPHICS_MAIN_PROGRAM

If you need to change the code contained in any of the routine bodies declared in the implementation module, you do not have to recompile the program to reflect the changes. For example, if you have to edit the implementation module, you can regenerate the application with the following commands:


$ EDIT GRAPHICS_IMPLEMENTATION
$ PASCAL GRAPHICS_IMPLEMENTATION
$ LINK GRAPHICS_MAIN_PROGRAM, GRAPHICS_IMPLEMENTATION,-
_$ GRAPHICS_INTERFACE
$ RUN GRAPHICS_MAIN_PROGRAM

In this manner, interfaces and implementations can save you maintenance time and effort. In addition, the interface and implementation design allows you to better predict when cascading inheritance may provide maintenance problems. Figure 2-3 shows two forms of cascading.

Figure 2-3 Cascading Using the Interface and Implementation Design


If the compilation units creating environment files are designed to contain both interface and implementation declarations, the cascading in column A may lead to more recompiling, more relinking, and more multiply declared identifiers. The design shown in column B does not always provide easy maintenance, but it is more likely to do so. For example, if each interface provided a different kind of constant or type (as determined by your application) and if the constants and types are not derived from one another, the inheritance path in column B may be quite efficient and orderly, and may require little recompiling and relinking.

Do not place the following in an implementation module:

  • Nonstatic types and variables at the module level
  • A module initialization section (TO BEGIN DO)
  • A module finalization section (TO END DO)

These restrictions are necessary because HP Pascal cannot determine the order of activation of initialization and finalization sections that do not directly follow an environment-file inheritance path. Since implementation modules do not create environment files, the initialization and finalization sections in those modules are effectively outside of any inheritance path. Also, if you use the previously listed objects in implementation modules, there may be attempts to access data that has not yet been declared. Consider the following example:


{In one file:}
[ENVIRONMENT( 'interface' )]
MODULE Interface;
PROCEDURE x; EXTERNAL;
END.


{In another file:}
[INHERIT( 'interface' )]
MODULE Implementation( OUTPUT );
VAR
   My_String : STRING( 10 );

[GLOBAL] PROCEDURE x;
   BEGIN
   WRITELN( My_String );
   END;

TO BEGIN DO
   My_String := 'Okay';
END.

In the previous example, it is possible for you to call procedure x (in some other module that also inherits INTERFACE.PEN) before the creation and initialization of the variable My_String. You can circumvent this problem by using a routine call to initialize the variable and by moving the code to the interface module, as shown in the next example:


{In one file:}
[ENVIRONMENT( 'interface' )]
MODULE Interface;
VAR
   My_String : STRING( 10 );

PROCEDURE x; EXTERNAL;
PROCEDURE Initialize; EXTERNAL;

TO BEGIN DO
   Initialize;
END.


{In another file:}
[INHERIT( 'interface' )]
MODULE Implementation( OUTPUT );

[GLOBAL] PROCEDURE x;
   BEGIN
   WRITELN( My_String );
   END;

[GLOBAL] PROCEDURE Initialize;
   BEGIN
   My_String := 'Okay';
   END;
END.

2.3 Data Models

Using separate compilation and a few other features of HP Pascal (including initial states, constructors, the HIDDEN attribute, and TO BEGIN DO and TO END DO sections), you can construct models for creating, distributing, isolating, and restricting data in an application.

Of course, the design of the data model depends on the needs of a particular application. However, to show some of the power of HP Pascal features used in conjunction, Examples 2-1, 2-2, and 2-3 in Section 2.4 create a generic graphics application. Consider the following code fragment from Example 2-1:


TYPE
   Shape_Types = ( Rectangle, Circle ); {Types of graphics objects}

   Shape( t : Shape_Types ) = RECORD
                                    {Starting coordinate points}
      Coordinate_X, Coordinate_Y : REAL VALUE 50.0;
      CASE t OF                           {Shape-specific values}
         Rectangle : ( Height, Width : REAL VALUE 10.0 );
         Circle    : ( Radius        : REAL VALUE 5.0 );
      END;

{Provide routines that manipulate the shapes:}
PROCEDURE Draw( s : Shape ); EXTERNAL;
PROCEDURE Rotate( s : Shape ); EXTERNAL;
PROCEDURE Scale( s : Shape ); EXTERNAL;
PROCEDURE Delete( s : Shape ); EXTERNAL;

The interface module provides an interface to the rest of the application. This module contains types and external procedure declarations that the data model chooses to make available to other compilation units in the application; other units can access these types and routines by inheriting the generated environment file.

The type Shape_Types defines two legal graphical objects for this application: a circle and a rectangle. The type Shape can be used by other units to create circles and rectangles of specified dimensions. This code uses a variant record to specify the different kinds of data needed for a circle (a radius value) and a rectangle (height and width values).

Since the type has initial-state values, any variable declared to be of this type receives these values upon declaration. Providing initial states for types that are included in environment files can prevent errors when other compilation units try to access uninitialized data.

The initial states in this code are specified for the individual record values. You can also provide an initial state for this type using a constructor, as follows:


   Shape( t : Shape_Types ) = RECORD
      Coordinate_X, Coordinate_Y : REAL;
      CASE t OF
         Square : ( Height, Width : REAL );
         Circle : ( Radius        : REAL );
      END VALUE [ Coordinate_X : 50.0; Coordinate_Y : 50.0;
                  CASE Circle OF [ Radius : 5.0 ] ];

If you use constructors for variant records, you can only specify an initial state for one of the variant values. If you need to specify initial states for all variant values, you must specify the initial states on the individual variants, as shown in Example 2-1.

The interface module also declares routines that can draw, rotate, scale, and delete an object of type Shape. The bodies of these routines are located in the implementation module. The interface module also contains a TO BEGIN DO section, as shown in the following code fragment:


[HIDDEN] PROCEDURE Draw_Logo; EXTERNAL;

{
Before program execution, display a logo to which the main
program has no access.
}
TO BEGIN DO
   Draw_Logo;

As with the other routines, the body of Draw_Logo is located in the implementation module. The HIDDEN attribute prevents compilation units that inherit the interface environment file from calling the Draw_Logo routine. This ensures that the application only calls Draw_Logo once at the beginning of the application.

Using this design, the interface module can provide graphical data and tools to be used by other compilation units without the other units having to worry about implementation details. The actual details are contained in one implementation module. For example, the routine bodies are contained in the implementation module. Consider the following code fragment from Example 2-2:


{Declare routine bodies for declarations in the interface}
[GLOBAL] PROCEDURE Draw( s : Shape );
   BEGIN
   CASE s.t OF
      Circle    : WRITELN( 'Code that draws a circle' );
      Rectangle : WRITELN( 'Code that draws a rectangle' );
      END;
   END; {Procedure Draw}

The routine bodies of the external routines declared in the interface module are located in the implementation module. The code in each of the routines uses the actual discriminant of parameter s to determine if the shape is a circle or a rectangle and draws the shape. If this code needs to change, it does not require that you recompile the code in Examples 2-1 or 2-3 in Section 2.4.

Example 2-2 also contains code that is isolated and hidden from other compilation units that inherit the interface environment file. Consider the following code fragment from the interface module:


[GLOBAL] PROCEDURE Draw_Logo;
   VAR
      Initial_Shape  : Shape( Circle ) {Declare object}
         VALUE [ Coordinate_X : 50.0;
                 Coordinate_Y : 50.0;
                 CASE Circle OF
                 [Radius       : 15.75;]];
   BEGIN
   WRITELN( 'Drawing a company logo' );
   Draw( Initial_Shape );
   {Code pauses for 30 seconds as the user looks at the logo.}
   Delete( Initial_Shape );
   WRITELN;
   {Ready for the rest of the graphics program to begin.}
   END;

In the graphical data model, you may wish to define a company logo, and you may wish to display that logo on the screen before any other graphical data is drawn or displayed. This code declares the variable Initial_Shape. Since this variable is declared locally to Draw_Logo and since Draw_Logo is contained in a module that does not produce an environment file, other modules that may have access to the interface environment file do not have access to this variable. In this application, you may not wish to give other compilation units the power to alter the company logo.

The code in the interface's TO BEGIN DO section, which executes before any program code, displays the company logo and deletes it to begin the application. Consider again the compilation process for interfaces, implementations, and programs:


$ PASCAL GRAPHICS_INTERFACE
$ PASCAL GRAPHICS_IMPLEMENTATION
$ PASCAL GRAPHICS_MAIN_PROGRAM
$ LINK GRAPHICS_MAIN_PROGRAM, GRAPHICS_IMPLEMENTATION,-
_$ GRAPHICS_INTERFACE
$ RUN GRAPHICS_MAIN_PROGRAM

HP Pascal executes the TO BEGIN DO section according to the inheritance order of environment files. Remember that HP Pascal cannot determine the order of execution for TO BEGIN DO sections contained in implementation modules, so do not use them there.

Using this design, you can allow different sites that run the graphics application to access global data through the interface module. One location can maintain and control the contents of the implementation module, shipping the implementation's object module for use at other sites. You can use this method for other types of sensitive data or data that needs to be maintained locally.

2.4 Separate Compilation Examples

Example 2-1 shows an interface module that creates the environment file INTERFACE.PEN. This environment file is inherited in Examples 2-2 and
in 2-3.

Example 2-1 An Interface Module for Graphics Objects and Routines

{
Source File: graphics_interface.pas
This module creates an interface to graphical data and routines.
}
[ENVIRONMENT( 'interface' )]
MODULE Graphics_Interface;
TYPE
   Shape_Types = ( Rectangle, Circle ); {Types of graphics objects}

   Shape( t : Shape_Types ) = RECORD
                                    {Starting coordinate points:}
      Coordinate_X, Coordinate_Y : REAL VALUE 50.0;
      CASE t OF                           {Shape-specific values}
         Rectangle : ( Height, Width : REAL VALUE 10.0 );
         Circle    : ( Radius        : REAL VALUE 5.0 );
      END;

{Provide routines that manipulate the shapes:}
PROCEDURE Draw( s : Shape ); EXTERNAL;
PROCEDURE Rotate( s : Shape ); EXTERNAL;
PROCEDURE Scale( s : Shape ); EXTERNAL;
PROCEDURE Delete( s : Shape ); EXTERNAL;
[HIDDEN] PROCEDURE Draw_Logo; EXTERNAL;

{
Before program execution, display a logo to which the main
program has no access.
}
TO BEGIN DO
   Draw_Logo;
END.

Example 2-2 shows the implementation of the routines declared in Example 2-1.

Example 2-2 An Implementation Module for Graphics Objects and Routines

{
Source File: graphics_implementation.pas
This module implements the graphics routines and data declarations
made global by the interface module.
}
[INHERIT( 'Interface' )]   {Predeclared graphics types and routines}
MODULE Graphics_Implementation( OUTPUT );

{Declare routine bodies for declarations in the interface:}
[GLOBAL] PROCEDURE Draw( s : Shape );
   BEGIN
   CASE s.t OF
      Circle    : WRITELN( 'Code that draws a circle' );
      Rectangle : WRITELN( 'Code that draws a rectangle' );
      END;
   END; {Procedure Draw}

[GLOBAL] PROCEDURE Rotate( s : Shape );
   BEGIN
   WRITELN( 'Rotating the shape :', s.t );
   END;

[GLOBAL] PROCEDURE Scale( s : Shape );
   BEGIN
   WRITELN( 'Scaling the shape :', s.t );
   END;

[GLOBAL] PROCEDURE Delete( s : Shape );
   BEGIN
   WRITELN( 'Deleting the shape :', s.t );
   END;

[GLOBAL] PROCEDURE Draw_Logo;
   VAR
      Initial_Shape  : Shape( Circle ) {Declare object}
         VALUE [ Coordinate_X : 50.0;
                 Coordinate_Y : 50.0;
                 CASE Circle OF
                 [Radius       : 15.75;]];
   BEGIN
   WRITELN( 'Drawing a company logo' );
   Draw( Initial_Shape );
   {Code pauses for 30 seconds as the user looks at the logo.}
   Delete( Initial_Shape );
   WRITELN;
   {Ready for the rest of the graphics program to begin.}
   END;
END.

Example 2-3 shows a main program and its use of the types and routines provided by the interface module.

Example 2-3 A Graphics Main Program

{
Source File: graphics_main_program.pas
This program inherits the interface environment file, which gives it
access to the implementation's declarations.
}
[INHERIT( 'Interface' )]  {Types and routines in interface module}
PROGRAM Graphics_Main_Program( OUTPUT );

VAR
   My_Shape : Shape( Rectangle )
      VALUE [ Coordinate_X : 25.0;
              Coordinate_Y : 25.0;
              CASE Rectangle OF
              [Height : 12.50; Width : 25.63]];
BEGIN
{
You cannot access the variable Initial_Shape, because it is in the
implementation module, and that module does not create an environment
file.

You can work with My_Shape.  If you did not provide initial values in
this declaration section, the module Graphics_Interface provided
initial values for the schema type Shape.
}
Draw(   My_Shape );
Scale(  My_Shape );
Rotate( My_Shape );
Delete( My_Shape );
END.

To compile, link, and run the code in Examples 2-1, 2-2, and 2-3, enter the following:


$ PASCAL GRAPHICS_INTERFACE
$ PASCAL GRAPHICS_IMPLEMENTATION
$ PASCAL GRAPHICS_MAIN_PROGRAM
$ LINK GRAPHICS_MAIN_PROGRAM, GRAPHICS_IMPLEMENTATION,-
_$ GRAPHICS_INTERFACE
$ RUN GRAPHICS_MAIN_PROGRAM
Drawing a company logo
Code that draws a circle
Deleting the shape :    CIRCLE
Code that draws a rectangle
Scaling the shape : RECTANGLE
Rotating the shape : RECTANGLE
Deleting the shape : RECTANGLE


Previous Next Contents Index