12. Packages, Libraries, and Modules

Introduction

------------------------------------------------------------------------------
-- Ada packages provide both namespace and encapsulation. As such they act
-- both as perl packages and perl modules.

package Alpha is
   name : String := "First";
end Alpha;

package Omega is
   name : String := "Last";
end Omega;

With Ada.Text_Io; use Ada.Text_IO;
with Alpha, Omega;
with Ada.Text_Io;

procedure main is
begin
   Put_Line("Alpha is " & Alpha.name & " ," &
            "Omega is " & Omega.name);
end main;
----------------------
with Cards; -- Make the Cards package visible
use Cards;  -- Place the Cards package within the current scope
-- Both operations happen only at compile time.
----------------------

package Cards is
   type Card is private;
   -- Print the value of a card
   procedure Print(Item : in Card);
   type Deck is private;

   -- Create an initial deck (open a new deck of cards)
   function Fill_Deck return Deck;

   -- Print all the cards remaining in a deck
   procedure Print(Item : in Deck);

   -- Shuffle the deck (randomize the order of the cards in the deck)
   procedure Shuffle(The_Deck : in out Deck);

   -- Deal the next card from the deck
   procedure Deal(The_Card : out Card; From : in out Deck);

   -- Return the number of cards left in the deck
     function Cards_Left(In_Deck : Deck) return Natural;

   -- Deck_Empty exception raised when trying to deal from an empty deck.
   Deck_Empty : Exception;

private
   -- Define the face values of the cards
   type Pips is (Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten,
                 Jack, Queen, King, Ace);
   -- Define the card suits
   type Suits is (Hearts, Spades, Clubs, Diamonds);

   -- A card is defined by its combination of face value
   -- and suit.
   type Card is record
     Pip  : Pips;
     Suit : Suits;
   end record;

   -- Define the number of cards in a standard deck.
   subtype Deck_Index is integer range 1..52;
   -- Cards in the deck are accessed through an order list.
   -- The values in the order list are sorted to create a
   -- shuffled deck.
   type Order_List is array(Deck_Index) of Deck_Index;

   -- A deck is an order list, an index into the order list
   -- indicating the next card to deal, and a count of the
   -- number of cards left (not yeat dealt) in the deck.
   type Deck is record
      This_Order : Order_List;
      Deal_Next  : Deck_Index := Deck_Index'First;
      Num_Left   : Natural := 0;
   end record;
end Cards;
-------------------------

Defining a Module's Interface

----------------------------------------------------------------------
-- Cards.ads
-- Package implementing a standard deck of playing cards
----------------------------------------------------------------------
package Cards is
   type Card is private;
   -- Print the value of a card
   procedure Print(Item : in Card);
   type Deck is private;
   -- Create an initial deck (open a new deck of cards)
   function Fill_Deck return Deck;

   -- Print all the cards remaining in a deck
   procedure Print(Item : in Deck);

   -- Shuffle the deck (randomize the order of the cards in the deck)
   procedure Shuffle(The_Deck : in out Deck);

   -- Deal the next card from the deck
   procedure Deal(The_Card : out Card; From : in out Deck);

   -- Return the number of cards left in the deck

   function Cards_Left(In_Deck : Deck) return Natural;

   -- Deck_Empty exception raised when trying to deal from an empty deck.
   Deck_Empty : Exception;
private
   -- Define the face values of the cards
   type Pips is (Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten,
                 Jack, Queen, King, Ace);
   -- Define the card suits
   type Suits is (Hearts, Spades, Clubs, Diamonds);

   -- A card is defined by its combination of face value
   -- and suit.
   type Card is record
     Pip  : Pips;
     Suit : Suits;
   end record;

   -- Define the number of cards in a standard deck.
   subtype Deck_Index is integer range 1..52;

   -- Cards in the deck are accessed through an order list.
   -- The values in the order list are sorted to create a
   -- shuffled deck.

   type Order_List is array(Deck_Index) of Deck_Index;

   -- A deck is an order list, an index into the order list
   -- indicating the next card to deal, and a count of the
   -- number of cards left (not yeat dealt) in the deck.
   type Deck is record
      This_Order : Order_List;
      Deal_Next  : Deck_Index := Deck_Index'First;
      Num_Left   : Natural := 0;
   end record;
end Cards;
--------------------

Trapping Errors in require or use

--------------------
-- No error trapping required in Ada, since all errors are detected
-- at compile time.
--------------------

Delaying use Until Run Time

--------------------
-- Not possible in Ada
--------------------

Making Variables Private to a Module

-------------------
-- Place the variables in the package implementation, or "body"


with Ada.Numerics.Float_Random;
use Ada.Numerics.Float_Random;
with Ada.Text_Io; use Ada.Text_Io;

package body Cards is
   type Card_Deck is array(Deck_Index) of Card;
-------------
-- Internal Function: Initialize
-- Purpose: Initialize the value of the common Card_Deck
-------------
   function Initialize return Card_Deck is
      Result : Card_Deck;
      Temp_Index : Integer := Deck_Index'First;
   begin
      for The_Suit in Suits loop
         for The_Pip in Pips loop
            Result(Temp_Index) := (The_Pip, The_Suit);
            Temp_Index := Temp_Index + 1;
         end loop;
      end loop;
      return Result;
   end Initialize;

   All_Decks : constant Card_Deck := Initialize;
-- private to the Cards package
----------
-- Procedure: Print
-- Purpose: Print the value of a card on standard output
----------
   procedure Print(Item : in Card) is
      package Pip_Io is new Ada.Text_Io.Enumeration_IO(Pips);
      package Suit_Io is new Ada.Text_Io.Enumeration_Io(Suits);
   begin
      Pip_Io.Put(Item => Item.Pip);
      Put(Item => " of ");
      Suit_Io.Put(Item => Item.Suit);
      New_Line;
   end Print;
----------------
-- Function: Fill_Deck
-- Purpose: Create a new card deck with all cards in order
---------------
   function Fill_Deck return Deck is
      Result : Deck;
      Temp_Index : Integer := Deck_Index'First;
   begin
      for Temp_Index in Deck_Index'Range loop
          Result.This_Order(Temp_Index) := Temp_Index;
      end loop;
      Result.Num_Left := Deck_Index'Last;
      return Result;
   end Fill_Deck;
--------
-- Procedure: Print
-- Purpose: Print all the cards remaining in the deck
--------
   procedure Print(Item : in Deck) is
   begin
      if Item.Num_Left > 0 then
         for Temp_Index in Item.Deal_Next..Deck_Index'Last loop
            Print(All_Decks(Item.This_Order(Temp_Index)));
         end loop;
      else
         Put_Line("The deck is empty.");
      end if;
end Print;

-----------
-- Procedure Swap
-- Exchange two Deck_Index values
-------------
   procedure Swap(Left, Right : in out Deck_Index) is
      Temp : Deck_Index := Left;
   begin
      Left := Right;
      Right := Temp;
   end Swap;
------------
-- Procedure: Shuffle
-- Purpose: Randomize the This_Order array for a deck to force
-- random access to the deck of cards
--  This algorithm is order O(n) and will work with any discrete
--  index type.
--  The Ada.Numerics.Float_Random routine is used so that the
--  random number generator is reset only once per shuffle. This
--  produces more random results than can be achieved by
--  resetting the generator for each iteration as would be needed
--  if the Ada.Numerics.Discrete_Random package had been used.
-----------
   procedure Shuffle(The_Deck : in out Deck) is
      use Ada.Numerics.Float_Random;
      Seed       : Generator;
      Max_Search : Deck_Index := Deck_Index'Pred(Deck_Index'Last);
      Difference : Integer;
      Rand_Value : Integer;
      Swap_Val   : Deck_Index;
   begin
      Reset(Seed);
      The_Deck.Deal_Next := Deck_Index'First;
      The_Deck.Num_Left  := Deck_Index'Last;
      for Index in Deck_Index'First .. Max_Search loop
         Difference := Deck_Index'Pos(Deck_Index'Last) -
                       Deck_Index'Pos(Index);
         Rand_Value := Integer( Random(Seed) * Float(Difference)) +
                       Deck_Index'Pos(Index);
         Swap_Val := Deck_Index'Val(Rand_Value);
         Swap(The_Deck.This_Order(Index), The_Deck.This_Order(Swap_Val));
      end loop;
      The_Deck.Num_Left := Deck_Index'Last;
      The_Deck.Deal_Next := Deck_Index'First;
   end Shuffle;

   procedure Deal(The_Card : out Card; From : in out Deck) is
   begin
      if From.Num_Left > 0 then
         The_Card := All_Decks(From.This_Order(From.Deal_Next));
         From.Num_Left := From.Num_Left - 1;
         if From.Deal_Next < Deck_Index'Last then
            From.Deal_Next := From.Deal_Next + 1;
         end if;
      else
         raise Deck_Empty;
      end if;
   end Deal;

   function Cards_Left(In_Deck : Deck) return Natural is
   begin
      return In_Deck.Num_Left;
   end Cards_Left;
end Cards;
------------------

Determining the Caller's Package

------------------
-- Not possible in Ada. All such determinations are done at compile time
------------------

Automating Module Clean-Up

Keeping Your Own Module Directory

Preparing a Module for Distribution

Speeding Module Loading with SelfLoader

Speeding Up Module Loading with Autoloader

Overriding Built-In Functions

Reporting Errors and Warnings Like Built-Ins

Referring to Packages Indirectly

Using h2ph to Translate C #include Files

Using h2xs to Make a Module with C Code

Documenting Your Module with Pod

Building and Installing a CPAN Module

Example: Module Template

Program: Finding Versions and Descriptions of Installed Modules