12 Standard Gnat Packages

 

  <--Last Chapter Table of Contents Next Chapter-->  

 
This section summarizes some of the more than 100 packages that come with the Gnat compiler. These include string handling, operating system binding, and sorts.

 

12.1 Standard String and Character Packages

 
 
Ada Package
Description
C Equivalent
Ada.Characters.Handling
Character operations
Strings.h?
Ada.Strings.Fixed
Ada string operations
Strings.h
Ada.Strings.Bounded
Bounded strings and operations
 -
Ada.Strings.Unbounded
Unbounded strings and operations
 -
Gnat.Case_Util
Just case conversions
 -
Ada.Strings.Unbounded.Text_IO
Text_IO for Unbounded Strings  -
Ada's built-in strings, or "fixed" strings, are made of array of characters. The length of the array determines the bounds of the string. A string that's too short for an array is padded with blanks. Although these strings are fast, they are cumbersome to use and not practical for string-intensive applications. One problems is, although the string type is an array with an undefined upper bound, sooner or later you have to specify an upper bound and run the risk of constraint errors working with arrays of different sizes.
Ada operator "&" concatenates fixed strings: this is the only built-in operator for fixed strings.
There are two alternative strings in Ada. Bounded strings are arrays of strings with a definite maximum size, separate from the length, which eliminates to constraint errors. These strings are still relatively fast, but waste a lot of storage on small strings and you run the risk of overflowing the string. I use 255 character bounded strings as general purpose strings in my programs.
The standard Ada library Ada.Strings.Bounded contains the definition of bounded strings and similar operations to Ada.Strings.Fixed. Because bounded strings have a definite upper bound, the package is generic and has to be instantiated for the maximum length. The library also includes a function to convert a bounded string to a fixed string.

Unbounded strings are strings that can be of any size. They are typically implemented by dynamic allocation, which makes them slow, but they don't waste memory the way bounded strings do and there's no risk over a string overflow. The standard Ada library Ada.Strings.Unbounded contains the definition of unbounded strings and operations on them, including a function to convert an unbounded string to a fixed string.
 

 
C: Unbounded strings are not exactly the same as C strings. For one thing, unbounded strings don't end in null characters. C String support is in the packages Interfaces.C.
with Ada.Text_IO, Ada.Strings.Unbounded.Text_IO;
use Ada.Text_IO, Ada.Strings.Unbounded, Ada.Strings.Unbounded.Text_IO;
procedure unbio is
-- this program demonstrates basic input/output with
-- unbounded strings. These routines are more efficient
-- because they avoid conversion into standard Ada
-- strings
us : Unbounded_String;
begin
  Put_Line( "This program displays information on the screen" );
  Put_Line( "and reads information from the keyboard" );
  New_Line;
  Put_Line( "Type in a string" );
  us := Get_Line;
  New_Line;
  Put_Line( "Put_Line displays a line of text and advances to" );
  Put_Line( "the next line." );
  Put( "The string you typed was " );
  Put_Line( us );
  New_Line;
end unbio;



This program displays information on the screen
and reads information from the keyboard
Type in a string
Uptown Girl, she been looking for a downtown man...
Put_Line displays a line of text and advances to
the next line.
The string you typed was Uptown Girl, she been looking for a downtown man...
For characters, the standard Ada library Ada.Characters.Handling provides basic operations such as conversions between case, tests for types of characters, and conversions two and from 16-bit wide characters.
  Text_IO.Put_Line( Ada.Characters.Handling.To_Upper( 'r' ) );
This example prints 'R' on the screen.

For string handling capabilities, you need to use a package. The standard Ada library Ada.Strings.Fixed contains operations for fixed strings, including extracting substrings, mapping characters from one set to another (for example, upper to lower case), and string searching. There is also an Ada.Strings.Unbounded package containing the same subprograms for unbounded strings, and likewise an Ada.Strings.Bounded for bounded strings.
 

Figure: Standard String Subprograms
Append / & — concatenate one string to another
Element — return the character at a particular index
Replace_Element — replace a character at a particular index
Slice — return a substring
Replace_Slice / Overwrite — replace a substring
Insert — add a string in the midst of the original string
Delete — remove a string in the midst of the original string
Count — return the number of occurrences of a substring
Index — locate a string in the original string
Index_Non_Blank — locate the first non-blank character
Head — return the first character(s) of a string
Tail — return the last character(s) of a string
Trim — remove leading or trailing spaces
* - duplicate the string a specific number of times
Tokenize
Translate — convert a string to a new set of characters using a mapping function
The following program demonstrates many of the standard Ada string subprograms using unbounded strings.
with Ada.Text_IO, Ada.Strings.Unbounded.Text_IO;
use Ada.Text_IO, Ada.Strings.Unbounded,
  Ada.Strings.Unbounded.Text_IO;
procedure strdemo is
-- demonstrate some of the Ada strings subprograms
  teststr : string := "The rich get richer";
  us : Unbounded_String;
begin
  Put_Line( "This program shows some Ada string capabilities" );
  New_Line;
  Put( "Our test string is " );
  Put_Line( teststr );
  New_Line;
  Put_Line( "To_Unbounded_String converts a string to an unbounded string" );
  us := To_Unbounded_String( teststr );
  Put_Line( us );
  New_Line;
  Put_Line( "The length of the string is " & length( us )'img );
  Put_Line( "If we append, ' but not happier', the string is" );
  Append( us, " but not happier" );
  Put_Line( us );
  New_Line;
  Put_Line( "The ampersand will work as well: " & us );
  New_Line;
  Put_Line( "The fifth character is " & Element( us, 5 ) );
  New_Line;
  Put_Line( "Replacing the 20th character, we get" );
  Replace_Element( us, 20, ',' );
  Put_Line( us );
  New_Line;
  Put_Line( "The 5th to 8th charcaters is " & Slice( us, 5, 8 ) );
  New_Line;
  Put_Line( "The first occurence of 'ch' is at " &
    Index( us, "ch" )'img );
  New_Line;
  Put_Line( "The first non-blank character is at " &
    Index_Non_Blank( us )'img );
  New_Line;
  Put_Line( "Replacing the first 'rich' with 'RICH' we get" );
  Replace_Slice( us, 5, 8, "RICH" );
  Put_Line( us );
  New_Line;
  Put_Line( "Inserting 'really ' at the 5th character, we get" );
  Insert( us, 5, "really " );
  Put_Line( us );
  New_Line;
  Put_Line( "Overwriting characters 5 to 8, we get" );
  Overwrite( us, 5, "most" );
  Put_Line( us );
  New_Line;
  Put_Line( "Deleting characters 5 through 11, we get" );
  Delete( us, 5, 11 );
  Put_Line( us );
  New_Line;
  Put_Line( "The first 8 characters at the head of the string are" );
  Put_Line( Head( us, 8 ) );
  New_Line;
  Put_Line( "The last 8 characters at the tail of the string are" );
  Put_Line( Tail( us, 8 ) );
  New_Line;
  -- Count is ambiguous because of the use clauses
  Put_Line( "The count of 'er' is " &
    Ada.Strings.Unbounded.Count( us, "er" )'img );
  New_Line;

 
end strdemo;

This program shows some Ada string capabilities
Our test string is The rich get richer
To_Unbounded_String converts a string to an unbounded string

The rich get richer

The length of the string is 19

If we append, ' but not happier', the string is

The rich get richer but not happier

The ampersand will work as well: The rich get richer but not happier

The fifth character is r

Replacing the 20th character, we get

The rich get richer,but not happier
The 5th to 8th charcaters is rich
The first occurence of 'ch' is at 7
The first non-blank character is at 1

Replacing the first 'rich' with 'RICH' we get

The RICH get richer,but not happier
Inserting 'really ' at the 5th character, we get
The really RICH get richer,but not happier
Overwriting characters 5 to 8, we get
The mostly RICH get richer,but not happier
Deleting characters 5 through 11, we get
The RICH get richer,but not happier
The first 8 characters at the head of the string are
The RICH
The last 8 characters at the tail of the string are
happier
The count of 'er' is 2
There are also a number of libraries dealing with wide strings, strings with 16-bit characters.
If you are only interested in doing case conversions, gnat provides a small package called case_util that does case conversions (and only case conversions) on characters and strings. Use case_util to avoid loading the entire Ada.Characters.Handling library.

The following sample program demonstrates the uses of case_util:

with text_io, gnat.case_util;

use text_io;
procedure casetest is
  teststr : constant string := "This is a TEST_string";
  tempstr : string := ".....................";
begin
  Put_Line( "This is an example of the Gnat string case conversion tools:" );
  New_Line;
  Put_Line( "The original string is '" & teststr & "'" );
  New_Line;
  TempStr := TestStr;
  Gnat.Case_Util.To_Upper( TempStr );
  Put_Line( "Upper case is '" & TempStr & "'" );
  tempstr := teststr;
  Gnat.Case_Util.To_Lower( TempStr );
  Put_Line( "Lower case is '" & TempStr & "'" );
  tempstr := teststr;
  Gnat.Case_Util.To_Mixed( TempStr );
  Put_Line( "Mixed case is '" & TempStr & "'" );
end casetest;

This is an example of the Gnat string case conversion tools:
The original string is 'This is a TEST_string'
Upper case is 'THIS IS A TEST_STRING'
Lower case is 'this is a test_string'
Mixed case is 'This is a test_String'
Ada defines a number of character sets. ASCII is the standard ASCII character set.
To put an "æ" character on displays that support the Latin character set, use
  Put( Ada.Characters.Latin_1.LC_AE_Dipthong );

 

12.1.1 String Performance

The choice of string type to use depends on functionality and performance. Here's a chart showing a series of tests using different string types. The tests were run on my Pentium II 350 MHz.

                    Fixed String (2K) Bounded String     Unbounded
                                      (2K)
 Character Append    0.01 s           72.8 s              16.0 s
 Concat.Two Strings 14.9 s            22.5 s              76.4 s

 Equality           0.18 s             0.09                0.08 s
                                       0.43 s              0.24 s
 Conversion to                        70.0 s              12.4 s
 Fixed              -                 71.8 s              13.6 s

 Insert/Delete      11.8 s            30.6 s             115.2 s
                    15.2 s
 Replace Character   0.3 s             0.7 s               0.9 s
 Determining Length  0.3 s             0.4 s               0.6 s
 Duplication         3.1 s             1.4 s               2.1 s

This is a very informal benchmark and is intended only to demonstrate the kinds of problems associated with different strings. The actual performance will depend on the length of the strings, the optimization in the packages and nature of the string operations. This case used 2K strings (where applicable) and each test was performed 1 million times.

Fixed strings are almost always faster, but they often involve writing work-arounds due to the possibility of constraint errors. Unbounded strings have no constraints but almost always the slower than either fixed or bounded strings. Bounded strings offer a balance between convenience and performance and are the fastest for copying values.

12.2 Advanced Input/Output

12.2.1 GNAT.IO

For small programs that don't need the full capabilities of Text_IO, GNAT provides a package called GNAT.IO. This package can get and put integers, characters and strings. Unlike Text_IO, it's also preelaborated.
with GNAT.IO;
use GNAT.IO;
procedure giodemo is
-- this program demonstrates basic input/output using the
-- GNAT.IO package, a stripped down version of Text_IO
  c : character; -- this is a letter
begin
  Put_Line( "This program displays information on the screen" );
  Put_Line( "and reads information from the keyboard" );
  New_Line;
  Put_Line( "Put_Line displays a line of text and advances to" );
  Put_Line( "the next line." );
  Put( "Put " );
  Put_Line( "displays text, but it doesn't start a new line" );
  Put_Line( "New_Line displays a blank line" );
  New_Line;
  Put_Line( "Get waits for a character to be typed." );
  Put_Line( "Type a key and the Enter key to continue." );
  Get( c );
  Put_Line( "The character you typed was '" & c & "'" );
end giodemo;

This program displays information on the screen
and reads information from the keyboard
Put_Line displays a line of text and advances to
the next line.
Put displays text, but it doesn't start a new line
New_Line displays a blank line
Get waits for a character to be typed.
Type a key and the Enter key to continue.
g
The character you typed was 'g'
[Not complete]
These packages are only useful for simple programs. Usually you will rely on packages/libraries provided for your project.

Text_IO file operations are very limited and are only intended for quick and dirty programs. There are other libraries for more extensive file operations, such as Ada.Sequential_IO and Ada.Direct_IO.

There is also a subpackage for displaying formatted text, such as columns of numbers.

12.2.2 IO_Aux

GNAT's IO_Aux package provides three commonly used functions to Text_IO programs: testing for a file's existence, and reading an unlimited length strings from a text file or a console.
with Ada.Text_IO, GNAT.IO_Aux;
use Ada.Text_IO, GNAT.IO_Aux;
procedure ioaux is
-- this program demonstrates the features of the IO_Aux
-- package
  TestFile : string := "/etc/passwd";
  procedure ScanString( s : string ) is
  begin
    Put_Line( "The string you typed was " & s );
    Put_Line( "It is" & s'length'img & " characters long" );
  end ScanString;
begin
  Put_Line( "This program demonstrates the features of the" );
  Put_Line( "IO_Aux package. This package adds three functions" );
  Put_Line( "to simple Text_IO programs." );
  New_Line;
  Put_Line( "File_Exists tests for a file's existence." );
  if File_Exists( TestFile ) then
    Put_Line( TestFile & " exists" );
  else
    Put_Line( TestFile & " doesn't exist" );
  end if;
  New_Line;
  Put_Line( "Get_Line is the same as Ada.Text_IO's Get_Line" );
  Put_Line( "except that reads a string of unlimited length" );
  Put_Line( "and doesn't return an explicit length value." );
  New_Line;
  Put_Line( "Please type in a string of any length" );
  ScanString( GNAT.IO_Aux.Get_Line );
  New_Line;
  Put_Line( "The third function is a version of Get_Line" );
  Put_Line( "that reads any string from a Text_IO files." );
  New_Line;
end ioaux;
 

This program demonstrates the features of the
IO_Aux package. This package adds three functions
to simple Text_IO programs.
File_Exists tests for a file's existence.
/etc/passwd exists
Get_Line is the same as Ada.Text_IO's Get_Line
except that reads a string of unlimited length
and doesn't return an explicit length value.
Please type in a string of any length
Mary had a little lamb
The string you typed was Mary had a little lamb
It is 22 characters long
The third function is a version of Get_Line
that reads any string from a Text_IO files.

 

12.3 Sequential_IO

A sequential file is a list of similar items saved on a disk (or other long-term storage media). They are similar to a one dimensional array except there is no upper bound, and each item must be processed in sequence (hence the name "sequential"). You can create sequential files of same-length strings, or integer, but most commonly records are used.

You can open an existing sequential IO file, or you can create a new one. When you open or create a file, you have to indicate what file mode you'll be using. "In" mode files can only be read. "Out" mode files can only be written to. "Append" is like out mode except that records are added to the end of an existing file.

The reset procedure changes to a new mode and repositions your program accordingly to the end or beginning of the file.

When you are finished with a sequential file, you can either close it or delete it if you don't need it again.

Because there is no way of knowing how many records are remaining in the file, there is a function called End_of_File that you can check after each read to see if the last item has been read. You can only use End_of_File in In mode--it makes no sense to use it in Out or Append modes since you always write at the end of the file.

The following program writes a couple of customer records to a sequential file and reads them back again:

with Ada.Text_IO, Ada.Sequential_IO, Ada.IO_Exceptions;
use  Ada.Text_IO;

procedure sequentio is
  -- Ada.Sequential_IO example

  type aCustomer is record
       name        : string(1..40);
       amountOwing : float := 0.0;
  end record;
  -- a customer record with two fields

  package aCustomerFile is new Ada.Sequential_IO( aCustomer );
  use aCustomerFile;
  -- instantiate a new package for sequential IO on a file of
  -- customer records

  CustomerFile : aCustomerFile.File_Type;
  -- our customer file
  -- use "aCustomerFile" because Text_IO and Sequential_IO have File_Type

  cr : aCustomer;

begin

  Put_Line( "This is a Ada.Sequential_IO example" );
  New_Line;

  -- create the file

  Create( CustomerFile,
          Mode => Out_File,
          Name => "customer.seq" );

  -- display some statistics

  Put_Line( "We created the file " & Name( CustomerFile ) );
  Put_Line( "We're currently using " & Mode( CustomerFile )'img & " mode" );
  if Is_Open( CustomerFile ) then
     Put_Line( "The file is open" );
  else
     Put_Line( "The file isn't open" );
  end if;
  New_Line;

  -- write the first record

  cr.name := "Tokyo Book Distributors                 ";
  Write( CustomerFile, cr );
  Put_Line( "Writing " & cr.name );

  -- write another record

  cr.name := "General Pizza Inc.                      ";
  Write( CustomerFile, cr );
  Put_Line( "Writing " & cr.name );
  Put_Line( "End_of_File not allowed on Out files" );
  begin
  if End_Of_File( CustomerFile ) then
     Put_Line( "We are at the end of the file" );
  else
     Put_Line( "We aren't at the end of the file" );
  end if;
  exception when Ada.IO_Exceptions.Mode_Error =>
    Put_Line( Standard_Error, "End_of_File caused Ada.IO.Exceptions.Mode_Error" );
  when others =>
    Put_Line( Standard_Error, "Unexpected exception occurred" );
  end;
  New_Line;

  -- change modes using Reset

  Put_Line( "Reset can change the file mode" );
  Put_Line( "Changing to In_File mode" );
  Reset( CustomerFile, In_File );

  -- read first record

  Put_Line( "Reading the next customer" );
  Read( CustomerFile, cr );
  Put_Line( "Read " & cr.name );
  New_Line;

  -- read second record
 
  Put_Line( "Reading the next customer" );
  Read( CustomerFile, cr );
  Put_Line( "Read " & cr.name );
  New_Line;

  -- check the end of the file

  Put_Line( "End_of_File works on In files" );
  if End_Of_File( CustomerFile ) then
     Put_Line( "We are at the end of the file" );
  else
     Put_Line( "We aren't at the end of the file" );
  end if;
  New_Line;

  Put_Line( "Closing file" );
  Close( CustomerFile );

end sequentio;

This is a Ada.Sequential_IO example

We created the file /home/ken/ada/trials/customer.seq
We're currently using OUT_FILE mode
The file is open

Writing Tokyo Book Distributors                 
Writing General Pizza Inc.                      
End_of_File not allowed on Out files
End_of_File caused Ada.IO.Exceptions.Mode_Error

Reset can change the file mode
Changing to In_File mode
Reading the next customer
Read Tokyo Book Distributors                 

Reading the next customer
Read General Pizza Inc.                      

End_of_File works on In files
We are at the end of the file

Closing file

[Form not covered--KB]

12.4 Direct_IO

In relational database programming, you create tables of information. The tables act like arrays that are limited in length by the amount of disk space you have. Each table consists of a series of rows, and each row is divided up into subcategories called columns.

A telephone book, for example, can be considered one large table. Each row contains information about a different person. Each row is subdivided into columns of names, addresses and phone numbers.

Although you could represent a database table using a sequential IO file, it would be very difficult to use. To look up the 1000th entry in the file, you would have to read through the first 999 entries.

The Ada equivalent to a database table is called a direct IO file. Some languages refer to this kind of file as a "random access" file. A direct IO file is called "direct" because you can move directly to any row in the file without having to read any other rows.

The rows in a direct IO file are typically represented by records (athough they can be any data of a known length) and the columns are the fields in the records. Direct IO files can also use variant records--Ada will ensure there is enough space in each entry for the largest variation.

You can open an existing direct IO file, or you can create a new one. When you open or create a file, you have to indicate what file mode you'll be using. "In" mode files can only be read. "Out" mode files can only be rewritten. Unlike sequential IO files, there is also an "In Out" mode which allows you to both read and write records. This is the most common mode for accessing direct IO files.

If you move to a position beyond the end of the file, such as trying to write to row 100 when there are only 50 rows, the other unused rows will be created and filled with zero bytes--ASCII.NUL in characters or strings, 0 in integers and long_integers, and so forth. The only way to shorten a direct IO file is to create a new one, delete the original and copy the new one in place of the original.

There are several useful functions for direct IO files:

The following example program reads and writes customer information using the Ada.Direct_IO package.

with Ada.Text_IO, Ada.Direct_IO, Ada.IO_Exceptions;
use Ada.Text_IO;

procedure dirio is
  -- Ada.Direct_IO example

  type aCustomer is record
       name        : string(1..40);
       amountOwing : float;
  end record;
  -- a customer record with two fields

  package aCustomerFile is new Ada.Direct_IO( aCustomer );
  use aCustomerFile;
  -- instantiate a new package for direct IO on a file of
  -- customer records

  CustomerFile : aCustomerFile.File_Type;
  -- our customer file
  -- use "aCustomerFile" because Text_IO and Direct_IO have File_Type

  cr : aCustomer;

begin

  Put_Line( "This is a Ada.Direct_IO example" );
  New_Line;

  -- create the file

  Create( CustomerFile,
          Mode => Out_File,
          Name => "customer.dir" );

  -- display some statistics

  Put_Line( "We created the file " & Name( CustomerFile ) );
  Put_Line( "We're currently using " & Mode( CustomerFile )'img & " mode" );
  Put_Line( "There are" & Size( CustomerFile )'img & " records" );
  Put_Line( "We are on row " & Index( CustomerFile )'img );
  if Is_Open( CustomerFile ) then
     Put_Line( "The file is open" );
  else
     Put_Line( "The file isn't open" );
  end if;
  New_Line;

  -- write the first record

  cr.name := "Midville Electric                       ";
  Write( CustomerFile, cr );
  Put_Line( "Writing " & cr.name );
  Put_Line( "There are" & Size( CustomerFile )'img & " records" );
  Put_Line( "We are on row " & Index( CustomerFile )'img );
  New_Line;

  -- write the next record on row 7

  cr.name := "New York Distributors                   ";
  Write( CustomerFile, cr, To => 7 );
  Put_Line( "Writing " & cr.name & " to row 7" );
  Put_Line( "There are" & Size( CustomerFile )'img & " records" );
  Put_Line( "We are on row " & Index( CustomerFile )'img );
  Put_Line( "End_of_File not allowed on In files" );
  begin
  if End_Of_File( CustomerFile ) then
     Put_Line( "We are at the end of the file" );
  else
     Put_Line( "We aren't at the end of the file" );
  end if;
  exception when Ada.IO_Exceptions.Mode_Error =>
    Put_Line( Standard_Error, "End_of_File caused Ada.IO_Exceptions.Mode_Error" );
  when others =>
    Put_Line( Standard_Error, "Unexpected exception occurred" );
  end;
  New_Line;

  -- change modes using Reset

  Put_Line( "Reset can change the file mode" );
  Put_Line( "Changing to InOut_File mode" );
  Reset( CustomerFile, InOut_File );

  -- read first record

  Put_Line( "Reading the next customer" );
  Read( CustomerFile, cr );
  Put_Line( "Read " & cr.name );
  New_Line;

  -- read second (undefined record)
 
  Put_Line( "Reading from row 2" );
  Read( CustomerFile, cr );
  Put_Line( "Read " & cr.name );
  New_Line;

  -- read 7th row
 
  Put_Line( "Reading from row 7" );
  Read( CustomerFile, cr, From => 7 );
  Put_Line( "Read " & cr.name );
  New_Line;

  -- check the end of the file

  Put_Line( "End_of_File works on InOut files" );
  if End_Of_File( CustomerFile ) then
     Put_Line( "We are at the end of the file" );
  else
     Put_Line( "We aren't at the end of the file" );
  end if;
  New_Line;

  Put_Line( "Closing file" );
  Close( CustomerFile );

end dirio;

This is a Ada.Direct_IO example

We created the file /home/ada/customer.dir
We're currently using OUT_FILE mode
There are 0 records
We are on row  1
The file is open

Writing Midville Electric                       
There are 1 records
We are on row  2

Writing New York Distributors                    to row 7
There are 7 records
We are on row  8
End_of_File not allowed on In files
End_of_File caused Ada.IO_Exceptions.Mode_Error

Reset can change the file mode
Changing to InOut_File mode
Reading the next customer
Read Midville Electric                       

Reading from row 2
Read 

Reading from row 7
Read New York Distributors                   

End_of_File works on InOut files
We are at the end of the file

Closing file
Note: In this example, reading from the unassigned second record put a row of 40 ASCII.NUL characters on the screen. Because these are non-printable characters, nothing is visible in the results.

[What about objects? How are tags treated? --KB]

Direct_IO files are suitable for small database tables. If you need to work with large amounts of data, you should consider installing one of the free Linux databases (such as PostgreSQL or mySQL) and using them to store and retrieve your data. This is discussed in upcoming chapters.

Alternately, you can write your own database package using a the Linux kernel. seqio, a sequential IO package, is developed in chapter 16.

 

12.5 Formatted Output

Formatted output refers to displaying a value based on a template showing, in general, how the output should look. Because the template is a string, it's easy to visualize the results. This idea is used in languages like COBOL and BASIC (with its PRINT USING command).

The Ada.Text_IO.Editing provides formatted output. The string template is called a picture. The picture can contain the following symbols.

A PICTURE_ERROR is raised if there is a mistake in the layout. A LAYOUT_ERROR is raised if the layout can't be used with a particular value. Using a negative number without specifying a format symbol that allows negative numbers causes a LAYOUT_ERROR.

Before using Text_IO.Editing, the internal generaic package Decimal_Output must be instantiated for a particular numeric type. Only decimal types are allowed.

   type money is delta 0.01 digits 18;
   package formatted_io is new ada.text_io.editing.decimal_output( money );

To_Picture converts a string to a picture type. Pic_String returns the string of the picture type.

    p : picture := To_Picture( "###9.99" );
    s : string := Pic_String( p );

Valid returns true if a string is a valid picture. When Blank_When_Zero parameter is true, a zero represented as an empty string is allowed. By default, the picture string must show something for a zero. Blank_When_Zero can also be used with To_Picture.

    if not Valid( "####9.99" ) then
       Put_Line( Standard_Error, "This is a bad picture string" );
    end if;

Put displays the formatted decimal value. There is also an Image function that returns the results as a string instead of displaying it on the screen. Length returns the length of the formatted output. There is no Put_Line.

    Put( 455.32, pic );
    str := Image( 455.32, pic );
    Put( str, 455.32, pic );

Here is an larger example:

with ada.text_io.editing;
use ada.text_io;
use ada.text_io.editing;

procedure formatted is
   type money is delta 0.001 digits 18;
   package formatted_io is new ada.text_io.editing.decimal_output( money );
   use formatted_io;

   procedure ShowValues( s : string ) is
   begin
     put( "  0.0  and " &s & " => " );
     put( 0.0, To_Picture( s ) );
     new_line;
     put( " 75.12 and " &s & " => " );
     put( 75.12, To_Picture( s ) );
     new_line;
     put( "-75.12 and " &s & " => " );
     begin
        put( -75.12, To_Picture( s ) );
     exception when others =>
        put( "LAYOUT_ERROR" );
     end;
     new_line;
   end ShowValues;

begin
  put_line( "This is an example of Formatted Output" );
  put_line( "--------------------------------------" );
  new_line;

  put_line( "Default currency symbol is " & Default_Currency  );
  put_line( "Default fill character is '" & Default_Fill & "'" );
  put_line( "Default separator character is '" & Default_Separator & "'" );
  put_line( "Default radix mark is '" & Default_Radix_Mark & "'" );
  new_line;

  ShowValues( "99999.99" );
  New_Line;

  ShowValues( "ZZZZ9.99" );
  New_Line;

  ShowValues( "****9.99" );
  New_Line;

  ShowValues( "-$$$9.99" );
  New_Line;

  ShowValues( "+###9.99" );
  New_Line;

  ShowValues( "<###9.99>" );
  New_Line;
end formatted;

This is an example of Formatted Output
--------------------------------------

Default currency symbol is $
Default fill character is ' '
Default separator character is ','
Default radix mark is '.'

  0.0  and ZZZZ9.99 =>     0.00
 75.12 and ZZZZ9.99 =>    75.12
-75.12 and ZZZZ9.99 => LAYOUT_ERROR

  0.0  and -9999.99 =>  0000.00
 75.12 and -9999.99 =>  0075.12
-75.12 and -9999.99 => -0075.12

  0.0  and ****9.99 => ****0.00
 75.12 and ****9.99 => ***75.12
-75.12 and ****9.99 => LAYOUT_ERROR

  0.0  and -$$$9.99 =>    $0.00
 75.12 and -$$$9.99 =>   $75.12
-75.12 and -$$$9.99 => - $75.12

  0.0  and +###9.99 => +  $0.00
 75.12 and +###9.99 => + $75.12
-75.12 and +###9.99 => - $75.12

  0.0  and <###9.99> =>    $0.00 
 75.12 and <###9.99> =>   $75.12 
-75.12 and <###9.99> => ( $75.12)

Put has many parameters used to override default values.

There is also a Wide_Text_IO.Editing for wide string.


 
 

12.6 Calendar Package

Calendar is the standard Ada package for telling time. You can get the current time, compare time values, do time arithmetic and comparisons. There is also a GNAT.Calendar package which extends the Ada.Caledar package with days of the week, second duration, and other features.


Table: North American Federal Holidays and Celebrations

Work schedules (not including small retail stores) often affected by these holidays.

North American Banking (and postal) Holidays include Easter Monday and Victoria Day (Canada).

 
Daylight Savings Time

Daylight Savings time begins, first Sunday in April (but not in Arizona, Hawaii, and parts of southern Indiana).

Daylight Savings Time ends, last Sunday in October (but not in Arizona, Hawaii, and parts of southern Indiana).

 

Table:Other Widely Celebrated North American Observances


The following program demonstrates the basic operations of the calender package.

with text_io, calender;
use calender;

procedure caldemo is
  Year : Year_Number;
  Month : Month_Number;
  Day : Day_Number;
  Seconds : Day_Duration;
  Christmas94 : time;
begin
  Text_IO.Put_Line( "A simple calendar example" );
  Text_IO.New_Line;

  Split( Clock, Year, Month, Day, Seconds );
  Text_IO.Put_Line( "The current date is" &
    Year'img & "/" &
    Month'img & "/" &
    Day'img );
  Text_IO.Put_Line( "It's" & seconds'img &
    " seconds into the day" );
  Text_IO.New_Line;

  Christmas94 := Time_Of( 1994, 12, 25 );
  if Christmas94 < Clock then
    Text_IO.Put_Line( "It's after Christmas 1994" );
  else
    Text_IO.Put_Line( "It's before Christmas 1994" );
  end if;
  Text_IO.New_Line;

  Split( Clock+12.5, Year, Month, Day, Seconds );
  Text_IO.Put_Line( "In 12.5 seconds it will be " &
    Year'img & "/" &
    Month'img & "/" &
    Day'img );
  Text_IO.Put_Line( "And" & seconds'img &
    " seconds into the day" );
end caldemo;



A simple calendar example

The current date is 1998/ 12/ 17
It's 59775.185023000 seconds into the day

It's after Christmas 1994

In 12.5 seconds it will be 1998/ 12/ 17
And 59787.686581000 seconds into the day

The GNAT.Calendar.Time_IO package will write a time value according to a format string, similar to the Linux strftime function.

Easter is one of the hardest holidays to calculate. The following is a program to calculate the date of Easter Sunday:

[This should be rewritten for Ada.Calender -- KB]

with Ada.Text_IO;
use Ada.Text_IO;

procedure easter is

procedure findEaster( year : integer; easter_month, easter_day : out integer ) is
  -- based on the public domain algorithm
  -- by Ed Bernal

  a,b,c,e,g,h,i,k,u,x,z : integer;

begin
      --
      --  "Gauss' famous algorithm (I don't know how or why it works,
      --  so there's no commenting)" -- Ed Bernal
      --

      a := year mod 19;
      b := year / 100;
      c := year rem 100;
      z := b / 4;
      e := b rem 4;
      g := (8*b + 13) / 25;
      h := (19*a + b - z - g + 15) rem 30;
      u := (a + 11*h) / 319;
      i := c / 4;
      k := c rem 4;
      x := (2*e + 2*i - k - h + u + 32) rem 7;
      easter_month := (h-u+x+90) / 25;
      easter_day := (h-u+x + easter_month +19) rem 32;
end findEaster;

  month, day : integer;

begin

  findEaster( 2000, month, day );
  Put( "Easter Sunday 2000 is month " & month'img );
  Put_Line( " and day " & day'img );

end easter;

Easter Sunday 2000 is month  4 and day  23
 

12.7 Tags Package

Ada.Tags contains some utility procedures to for the invisible tags that accompany tagged records, including converting tags to and from strings. The following program shows what the tags package can do and shows tag comparison with the in operator.

with text_io, ada.tags;

procedure t is

  type ParentRec is tagged record

    i : integer;
  end record;

  type ChildRec is new ParentRec with record

    j : integer;
  end record;

  child : ChildRec;

begin

  Text_IO.Put_Line( "Working with Tagged Record Tags:" );
  Text_IO.New_Line;
  Text_IO.Put_Line( "ParentRec has an expanded name of " &
  Ada.Tags.Expanded_Name( ParentRec'Tag ) );
  Text_IO.Put_Line( "ChildRec has an expanded name of " &
  Ada.Tags.Expanded_Name( ChildRec'Tag ) );
  Text_IO.New_Line;
  Text_IO.Put_Line( "ParentRec has an external tag of " &
  Ada.Tags.External_Tag( ParentRec'Tag ) );
  Text_IO.Put_Line( "ChildRec has an external tag of " &
  Ada.Tags.External_Tag( ChildRec'Tag ) );
  Text_IO.New_Line;

  if child in ParentRec'class then

    Text_IO.Put_Line( "child (a child rec) is in ParentRec'class" );
  else
    Text_IO.Put_Line( "This should not happen" );
  end if;
  if child in ChildRec'class then
    Text_IO.Put_Line( "child (a child rec) is in ChildRec'class" );
  else
    Text_IO.Put_Line( "This should not happen" );
  end if;
end t;
 

Working with Tagged Record Tags:

 
ParentRec has an expanded name of T.PARENTREC
ChildRec has an expanded name of T.CHILDREC

ParentRec has an external tag of T.PARENTREC

ChildRec has an external tag of T.CHILDREC

child (a child rec) is in ParentRec'class

child (a child rec) is in ChildRec'class

   

12.8 Tables

Gnat 3.11 introduces gnat.table, a gnat package for creating an aribrary length array (or, for that matter, a link list).

[expand and give example program]
 

12.9 Hash Tables

Gnat provides a generic package for hash tables called gnat.htable. You provide gnat's package with information on the size of the tables, the elements it contains, and a hash function and the instantiation provides Get and Set procedures to put values in and take values out of your hash table.

   
Gnat 3.11: This version of gnat adds remove and iterator subprograms for hash tables.

The following is an example using a hash table of integers.

with text_io, gnat.htable;

use text_io;

procedure hashtest is

-- First, define the items required by gnat.htable

  type HashTableIndex is newinteger range 1..200;

  subtype HashElement is integer;
  EmptyPosition : constant HashElement := 9999;

  function HashOf( he : HashElement ) return HashTableIndex is

  begin
    return HashTableIndex( ( ( he * 91 ) mod integer( HashTableIndex'last ) ) + 1 );
  end HashOf;

  -- OK, instantiate the package

  --
  -- IntTable is a simple HashTable of integer.
  --
  -- Since we're using simple integers, the hash key is the integer
  -- itself and we can compare integers with equals without having
  -- to write a function to compare the values.

  package IntTable is new gnat.htable.simple_htable(

    Header_Num => HashTableIndex, -- how big the table is
    Element => HashElement,-- what's in the table
    No_Element => EmptyPosition,-- what's in empty positions
    Key => HashElement, -- what the key is
    Hash => HashOf,-- function to generate the hash
    Equal => "=" ); -- how to compare things in table

begin

  Put_Line( "This is an example of a hash table of integers" );
  New_Line;

  IntTable.Set( 1, 1 );

  IntTable.Set( 27, 27 );
  Put_Line( "Added 1 and 27 to the hash table" );
  Put_Line( "Empty positions are" & EmptyPosition'img );
  New_Line;
  Put_Line( "Pulling 1 from the hash table =" & IntTable.Get( 1 )'img );
  Put_Line( "Pulling 27 from the hash table =" & IntTable.Get( 27 )'img );
  Put_Line( "Pulling 99 from the hash table =" & IntTable.Get( 99 )'img );
end HashTest;
 


 
This is an example of a hash table of integers
Added 1 and 27 to the hash table
Empty positions are 9999

Pulling 1 from the hash table = 1

Pulling 27 from the hash table = 27
Pulling 99 from the hash table = 9999

[Could use more realistic example--KB]
 

12.10 Bubble and Heap Sorts

Gnat provides two packages for bubble sorting. Both assume that your information is in an array with a lower bound of zero. The zero element is used as temporary space for the sort.

The first, gnat.bubble_sort_g, is a generic. You provide the package with a procedure to move data in the array and a function to check for one value being less than another. The instantiation provides a sort procedure.

with text_io, gnat.bubble_sort_g;

use text_io;

procedure bubble1 is

-- Our table to sort

  type IntegerTable is array( 0..5 ) of integer;

  it : IntegerTable := ( 0, 13, 4, 5, 16, 8 );

  -- Define the items required by a generic gnat bubble sort

  procedure MoveIntegers( From, To : natural ) is

  begin
    it( To ) := it( From );
  end MoveIntegers;

  function CompareIntegers( left, right : natural ) return boolean is

  begin
    return it( left ) < it( right );
  end CompareIntegers;

  -- OK, instantiate the package

  --

  package IntSort is new gnat.bubble_sort_g(

    Move => MoveIntegers, -- how to move two things
    Lt => CompareIntegers ); -- how to compare to things

  procedure ShowTable is

  begin
    for i in IntegerTable'range loop
      Put_Line( i'img & " = " & it( i )'img );
    end loop;
  end ShowTable;

begin

  Put_Line( "This is an example of bubble sorting an integer table" );
  New_Line;
  Put_Line( "The table begins as:" );
  ShowTable;
  IntSort.Sort( it'last ); -- sort elements 1 to top of it array
  New_Line;
  Put_Line( "The sorted table is:" );
  ShowTable;
end bubble1;
 


 
This is an example of bubble sorting an integer table
The table begins as:

0 = 0

1 = 13
2 = 4
3 = 5
4 = 16
5 = 8

The sorted table is:

0 = 13

1 = 4
2 = 5
3 = 8
4 = 13
5 = 16

The second, gnat.bubble_sort_a uses callbacks instead of a generic package. Use this package if you want to conserve memory by avoiding a lot of instantiations of the generic bubble_sort_g. Remember that callbacks must be global, so we can't simple pass the local subprograms we created in bubble1. This time we must store the array and subprograms in a separate package.

with text_io, gnat.bubble_sort_a, inttable;

use text_io, inttable;

procedure bubble2 is

begin
  Put_Line( "This is an example of bubble sorting an integer table" );
  New_Line;
  Put_Line( "The table begins as:" );
  ShowTable;
  gnat.bubble_sort_a.Sort( n => it'last,
    Move => MoveIntegers'access,
    Lt => CompareIntegers'access );
  -- sort elements 1 to top of it array
  New_Line;
  Put_Line( "The sorted table is:" );
  ShowTable;
end bubble2;

package inttable is

-- Our table to sort

  type IntegerTable is array( 0..5 ) of integer;

  it : IntegerTable := ( 0, 13, 4, 5, 16, 8 );

  -- Define the items required by a callback gnat bubble sort

  -- these must be global to work

  procedure MoveIntegers( From, To : natural );

  -- move one item in the table from From position to To position

  function CompareIntegers( left, right : natural ) return boolean;

  -- compare two items in the table and determine if left is less than
  -- than right

procedure ShowTable;

end inttable;

 

with text_io;

use text_io;

package body inttable is

  procedure MoveIntegers( From, To : natural ) is

  begin
    it( To ) := it( From );
  end MoveIntegers;

  function CompareIntegers( left, right : natural ) return boolean is

  begin
    return it( left ) < it( right );
  end CompareIntegers;

  procedure ShowTable is

  begin
    for i in IntegerTable'range loop
      Put_Line( i'img & " = " & it( i )'img );
    end loop;
  end ShowTable;

end inttable;

 


 
This is an example of bubble sorting an integer table
 
The table begins as:

0 = 0

1 = 13
2 = 4
3 = 5
4 = 16
5 = 8

The sorted table is:

0 = 13

1 = 4
2 = 5
3 = 8
4 = 13
5 = 16

The heap sort package works identically, with both generic (heap_sort_g) and callback (heap_sort_a) versions as well. Heap sorts are better suited to large amounts of data. Here's the callback version using the same inttable package we used above.

with  text_io, gnat.heap_sort_a, p;

use text_io, p;

procedure heaptest is

begin
  Put_Line( "This is an example of heap sorting an integer table" );
  New_Line;
  Put_Line( "The table begins as:" );
  ShowTable;
  gnat.heap_sort_a.Sort( n => it'last,
    Move => MoveIntegers'access,
    Lt => CompareIntegers'access );
  -- sort elements 1 to top of it array
  New_Line;
  Put_Line( "The sorted table is:" );
  ShowTable;
end heaptest;
 


 
This is an example of heap sorting an integer table
 
The table begins as:

[this wasn't corrupted before—MS Word bug?]

 

12.11 Regular Expressions

"Regular Expressions" refers to pattern matching for strings: identifying all strings that adhere to a certain pattern. For example, listing all files that end with .ads using the shell command "ls *.ads" is an example of a regular expression.
 
GNAT has two built-in packages for dealing with regular expressions. The first, called "Regexp", performs pattern matching using two different standards. First, it supports standard UNIX shell "file globbing" expressions as described by "man bash". Second, it supports BNF patterns as described in the Ada Reference Manual.

Using the package is a two step process. First, you must compile the expression using the Compile function. Then, you check for a string that matches the expression using the Match function.

The following program demonstrates the Regexp package.

with Ada.Text_IO, GNAT.Regexp;

use Ada.Text_IO, GNAT.Regexp;

procedure regex is

procedure TestMatch( re : Regexp; s : string ) is

begin
  if Match( s, re ) then
    Put_Line( s & " matches the expression" );
  else
    Put_Line( s & " doesn't match the expression" );
  end if;
end TestMatch;

Criteria : Regexp;

begin

  Put_Line( "This program demonstrates GNAT's regular expression" );
  Put_Line( "capabilities. These are used to find text that match" );
  Put_Line( "a certain pattern." );
  New_Line;

  -- UNIX Regular Expressions

  Put_Line( "A 'globbing pattern' is a UNIX shell-style pattern matching" );

  Put_Line( "The pattern 'a*' matches anything starting with the letter 'a'" );
  Criteria := Compile( "a*", Glob => true, Case_Sensitive => true );
  New_Line;
  TestMatch( Criteria, "accounting" );
  TestMatch( Criteria, "President" );
  TestMatch( Criteria, "sundries" );
  New_Line;

  -- BNF Expressions

  Put_Line( "A non-globbing pattern is a BNF pattern, as used in the Ada" );

  Put_Line( "Reference Manual. For example, 'a[a-z]*' means characters" );
  Put_Line( "beginning with 'a' and with any number of letters following." );
  Criteria := Compile( "a[a-z]*", false, true );
  New_Line;
  TestMatch( Criteria, "accounting" );
  TestMatch( Criteria, "sales" );
  New_Line;
end regex;
 


 
This program demonstrates GNAT's regular expression
capabilities. These are used to find text that match
a certain pattern.

A 'globbing pattern' is a UNIX shell-style pattern matching

The pattern 'a*' matches anything starting with the letter 'a'

accounting matches the expression

President doesn't match the expression
sundries doesn't match the expression

A non-globbing pattern is a BNF pattern, as used in the Ada

Reference Manual. For example, 'a[a-z]*' means characters
beginning with 'a' and with any number of letters following.

accounting matches the expression

sales doesn't match the expression

The second Gnat pattern matching package is "Regpat" which interprets full UNIX V7 regular expressions as defined in the "man regexp" Linux man page. Don't be confused by the naming conventions: the Regexp package does not do Linux regular expressions.


 

12.12 Advanced String Processing

[spitbol-style string processing not finished]

 

12.13 GLADE Distributed Processing

GLADE is the free distributed processing package for TCP/IP and gnat. It is distributed separately from the gnat compiler. This should not be confused with the GTK's Glade, the GUI builder for the Gimp Toolkit widgets (http://glade.gnome.org), which has bindings for Ada (http://glade.pn.org/).

GLADE is built into the ALT version of GNAT.

To install GLADE, unpack it and type "configure" and "make install".

GLADE works on partitions, programs designed to run on other computers. Each partition has a channel between itself and another partition. Of course, the partitions can also run concurrently on one computer. You describe the partitions and channels using an Ada-like language called Garlic.

GLADE uses rsh to start partitions, so make sure you don't run the programs under the root login since root is not allowed to run programs via rsh.

[KB: I could install and compile programs with glade, but the communication wasn't working…error in my networking setup or did I not install it properly?]

 

12.14 Basic Math Packages

Type Ada Package Description
Generic Ada.Numerics.Generic_Elementary_Functions basic math for floating point numbers
Short_Float Ada.Numerics.Short_Elementary_Functions basic math for short_float type
Float Ada.Numerics.Elementary_Functions basic math for float type
Long_Float Ada.Numerics.Long_Elementary_Functions basic math for long_float type
Long_Long_Float Ada.Numerics.Long_Log_Elementary_Functions basic math for long_long_float type

Sooner or later, you will ask the question, "So, how do I compute the cosine of a number?" The answer found is the Ada.Numerics.Generic_Elementary_Functions package. This package with the unusually long name is the basic floating point math package. This is a generic package that you instantiate for a particular floating point type. For example, to set up the package for a custom floating point type called "percent",

  with Ada.Numerics.Generic_Elementary_Functions;
  type percent is new float range 0.0..1.0;
  package percentMath is new Ada.Numerics.Generic_Elementary_Functions( percent );
  use percentMath;

The "use percentMath" statement saves us from typing "percentMath." before every function we use.

With percentMath instantiated, we can now perform basic floating point math functions such as

  Put_Line( "20% to the power of 3 is" & percent'image( 0.2**3.0 ) );

As shown in the table at the start of this section, elementary function packages for the basic floating point types are included with Gnat.

The elementary package includes:

FunctionDescription
Sqrt( x )Square Root
Log( x )Natural Logarithm (ln in some other languages)
Log( x, b )Logarithm to base b
Exp( x )Raise e by power x
**Power operator
Sin( x )Sine for x radians
Sin( x, c )Sine for x where cycle range is c (eg. 360 for degrees)
Cos( x )Cosine for x radians
Cos( x, c )Cosine for x where cycle range is c
Tan( x )Tangent for x radians
Tan( x, c )Tangent for x where cycle range is c

There are corresponding functions for arctan, arccot, sinh, cosh, tanh, coth, arccosh, arctanh, artcoth.

Here's an example using the built-in functions for the float type, and creating our own functions for our own percent type:

with Ada.Text_IO, Ada.Numerics.Elementary_Functions,
  Ada.Numerics.Generic_Elementary_Functions;
use Ada.Text_IO, Ada.Numerics.Elementary_Functions;

procedure floatmath is
  type percent is new float range 0.0..1.0;
  package percentMath is new
    Ada.Numerics.Generic_Elementary_Functions( percent );
  use percentMath;

  half : percent := 0.5;

begin
  Put_Line( "Here's some floating point math!" );
  New_Line;

  Put_Line( "4.0 to the power 3.0 is" &
    float'image( 4.0 ** 3.0 ) );

  Put_Line( "The sine of 0.4 radians is" &
    float'image( sin( 0.4 ) ) );

  Put_Line( "The cosine of 180 degrees is" &
    float'image( sin( 180.0, 360.0 ) ) );

  Put_Line( "The square root of 81 is" &
    float'image( sqrt( 81.0 ) ) );

  Put_Line( "50% squared is" &
    percent'image( half ** 2.0 ) );

end floatmath;

Here's some floating point math!

4.0 to the power 3.0 is 6.40000E+01
The sine of 0.4 radians is 3.89418E-01
The cosine of 180 degrees is 0.00000E+00
The square root of 81 is 9.00000E+00
50% squared is 2.50000E-01

When you work with floating point subprograms in libraries outside of Ada, there's a chance that the library will change the floating point arithmetic settings for your CPU. When this happens, use the GNAT.Float_Control package to change your CPU back to GNAT's preferred defaults. There is only one subprogram in this package: reset.

If you are interested in integer operations not covered by the built-in Ada features, the Interfaces package (the package used to interface Ada to other languages) defines several bit-shifting functions. In order to use these functions, you'll need to convert (or derrive) your integer values to one of Interfaces' integer types:

FunctionDescription
Rotate_LeftRotate bits in integer types leftward
Rotate_RightRotate bits in integer types rightward
Shift_LeftShift bits in integer types leftward
Shift_RightShift bits in integer types rightward
Shift_Right_ArithmeticArithmetic shift bits in integer types rightward
C: Shift_Left is the equivalent of the C << operator. Shift_Right is the equivalent of the >> operator.

In Gnat, bit-shifting operations are intrinsic. That is, they act as built-in functions and execute quickly.

Here is an example of shifting integer values.

with Ada.Text_IO, Interfaces;
use Ada.Text_IO, Interfaces;

procedure shiftMath is
  six : unsigned_64 := 6;
begin

  Put_Line( "Time to do a little bit shifting" );
  New_Line;

  Put_Line( "Our integer is" & six'img );
  Put_Line( "In binary, this is" & six'img );

  Put_Line( "Shifted left once is" &
    Shift_Left( six, 1 )'img );
  Put_Line( "Shifted left twice is" &
    Shift_Left( six, 2 )'img );
  Put_Line( "Shifted right once is" &
    Shift_Right( six, 1 )'img );
  Put_Line( "Arithmetic Shifted right once is" &
    Shift_Right_Arithmetic( six, 1 )'img );

end shiftMath;

Time to do a little bit shifting

Our integer is 6
In binary, this is 6
Shifted left once is 12
Shifted left twice is 24
Shifted right once is 3
Arithmetic Shifted right once is 3
 

12.15 Exception Handling and Traceback Packages

Gnat includes packages for working with exceptions. Using these packages, you can add a message to your exceptions, save exceptions, and examine an exception occurences when they are raised.

Traceback is a technique to examine the run-time stack and identify where an exception occurred. Gnat can identify the specific source file and line where an exception occurred. To use tracebacks in Linux, you must compile your program with the -funwind-tables switch and bind with the -E switch.

[Zero-cost exceptions not covered yet--KB]

with Ada.Text_IO,Ada.Exceptions,Gnat.Current_Exception,Gnat.Traceback.Symbolic;
use  Ada.Text_IO,Ada.Exceptions,Gnat.Current_Exception,Gnat.Traceback.Symbolic;

procedure exc is
  e  : exception;
  saved_exception : Exception_Occurrence;

  procedure CrashMe is
  begin
     Raise_Exception( e'identity, "call exec development team" );
  end CrashMe;

begin
   Put_Line( "This is an example of the Ada.Exceptions package" );
   New_Line;

   -- Information about an exception that is not in progress

   Put_Line( "Exception_Name returns a unique name for an exception" );
   Put_Line( "The unique name our exception is " & Exception_Name( e'identity ) );
   New_Line;

   -- Raising an exception with a message

   Put_Line( "raise will raise an exception with no message" );
   New_Line;

   Put_Line( "Raise_Exception will raise an exception with a message" );
   Put_Line( "Raising " & Exception_Name( e'identity ) &
     " with the message 'call exc development team'" );
   Put_Line( "in the subprogram 'CrashMe'." );
   New_Line;
   CrashMe;

exception when occurrence: others =>

   -- Information about an exception that is is in progress

   Put_Line( "-------------------------------------------------------" );
   Put_Line( "An exception has been raised!  Now in exception handler" );
   Put_Line( "The name of the exception is " & Exception_Name( occurrence ) );
   New_Line;
   Put_Line( "Exception_Message returns the message for this exception" );
   Put_Line( "The message for this exception is '" & Exception_Message( occurrence ) & "'" );
   New_Line;
   Put_Line( "Exception_Information provides the name, message and any traceback information:" );
   Put_Line( Exception_Information( occurrence ) );
   New_Line;
   Put_Line( "The Gnat.Current_Exception package contains short-hand" );
   Put_Line( "versions of Exception_Name, Exception_Message, Exception_Information." );
   Put_Line( "These functions assume you're referring to the current exception" );
   Put_Line( "Gnat.Current_Exception.Exception_Name is " & Exception_Name );
   Put_Line( "Gnat.Current_Exception.Exception_Message is '" & Exception_Message & "'");
   New_Line;
   Put_Line( "The Gnat.Traceback.Symbolic package returns the contents of the" );
   Put_Line( "runtime stack.  That is, it shows which subprograms were being" );
   Put_Line( "executed when the exception occurred." );
   Put_Line( "The symbolic traceback is" );
   Put_Line( Symbolic_Traceback( occurrence ) );
   New_Line;
   Put_Line( "Ada.Exceptions can also save and re-raise in-progress exceptions" );
   New_Line;
   Put_Line( "Save_Occurence can save the in-progress exception" );
   Save_Occurrence( saved_exception, occurrence );
   Put_Line( "Exception now saved." );
   New_Line;
   Put_Line( "Reraise_Occurrence will raise an in-progress exception" );
   Put_Line( "Reraising the one we just saved..." );
   Reraise_Occurrence( saved_exception );

   --Allocate/DeallocateMachineState not covered--for zero-cost exceptions

end exc;


This is an example of the Ada.Exceptions package

Exception_Name returns a unique name for an exception
The unique name our exception is EXC.E

raise will raise an exception with no message

Raise_Exception will raise an exception with a message
Raising EXC.E with the message 'call exc development team'
in the subprogram 'CrashMe'.

-------------------------------------------------------
An exception has been raised!  Now in exception handler
The name of the exception is EXC.E

Exception_Message returns the message for this exception
The message for this exception is 'call exec development team'

Exception_Information provides the name, message and any traceback information:
Exception name: EXC.E
Message: call exec development team


The Gnat.Current_Exception package contains short-hand
versions of Exception_Name, Exception_Message, Exception_Information.
These functions assume you're referring to the current exception
Gnat.Current_Exception.Exception_Name is E
Gnat.Current_Exception.Exception_Message is 'call exec development team'

The Gnat.Traceback.Symbolic package returns the contents of the
runtime stack.  That is, it shows which subprograms were being
executed when the exception occurred.
The symbolic traceback is
0x8049cb3 in exc at exc.adb:10


Ada.Exceptions can also save and re-raise in-progress exceptions

Save_Occurence can save the in-progress exception
Exception now saved.

Reraise_Occurrence will raise an in-progress exception
Reraising the one we just saved...
raised EXC.E : call exec development team
Call stack traceback locations:
0x80497eb

Because the reraised exception propogated all the way to the main program and caused it to fail, the final line was actually written to Standard_Error.

 

  <--Last Chapter Table of Contents Next Chapter-->