Linux Ada Programming
Linux Ada Programming
Do you want to develop Linux applications twice as fast as the C language? Read on!
Latest version: [ North America/Canada ] Download files: [ .zip HTML ] [ PDF ] Unofficial mirrors: [ Europe/Spain ] [ Asia/Japan ] [ OOP Web ]
Search the Big Book for a word or phrase Special Thanks To Jeff Creem (user stack and errno clarifications) Wilhelm Spickermann (CVS) Leonid A. Timochouk (Florist clarifications) Jrgen Pfeifer (Multithreading information) Bernhard Gubanka (Debugging Pools clarifications) Eric L. Schott, Warren W. Gay, Jean-Marc Bourguet (Adjust vs. C++ Copy Constructors) Jean-Marc Bourguet (C++ exceptions) Warren W. Gay (Ada Streams) Rush Kesler (PDF version) Duncan Sands (Fortran, Elaborate_All) Erik Sigra (Automake)
Search
Talk with the author at [email protected] (remove the "-nospam"). Hosted by PegaSoft Canada. Special thanks to the Ada Linux Team.
Table of Contents
i. Preface 1 Introduction 1.1 A Brief History of Linux 1.2 1995: The Year of Ada and Gnat 1.3 Why use Ada? 1.4 Why Ada and Linux? 1.5 Linux Ada Resources 2 Installing Gnat on Linux 2.1 Installing the ALT RPMs 2.2 Installing the ACT Binaries 2.3 Compiling Gnat from its Sources 2.4 Case Study: Installing Gnat 3.11 on over an old Linux Distribution 3 The Integrated Development Environments 3.1 TIA: The Console IDE 3.1.1 Quick Start 3.1.2 TIA Keyboard Legend 3.1.3 The File Menu 3.1.4 The Edit Menu 3.1.5 The Find Menu 3.1.6 The Misc Menu 3.1.7 The Project Menu 3.1.8 The ? Menu 3.2 GRASP-the X windows IDE 3.2.1 Installation 3.2.2 QuickStart 3.2.3 The Project Window 3.2.4 The Source File Window 3.2.5 The Button Bar 3.3 Other Tools and IDEs 3.3.1 VAD 3.3.2 Jessie 3.3.3 RAPID 3.3.3 VIDE 3.3.5 GLIDE
4 From Source Code to Executable 4.1 Gnat Filename Conventions 4.2 Writing Your First Ada Program 4.2.1 Writing a Program with an IDE 4.2.2 Writing a Program without an IDE 4.2.3 After Building 4.3 The Three Step Process 4.4 Gnat Compiling Options 4.4.1 Run-time Error Checking 4.4.2 Checking without Compiling 4.4.3 When you have Too Many Errors 4.5 Gnat Binding Options 4.6 Gnat Linking Options 4.7 Gnatmake Options 4.7.1 So you changed the comments ?
5 Building Large Projects 5.1 Make: the Traditional Project Builder 5.1.1 A Simple Ada Makefile 5.2 Cook: A Parallel Make 5.2.1 Cooking in Parallel 5.2.2 A Simple Ada Cookbook 5.3 Automake and Autoconf: UNIX Portability
6 Development Utilities 6.1 Saving Time with Gnatstub 6.2 Crossreferencing with Gnatxref 6.3 Eliminating Dead Code with Gnatelim 6.4 Execution Stack & Memory Leak Detection 6.5 Conditional Compiling with Gnatprep 6.6 Profiling with gprof 6.7 Shared Libraries Using GnatDLL 6.8 Source as Web Pages Using GnatHTML 6.9 GnatFIND
7 Optimizing Your Project 7.1 Compiler Optimization Options 7.2 Gnat Source Optimization Options 7.3 CPU Optimization Options 7.4 What Differnece Does Optimization Make? 7.5 Working with the Assmebly Source
8 Debugging Your Project 8.1 Limit and the Heap Size 8.2 The Debugging Pragmas 8.3 Identifying Files 8.4 Compiler Info with -gnatG 8.5 Floating Point Numbers 8.6 Gdb: The GNU Debugger 8.7 Code Restrictions
9 Team Development 9.1 Change Logs 9.2 RCS: Revision Control System 9.3 CVS: Concurrent Versions System 9.4 Creating Transcripts with Script 9.5 Timing Execution with Time
10 An Introduction to Ada 10.1 Your Main Program 10.2 Text_IO 10.3 Fundamental Data Types 10.4 Type Attributes 10.5 Operatiors and Expressions 10.6 Variable Declarations 10.7 New Types 10.7.1 Modular Types 10.7.2 Text_IO and New Types 10.8 Aggregate Types
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/book.html (4 of 10) [7/20/2001 11:22:56 AM]
10.9 Enumerated Types 10.10 Procedures and Function 10.11 Flow of Control
11 Advanced Ada Programming 11.1 Packages 11.2 Controlling Elaboration 11.2.1 First line of defense: Pure, Preelaborate and No_Elaboration_Code 11.2.2 Second line of defense: Elaborate, Elaborate_Body, Elaborate_All 11.2.3 Other Elaboration Pragmas 11.3 Objects 11.4 Objects with Automatic Initialization/Finalization 11.5 Multiple Inheritance 11.6 Private Objects 11.7 Generics 11.8 Exceptions 11.9 Dynamic Allocation 11.10 Callbacks 11.10.1 Storage Pools 11.10.2 Access Parameters 11.11 Multithreading 11.11.1 FSU vs. Native Threads 11.11.2 Tasks 11.11.3 Task Types 11.11.4 Protected Items/Types 11.12 Ada Text Streams 11.13 Pragmas 11.14 Low-Level Ada
12 Standard Gnat Packages 12.1 Standard String and Character Packages 12.2 Advanced Input/Output 12.2.1 GNAT.IO 12.2.2 GNAT.IO_Aux 12.3 Sequential_IO 12.4 Direct_IO 12.5 Formatted Output 12.6 Calendar Package
12.7 Tags Package 12.8 Tables 12.9 Hash Tables 12.10 Bubble and Heap Sorts 12.11 Regular Expressions 12.12 Advanced String Processing 12.13 GLADE Distributed Processing [not finished] 12.14 Basic Math Packages 12.15 Exception Handling and Traceback Packages
13 Linux Introduction 13.1 Introduction to Processes 13.1.1 Parents, Children and Families 13.1.2 Ownership and Permissions 13.2 Using System and OsLib.Spawn 13.3 The Linux Environment 13.4 Standard C Libraries 13.5 The Linux Kernel 13.5.1 Kernel Calls 13.5.2 Devices 13.5.3 Proc File System 13.5.4 AudioCD: An Example Program 13.6 Standard Input/Output/Error 13.8 Linux Binary Formats 13.9 Linux Libraries 13.10 Libc5, Libc6 and Upward Compatibility 13.11 Linux Basics
14 Linux Programming 14.1 Gnat OS Library 14.2 Installing Binding Packages 14.3 Catching Linux Signals 14.4 Working with the Command Line 14.4 Linux Environment Variables 14.6 GNAT.Directory_Operations Package 14.7 GNAT.Lock_Files Package
15 Free Ada Bindings 15.1 Using Florist, a POSIX binding 15.2 Using Texttools 15.2.1 Installation 15.2.2 Introduction 15.2.3 Package Overview 15.2.4 Window Overview 15.2.5 Other Useful Window Manager Subprograms 15.2.6 Alerts 15.2.7 Other Predefined Windows 15.2.8 Control Overview 15.2.9 OS Package 15.2.10 UserIO Overview 15.2.11 Appearance and Keys 15.3 Using Ncurses [not finished] 15.4 Using GTK+ Widgets [not finished] 15.5 Using Motif Widgets [not finished] 15.6 Using the TASH TCL Binding [not finished 15.7 Using the Mesa/OpenGL Binding [not finished] 15.8 Engine_3D [not finished]
16 Advanced Linux Programming 16.1 Writing Your Own Bindings 16.2 Linux Errors and Errno 16.3 The Linux Clock 16.3.1 Basic time functions 16.3.2 Timeval Calls - Microsecond Accuracy 16.3.3 Functions Using the tm Record 16.3.4 Time as a String 16.3.5 Timer Functions 16.4 Process Information 16.4.1 Ownership 16.4.2 Other Functions 16.5 Environment Variables 16.6 Multitasking 16.7 Linux File Operations 16.8 Opening and Closing Files 16.9 Directories
16.10 Stdio Files 16.11 Stdio Pipes 16.12 Memory Management 16.13 The Virtual Consoles 16.14 Making Database Queries 16.14.1 mySQL [not finished] 16.14.2 PostgreSQL [not finished] 16.15 Dynamic Loading [not finished] 16.16 A Word on Device Drivers 16.17 Linux Sound 16.17.1 Detecting a Sound Card 16.17.2 Playing Sound Samples 16.17.3 Using the Mixer 16.17.4 Recording Sound Samples [not finished] 16.18 Audio CDs 16.19 Kernel Pipes [not finished] 16.20 Shared Memory [not finished] 16.21 Message Queues [not finished] 16.22 Semaphores [not finished] 16.23 Sockets 16.24 Memory Management 16.25 Exit Procedures
17 Moving C Programs To Ada 17.1 c2ada: Translating Your Programs 17.2 Interfaces.C package 17.3 Interfaces.C.Pointers package 17.4 Interfaces.C_Streams package 17.5 Ada and C Files 17.6 A Word on Interfaces.Fortran
18 Data Structures 18.1 Using the Booch Components 18.1.1 Containers 18.1.2 Iterators 18.1.3 Single linked Lists 18.1.4 Double linked Lists 18.1.5 Bags
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/book.html (8 of 10) [7/20/2001 11:22:56 AM]
18.1.6 Sets 18.1.7 Collections 18.1.8 Queues 18.1.9 Stacks 18.1.10 Deques 18.1.11 Rings 18.1.12 Maps 18.1.13 Binary Trees 18.1.14 AVL Trees 18.1.15 Multiway Trees 18.1.16 Graphs 18.1.17 Smart Pointers 18.1.18 Booch Multithreading
19 Specialized Topics 19.1 Ada Meets Java 19.1.1 The Java Virtual Machine [unfinished] 19.1.2 JGNAT [unfinished] 19.2 ASIS [unfinished]
20 Developing Your Project 20.1 The Project Proposal 20.2 The Design Phase 20.3 The Development Phase 20.4 The Alpha/Beta Release 20.5 Releasing Your Software 20.5.1 A Third Party Library 20.6 Distribution Formats 20.6.1 RPM: Red Hat Package Manager [not finished] 20.6.2 TGZ Packages 20.6.3 TAR.BZ2 Packages 20.6.4 Other Formats 20.7 Man Pages 20.8 Linux Software Map Entry 20.9 Licensing Options
Appendices
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/book.html (9 of 10) [7/20/2001 11:22:56 AM]
Appendix A: The Linux Shell Appendix B: Linux Error Codes Appendix C: Linux Kernel Calls Appendix D: Signals Appendix E: Ioctl parameters Appendix F: Overview of Gnat Packages Glossary
i. Preface
I've been working with Linux since kernel 0.97 and with Gnat since version 2.00. In the past five years or so, I've been frustrated by the lack of documentation on Linux Ada programming. Gnat is one of the most powerful development environments for Linux, certainly superior to C or C++, and yet most people have never heard of it. Those that have often ignore it because they can't find enough documentation to install Gnat, let alone to evaluate it. After my article "Gnat: C++ and Java under Fire", published in the October 1998 edition of Linux Journal, I decided to collect my knowledge of Linux Ada programming and set down what I've learned: from installing Gnat to interfacing with the Linux kernel. I wanted to create a book that had everything I needed in one place to write professional Linux applications. After publishers declined to put it in print because Ada developers are a small (though growing) niche in the Linux market, I decided to publish it online so that the facts about Linux Ada programming would be understood. This document covers basic software development on Linux, a review of the core Ada 95 language, and an introduction to designing programs that work with the Linux kernel and standard C libraries. It also covers some of the Ada bindings that exist for packages like Motif, TCL and GTK+. This book tries to describe Linux specifics whenever possible. This is not another UNIX book recycled with the word "Linux" substituted in. Although many Ada basics are covered, this document assumes the reader is familiar with a high-level programming language such as BASIC, C, C++, Java . Borland Delphi programmers will notice similarities between Delphi and Ada. Because C is the dominant language in the Linux world, the differences between C and Ada are hilighed throughout the text. The document is designed to be used as a reference after it's been read, with many tables and examples covering common Linux programming problems. Although this book covers a lot of material, it is not intended as an exhaustive survey of Linux Ada programming.Linux is in a constant state of development. Refer to your Linux documentation for the latest information and newest features. Also, Ada 95 has several application specific and portability features which are not covered since they are not related to general Linux Ada programming. Because of the fast pace of Linux development, information in this document may be obsolete, or (to paraphrase Douglas Adams) apocryphal or wildly inaccurate. However, most of the facts have been verified against Gnat 3.11 (or a later version), and most of the examples in this document have been compiled under Gnat. Ken O. Burtch, September 1999
1 Introduction
Table of Contents Next Chapter-->
[Rewrite & Expand] Ada 95 is arguably the most powerful development language available for Linux, with features comparable to Java and execution speeds similar to, and sometimes exceeding, C. gnat, the main [perhaps only'check at HBAP?--KB] Ada 95 compiler for Linux, is also absolutely free. This makes a combination that's hard for Linux programmers to ignore.
(1815-1852), considered by some to be the world's first programmer. The original Ada had several shortcomings in the areas of software engineering: Ada programs tended to be big and awkward to maintain over time. In 1990, ANSI began a project to revise Ada, to include object oriented features, hierarchical program libraries, support for other languages, and add-ons for specialized applications like systems programming, real-time systems, distributed information (client/server) systems and scientific programming. The updated language is known as Ada 95. GNAT is a GPL Ada compiler, available for Linux, Windows NT, and many other platforms. It was originally created at New York University. GNAT is owned by Ada Core Technologies (ACT, http://www.gnat.com): although gnat is free, companies who want support can purchase it for a fee. The Linux version of GNAT supports the entire Ada 95 standard, including all optional features. It includes many extensions, like cross-compiling and support for the C++ language. ACT also provides GLADE, a free RPC-based TCP/IP networking implementation Ada 95's distributed systems annex. The GNAT manual describes their compiler as "an industrial-quality Ada 95 compiler, integrated into the GCC retargetable compiler system. GNAT is a complete compiler, validated on several platforms, that includes support for all the Ada 95 annexes specified in the Ada Reference manual. Because of its integration into the GCC system, GNAT is available on a large number of hardware/operating system platforms, and can be used as a cross-compiler from any of its targets to any other one. Because of the common code-generator technology of GCC, GNAT has excellent support for multi-language programming: Ada, C, C++, Fortran, etc. GNAT also represents a substantial improvement in Ada compilation technology. It's [sic] open-system philosophy stands in contrast with the opaque approach of older Ada compilers. There are no hidden and complex central libraries whose use requires a totally new set of commands, and no rigid development environments that often force needless recompilations. While preserving all of Ada's safety, GNAT's source-based model provides the flexibility and efficiency typically encountered in C development environments. Furthermore, GNAT's flexibility greatly facilitates its integration within third-party development environments and CASE tools. A number of standard editors, debuggers, profilers, memory analyzers, test coverage or configuration-management tools, etc. can be used with GNAT, which coexists comfortably with familiar programming tools (unlike older Ada compilation systems)." Fun Fact: When Gnat 3.11p was released, Robert Dewar said that Linux would never be a billion dollar platform and deserved no special consideration by ACT. By the time Gnat 3.12p was released just over a year later, the Red Hat company was worth more than 18 billion dollars, or 40% of the server market. The first platform supported by Gnat 3.12p was Linux.
customer". GNAT was developed closely with gcc, the native C compiler for Linux. Unlike some compilers that translate a program into C and then feed the C program into gcc, gcc has built-in support for the Ada language. Like g++, the GNU C++ compiler, gnat works with gcc, allowing it to produce fast, quality executables without any intermediate steps. This integration gives a lot of flexibility to programmers who want or need to support multiple languages. GNAT has an extensive set of features for trading variables and function calls between Ada and C/C++. It can import C/C++ items into Ada, export Ada items to C/C++. You can also link Ada functions indirectly into Java, using Java's ability to import C++ functions. GNAT comes with over 140 standard libraries. These include numeric and string libraries, file operations, hash tables and sorts. If you would rather work directly with Linux C libraries, a variety of "binding" libraries exist, available for download from the Public Ada Library or The Home of the Brave Ada Programmers. These include bindings for POSIX (that is, the Linux kernel), X Windows, Motif, TCL and WWW CGI applications. The Ada Linux Team prepackage many bindings for use with their version of the Gnat compiler. More and more Linux libraries feature Ada bindings, including ncurses (a standard text screen drawing library) and GTK (the Gimp Toolkit, a graphics package). Although gnat is distributed under the GPL license, gnat and its libraries may be used in commercial applications. The GtkAda mailing list is at http://gtkada.eu.org. The Gnat mailing list is at http://www.diax.ch/users/gdm/gnatlist.htm. The Gnat Glade chat mailing list is at [email protected].
source code prone to a longer lifespan. Ample Libraries - The GNAT compiler comes with many general purpose libraries, and bindings exist for most of the key Linux libraries. Open Source Friendly - Ada's readability and scalability make it an ideal language for open source development. Child packages, for example, make it easy to extend someone else's work without affecting the original source code.
Next Chapter-->
Gnat is a part of the gcc project. The gcc command itself isn't a compiler: it's a program that determines the kind of source file you have and runs the appropriate compiler for you. For example, the Ada compiler is called gnat1, and the C compiler is called cc1. When gcc detects an Ada source file, it runs gnat1 to compile it. Because gcc and Gnat must work as a team, specific versions of Gnat are created for specific versions of gcc. Gnat 3.10p is compiled against the gcc for kernel 2.0.29 (for example, the version of gcc used in the Slackware 3.2 distribution). Gnat 3.11p, 3.12p and 3.13p are compiled against gcc 2.8.1.
To find out which version of gcc you have, run gcc with the -v switch. The standard Gnat distribution from ACT comes with its copy of the correct version of gcc and can install Gnat and its gcc in a separate directory. The binary versions from ACT's web site have C++ support removed, so if you want gcc to support C++ and Ada simultaneously, you'll have to recompile gcc and Gnat from their sources.
It is possible to install one version of gcc overtop of another and to select one version or the other using the gcc -V switch, but gcc must again be recompiled from its (newest) sources to make it aware of the other version. There are patches available via the ALT web site for compiling gnat from the sources for the egcs compiler instead of gcc. egcs (pronounced "eggs") is a variation of gcc designed specifically for Pentium computers. Egcs is based on gcc 2.8.0. Slackware 3.6, for example, used egcs. The egcs optimizations are being merged with gcc for the upcoming release of gcc 3.0. ACT has announced plans to fold Gnat into the Gcc project for Gcc 3.1 and have moved some of their discussions to the gcc mailing list.
The rpm files are built for Red Hat and S.u.S.E. distributions. If you try installing it on another distribution, use --nodep to ignore any package dependency warnings. 1. Download and read the readme file. 2. Download the gnat-3.xxp-runtime* rpm file (where xx is the current version of Gnat and * is the rest of the filename). For older RPMs, this is gnat-3.xxp*. 3. Download the gnat-3.xxp rpm file. For older RPMs, this is gnat-3.xxp-devel*. 4. rpm -i gnat-3.xxp-runtime* 5. rpm -i gnat-3.xxp* 6. Download and install any of the additional Gnat packages you need The rpm files on the ALT site are configured to work with the ALT version of gnat. To install them, simply download them and run rpm with the -i switch. The ALT GNAT build system is available for those wanting to know more about how the RPMs are constructed. Using CVS, you can check our the source code. export CVSROOT=":pserver:[email protected]:/var/cvs" cd $HOME cvs login # (use empty password) cvs -z9 co -d ALT gnuada/alt-build
Gnat 3.12 and older have an additional install option to overwrite you're existing copy of gcc, provided it is right version. Since it is rare that a distribution has the exact same version of gcc, this option is no longer provided. ACT will sometimes release several versions of Gnat for different C libraries. When downloading the binaries, make sure that you download the version compiled against the appropriate C library. This is due to the constantly evolving nature of Linux.
To find out which libc library your distribution uses, examine the /lib/libc.so link to find out which file it points to. For example, if /lib/libc.so points to a libc5 library, then you'll need the libc5 version.
The latest public version from ACT is 3.13p, which has been compiled for gcc 2.8.1. By default, Gnat is installed in a /usr/gnat subdirectory. If you don't have gcc 2.8.1, you can specify a separate directory where gnat will install itself and its own personal copy of gcc 2.8.1. Using this method, you need to perform an additional step. The installation program (doconfig) creates a shell script containing environment variables that you can copy to your shell startup script (under bash, this is usually the .profile file in your home directory). Gcc uses these variables to locate the gnat files. You will need to include the gnat directory in the front of your PATH variable to prevent gnat from using the gcc that came with your Linux distribution. For example, use the shell command: export PATH="/usr/gnat/bin:$PATH" Only use this command when you want to use Gnat since it effectively hides the copy of gcc that came with your distribution. If you don't want gnat to be enabled by default, you can write a short shell script that assigns the environment variables, sets the path, and starts a new shell.
compile a second gcc compiler just for use with Gnat. For gnat 3.13, you'll need the gcc 2.8.1 source code. You should be able to compile an older version of gcc to work with newer C libraries, provided the compiler is only a few months out of date. Now follow the directions to compile Gnat. Make sure libgnat.a is accessible to the linker. If it isn't, copy it to /usr/lib and run ldconfig to update Linux's shared library tables. [More here - KB]
2.4 Case Study: Installing Gnat 3.11 on over an old Linux Distribution
We installed Gnat 3.11p on a Pentium running a Slackware distribution with egcs and lib6. We wanted to replace egcs with gcc 2.8.1 and install the Gnat binaries (compiled for 2.8.1) over top. We first went to the Sunsite mirror which provides Linux compiled binaries of gcc, ready to be unpacked and installed Unfortunately, the readme file reported they had trouble compiling gcc and supplied egcs instead. egcs is based on gcc 2.8.0 which meant that we couldn't use it with gnat 3.11. Instead we downloaded the gcc 2.8.1 source code from a GNU FTP mirror site and prepared to build the compiler from scratch. 1. We ran Gnat doconfig program and select option 1. The gcc path that it's expecting is displayed as i686-pc-linux-gnu. This was going to be our configuration host setting for gcc. 2. We followed the instructions in the gcc INSTALL file. configure --with-gnu-as --with-gnu-ld --enable-threads=posix --host i686-pc-linux-gnu 3. We checked the gcc makefile to make sure i686-pc-linux-gnu was reasonable. It required lib6 and lib6 was installed. The Makefile also showed that the i686 setting is compatible with our Pentium (i586). 4. Before running make, we changed the Makefile's OLDCC variable from cc to /usr/bin/gcc. There was a cpp syntax error while building libgcc1.a, probably the error the Slackware people encountered. We tracked down the line causing the problem in the Makefile and discovered they were calling cc to do the compiling, which doesn't handle the C preprocessor (cpp) properly. Typing in the line at the shell prompt showed that /usr/bin/gcc worked fine while /usr/bin/cc would not. The note in the Makefile said we shouldn't use gcc to avoid circular references in some of the functions (that is, that it might inadvertantly call the 2.8.1 compiler instead of the old 2.7.2.3 compiler), so we made sure we included the full path. 5. make LANGUAGES="c c++" 6. mkdir stage1; make stage1 make CC="stage1/xgcc -Bstage1/" CFLAGS="-g -O2 -fsigned-char" 7. mkdir stage2; make stage2 make CC="stage2/xgcc -Bstage2/" CFLAGS="-g -O2 -fsigned-char" 8. make compare reported no errors. 9. make install CC="stage2/xgcc" -Bstage2/" CFLAGS="-g -O2" LANGUAGES="c c++" 10. gcc -v and gcc -dumpversion reported the correct version. We deleted the old /usr/lib/gcc-lib/i486-linux directory to save some space. 11. We installed gnat by running doinstall If we were doing C++ programming, we would need to install the standard C++ library, libstdc++, as well. In this case, we enabled C++ support to avoid recompiling gcc for C++ in the future. <--Last Chapter Table of Contents Next Chapter-->
There are two IDE's, or Integrated Development Environments, available for gnat and Linux. PegaSoft's TIA (Tiny IDE for Ada) is a text-based IDE, while GRASP is an X-Windows IDE. Both have similar basic features. On the other hand, if you are looking just for text editors with Ada syntax hilighting, many exist for Linux including elvis, emacs/xemacs and nedit.
This IDE is designed for rapid Ada development.To meet this goal, it uses a number of interesting features: q a ddd style debugger q automatic saving - whenever you open a new source file, tia saves your old file q quiet updates - each time a file is saved, TIA will attempt to recompile the file, to reduce the project rebuilding time.
q q
TIA will only update one file at a time to avoid slowing down your machine. automatic spelling correction - When you press return/enter while editing your source file, TIA automatically corrects common spelling mistakes for any of the following words or phrases: procedure, function, package, exception, subtype, "end if;", "end loop;" , "end record;". error hilighting - you can move between compiler errors with a single keypress, and the cursor is automatically positioned at the exact location of the error and the message displayed at the bottom of the window. quick open - you can open recently opened files with a single keypress tight integration with gnat - for example, you can load a package spec and create a body using gnatstub by simply selecting Stub in the File menu. support for keyboard macros
If you are interested in an X Windows IDE, you should read the next section on GRASP.
Down Arrow - 10% Forward in Document End - Bottom of Document Home - Top of Document Left Arrow - Back one line Right Arrow- Forward one line Up Arrow - 10% Back in Document In TIA, the width of the text is limited to size of the edit area. Any lines that are longer that the edit area are denoted with an ellipsis at the end. The edit area does not scroll left or right as it does in pico.
Besides Ada 95, GRASP supports C, C++, Java and VHML source files. It supports operating systems other than Linux and can also work with Ada compilers other than gnat. GRASP is available for download from that GRASP home page at http://www.eng.auburn.edu/grasp.
3.2.1 Installation
1. Download a version of GRASP from the GRASP web site. GRASP uses the Motif widget library. The static version has a copy of Motif included with it: download this version of you don't have a Motif compatible library (such as LessTif). If you have a Motif compatible library, download the dynamic version to save disk space. If you are using LessTif, make sure that the libXm and related files are properly linked in /lib and run ldconfig to ensure Linux sees the changes. 2. Move the tar archive to the location you want to install GRASP in. For example, "/usr/local" would be a good choice. 3. Unpack the grasp archive with "tar xfvz". 4. GRASP requires an environment variable called "GRASP_HOME" to be set so GRASP knows where it was installed. To define GRASP_HOME every time for any user, add the following line to the end of your /etc/profile file: export GRASP_HOME=graspdir/graspada export PATH="$PATH:$GRASP_HOME/bin" where graspdir is the directory where you installed grasp (eg./usr/local").
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/3.html (6 of 10) [7/20/2001 11:30:22 AM]
Login in again to make the changes take effect and type "grasp&" to start GRASP. GRASP provides online help. Extensive documentation is provided in HTML format, but it's not lynx browser friendly. You'll have to use a GUI browser like Netscape.
Show Controls - toggles button bar at top of edit area Show message Bar - toggles message bar at bottom of window The Templates Menu - insert source code for typical Ada multiline statements. The source code must be edited to fit the current program. The Window Menu - opens various grasp windows The Compiler Menu Make - builds the project using the make command defined in command setup (typically make) Compile and Link - builds the project using gnatmake Compile - compiles the source file without building the project Semantic Check - checks the source code for errors Flag Setup - configures the switches for compiling, linking, etc. Command Setup - configures the commands to compile, link, etc. The Run Menu Run - runs the program using popen Run Previous - runs the last file that was run Run File - runs a particular file Cleanup Session - kills any hung processes.Doesn't affect daemons. The CPG Menu Generate CPG - create a Compilexity Profile Graph Weights - configure the CPG weights
3.3.2 Jessie
Jessie is an X Windows IDE for building large projects and designing multiple executables at once. It's an open source project of Silicon Graphics (http://www.sgi.com) and works with multiple languages. ACT has announced that Gnat will provide Jessie support in the future. Jessie is downloadable from http://oss.sgi.com.
3.3.3 Rapid
RAPID is an X Windows GUI Builder that works with TASH (the Ada TCL/TK package). You can draw TCL/TK windows containing Labels, Text Buttons, Radio Buttons, Check Boxes, and other widgets. When you select "Compile", RAPID saves the Ada source code necessary to display using TASH the window you drew. RAPID is available from ALT.
3.3.4 VIDE
VIDE is a C/C++/Java IDE, but it will work with Gnat if configured correctly. However, it doesn't support Ada keyword hilighting.
<--Last Chapter
Table of Contents
Next Chapter-->
Big Online Book of Linux Ada Programming - 4 From Source Code to Executable
You can change the colour used by ls to display these filenames by changing the /etc/DIR_COLORS file. Directions on how to do this are included in the file.
Big Online Book of Linux Ada Programming - 4 From Source Code to Executable
Big Online Book of Linux Ada Programming - 4 From Source Code to Executable
If there is a problem with your program, gnatmake will show you the line number, the position on the line, and an error message describing the problem. If you typed in the program correctly, you should now have an executable file called hello. Run the file by typing hello (or ./hello on some distributions) and Linux will respond by displaying the message Hello World!
Big Online Book of Linux Ada Programming - 4 From Source Code to Executable
Big Online Book of Linux Ada Programming - 4 From Source Code to Executable
q q q q
q q q q q q q q q q
-gnats - synatx check only -gnatt - create tree output file -gnatu - list units being compiled -gnatwm - warning mode(s) m. These include r -gnatwa - show all optional warnings r -gnatwA - show no optional warnings r -gnatwc - warnings for always true/false expressions in statements r -gnatwe - treat warnings as errors r -gnatws - suppress warnings r -gnatwl - warnings on elaboration errors r -gnatwu - warnings on unused variables, uninitialized parameters, unused packages r -gnatyk - check indentifier case -gnatx - suppress cross-reference information -gnaty - Impose line length limit, etc. [? - KB] -gnatzm - generate distribution stubs for m -gnatZ - zero-cost exceptions (default) -gnat83 - enforce old Ada 83 conventions -gnat95 - enforce Ada 95 conventions (default) -mno-486 - create an executable that can run on a Intel 386 or newer -m486 - create an executable that can run on a Intel 486 or newer -mcpu=model - compile an executable for the given cpu model -fstack-check - check for stack overflows
The -gnat switches can be combined together, such as -gnatbcs for -gnatb, -gnatc, and -gnats. Many of the GCC switches listed in 4.5 can be used as well. 4.4.1 Run-time Error Checking Ada has extensive checking for run-time errors. By default, gnat turns off some of these checks to improve the speed of the programs. To turn on all error checking, you need to use -gnato -gnatE switches. To turn off all error checking, you need to use -gnatp. IDE: TIA sets these switches for you based on your choices in the project parameters window. 4.4.2 Checking without Compiling In gnat 3.10, if you want to check a source file without actually compiling it, use the gnatf utility. In gnat 3.11 or later, you can use gcc with the -gnatc option to check a source file. IDE: TIA uses -gnatc when you chose File/Check.
Big Online Book of Linux Ada Programming - 4 From Source Code to Executable
4.4.2 When you have Too Many Errors When you have so many compiling errors that they run off the top of the screen, you can redirect the errors to a file and list them with the less command by adding the following to the end of your compiling command: "2> temp.out; less temp.out".
Big Online Book of Linux Ada Programming - 4 From Source Code to Executable
q q q q
-ws - suppress all binder warnings -v - verbose messages -x - check only, ignore source files. Don't generate a binder program. See -s. -z - same as -n [?-KB]
When linking in libraries, the order of the libraries is important. When libraries depend on each other, libraries must be listed before the libraries that they use.
Big Online Book of Linux Ada Programming - 4 From Source Code to Executable
q q q q
q q q q q q q q q q q q q q q
-jn - on multiprocessor machiens, compile using n processes at once -k - ignore errors, and compile as much as possible -M - create a list of dependences suitable for make's Makefile -i - instead of the current directory, keep intermediate files in directories where their sources are found -m - update dependancies without compiling -n - check dependencies, but don't do anything -o name - save the executable as name -q - quiet - no status messages -s - switch change - recompile if the switches have changed -v - verbose - explain why files are compiled -aIdir - besides the directory of the first file, search directory dir for source files -aOdir - besides the directory of the first file, search directory dir for object and .ali files -Adir - same as -aIdir and -aLdir -Idir - same as -aodir -aIdir -I- - don't look for source files in the directory of the first file -Ldir - besides directory of the first file, look for libraries in directory dir -cargs s - pass switches s to compiler -bargs s - pass switches s to binder -largs s - span>pass switches s to linker (e.g. -largs somefile.o to link in the somefile object file)
The ALT version of Gnat uses uses the name gnatgcc, not gcc, for the GCC compiler.
# Sample Ada makefile # # Assumes main program is named main.adb # # by Ken O. Burtch OBJS = main.o somepackage.o # How to compile Ada files .adb.o: gcc -c $< .SUFFIXES: .adb .o # How to link the main program main: $(OBJS) gnatbind -xf main.ali; gnatlink main.ali clean: rm *.o *.ali core
# # # #
run GNU configure build cook verify cook by running tests install cook on your computer
When you type "cook", cook looks for a file called Howto.cook. This file, called a "cookbook", contains rules, or "recipes", for building a project. Each rule has three parts:
q
targets - the files built with this rule (e.g. the object file names)
ingredients - the files need to do the building (e.g. source code fields) q body - the commands to do the work. these can include other rules. Here is an example Howto.cook file with one rule:
q
main: main.adb { gnatmake main.adb; } The target is "main". To create main, cook must examine main.adb. If main.adb is newer than main, cook creates a new main by running the Gnatmake command. Cook comes with some predefined rules for compiling certain kinds of files. These predefined cookbooks can be attached to your Howto.cook file by using "#include". For example, #include "c" includes basic rules describing the relationships of C files to each other.
The ALT version of Gnat uses uses the name gnatgcc, not gcc, for the GCC compiler. /* /* /* /* /* /* /* /* ---------------------------------------- */ This is a simple Ada Howto.cook cookbook */ */ it assumes the main program is named */ main.adb */ */ by Ken O. Burtch */ ---------------------------------------- */
OBJS = main.o somepackage.o; /* How to compile individual Ada files */ %.o: %.adb { gcc -c %.adb; } /* How to compile individual C files */ /* ( Just in case we want to mix C and Ada ) */ %.o: %.c { gcc -c %.c; } /* How to bind and link the main program */ main: [OBJS] { gnatbind -xf main.ali; gnatlink main.ali; } /* How to clean up intermediate files */ clean: { rm *.o *.ali core; } There are many other features in cook not covered here. More information about cook can be found in the Cook User Manual and the Cook Reference Manual.
checking checking ... checking checking checking checking checking checking creating creating ... creating
for POSIXized ISC... no for ranlib... ranlib for libc5... no for dlopen in -ldl... yes for MIT PThreads... no for Base LinuxThreads... yes for LinuxThreads w/ErrorCheck Mutex... yes for sys/asoundlib.h... no ./config.status Makefile config/config.h
<--Last Chapter
Table of Contents
Next Chapter-->
6 Development Utilities
<--Last Chapter Table of Contents Next Chapter-->
6.1 Saving Time with Gnatstub Starting with gnat 3.11p, gnat provides a prototyping tool called Gnatstub.Gnatstub takes an Ada package specification and creates a corresponding body, ready to have the details outlined in the spec filled in. These empty subprograms are sometimes called "stubs". This is especially useful on a large project where programmers write a series of package specs to test their design. Once the package design is set, Gnatstub can create a basic body and save the programmers the work of copying and modifying the specification by hand. IDE: TIA will run gnatstub on the current file using Stub in the File menu. For example, suppose you have the following package specification in a file called tiny.ads: package tiny is procedure simple_procedure; function simple_function return boolean; end tiny; You can create a stub body for this package using gnatstub tiny.ads Gnatstub produces the following tiny.adb file: package body tiny is ---------------------- simple_function ---------------------function simple_function return boolean is begin return simple_function; end simple_function; ----------------------
-- simple_procedure ----------------------procedure simple_procedure is begin null; end simple_procedure; end tiny; This package body is in proper Ada format, ready to be compiled. Of course, it doesn't actually do anything useful. It's up to the programmer fill in the implementation details.
4. gnatelim main > gnat.adc 5. gnatmake -f main These commands will generate a complete set of tree files for your project, strip out all unused subprograms, and will then recompile the project as a finished executable. gnatelim is is based on ASIS. [gnatelim doesn't work under gnat 3.11.--KB]
Suppose you create a file called "prepvalues" with the following Gnatprep definitions: ALPHAVERSION := true BETAVERSION := false RELEASEVERSION := false TRANSLATION := English Suppose also that you had a short program with Gnatprep statements in it: with text_io; use text_io; procedure preptest is -- only include the relevant parts for this version #if ALPHAVERSION s_version : string := "alpha"; #elsif BETAVERSION s_version : string := "beta"; #elsif RELEASEVERSION s_version : string := "release"; #else s_version : string := "unknown"; #end if; -- string is the value of the gnatprep variable named translation s_translation : string := "$TRANSLATION"; begin Put_Line( "This is the " & s_version & " edition" ); Put_Line( "This is the " & s_translation & " translation" ); end preptest; Running gnatprep on the above program with the prepvalues file gives you the following program: with text_io; use text_io; procedure preptest is -- only include the relevant parts for this version s_version : string := "beta"; -- string is the value of the gnatprep variable named translation s_translation : string := "English";
begin Put_Line( "This is the " & s_version & " edition" ); Put_Line( "This is the " & s_translation & " translation" ); end preptest; The Gnatprep command switches are: -Dsymbol=value - define values on the command line instead of a prep values file, same as -D in C. For example, -DMacintosh=FALSE -b - replace gnatprep commands with blank lines (instead of -c) -c - comment out gnatprep commands (instead of -b) -r - generate a Source_Reference pragma -s - print a sorted list of symbols and values -u - treat undefined symbols as if they were FALSE C: There is no gnatprep equivalent of __FILE__ (name of current source file) or __LINE__ (number of current line).
return param * factorial( param - 1 ); end factorial; end fact; with fact; use fact; procedure bench2 is maxFactorials : constant integer := 1000; type factorialArray is array( 1..maxFactorials ) of integer; list : factorialArray; begin for i in 1..maxFactorials loop list( i ) := factorial( i ); end loop; end bench2; After compiling and linking the program with the -pg switch, run the program. The program produces a gmon.out file containing profile information about the program. Now we can use Gprof to get an analysis of the program. Running gprof -c bench2 returns the following information. Note that Ada subprograms are labeled with the package name, a double underscore, and the subprogram name. [Need to clean this up--KB] Flat profile: Each sample counts as 0.01 seconds. %cumulative self self total
62.500.050.05__mcount_internal 25.000.070.02mcount
0.000.080.0010.000.00fact___elabb
%the percentage of the total running time of the time program used by this function. cumulative a running sum of the number of seconds accounted seconds for by this function and those listed above it. self the number of seconds accounted for by this seconds function alone.This is the major sort for this listing. calls the number of times this function was invoked, if this function is profiled, else blank. self the average number of milliseconds spent in this ms/call function per call, if this function is profiled, else blank. total the average number of milliseconds spent in this ms/callfunction and its descendents per call, if this function is profiled, else blank. name the name of the function. This is the minor sort for this listing. The index shows the location of the function in the gprof listing. If the index is in parenthesis it shows where it would appear in the gprof listing if it were to be printed. Call graph (explanation follows) granularity: each sample hit covers 4 byte(s) for 100.00% of 0.01 seconds index % time self children called name 499500fact__factorial [1] 0.010.001000/1000_ada_bench2 [3]
----------------------------------------------0.000.000/0_start [473] [2]100.00.000.01main [2] 0.000.011/1_ada_bench2 [3] 0.000.000/0__gnat_initialize [399] 0.000.000/0adainit [61] 0.000.000/0__gnat_break_start [395] 0.000.000/0adafinal [60] 0.000.000/0__gnat_finalize [397] 0.000.000/0exit [95]
-----------------------------------------------
-----------------------------------------------
This table describes the call tree of the program, and was sorted by the total amount of time spent in each function and its children. Each entry in this table consists of several lines.The line with the index number at the left hand margin lists the current function. The lines above it list the functions that called this function, and the lines below it list the functions this one called. This line lists: index A unique number given to each element of the table. Index numbers are sorted numerically. The index number is printed next to every function name so it is easier to look up where the function in the table. % time This is the percentage of the `total' time that was spent in this function and its children.Note that due to different viewpoints, functions excluded by options, etc, these numbers will NOT add up to 100%. self This is the total amount of time spent in this function. children This is the total amount of time propagated into this function by its children. called This is the number of times the function was called. If the function called itself recursively, the number only includes non-recursive calls, and is followed by a `+' and the number of recursive calls. name The name of the current function. The index number is printed after it. If the function is a member of a cycle, the cycle number is printed between the
function's name and the index number. For the function's parents, the fields have the following meanings: self This is the amount of time that was propagated directly from the function into this parent. children This is the amount of time that was propagated from the function's children into this parent. called This is the number of times this parent called the function `/' the total number of times the function was called. Recursive calls to the function are not included in the number after the `/'. name This is the name of the parent.The parent's index number is printed after it.If the parent is a member of a cycle, the cycle number is printed between the name and the index number. If the parents of the function cannot be determined, the word `<spontaneous>' is printed in the `name' field, and all the other fields are blank. For the function's children, the fields have the following meanings: self This is the amount of time that was propagated directly from the child into the function. children This is the amount of time that was propagated from the child's children to the function. called This is the number of times the function called this child `/' the total number of times the child was called. Recursive calls by the child are not listed in the number after the `/'. name This is the name of the child.The child's index number is printed after it.If the child is a member of a cycle, the cycle number is printed between the name and the index number. If there are any cycles (circles) in the call graph, there is an entry for the cycle-as-a-whole. This entry shows who called the cycle (as parents) and the members of the cycle (as children.) The `+' recursive calls entry shows the number of function calls that
were internal to the cycle, and the calls entry for each member shows, for that member, how many times it was called from other members of the cycle. Index by function name (418) __mcount_internal[1] fact__factorial[5] <cycle 2> [3] _ada_bench2(177) mcount [6] fact___elabb[4] <cycle 1> If factorial is a function internal to program bench2, the function name won't show up in Gprof. For example: procedure bench2 is function factorial( param : integer ) return integer is begin if param > 1 then return param * factorial( param - 1 ); end if; return 1; end factorial; maxFactorials : constant integer := 100; type factorialArray is array( 1..maxFactorials ) of integer; list : factorialArray; begin for i in 1..maxFactorials loop list( i ) := factorial( i ); end loop; end bench2; gprof -c bench2 returns the following information: Flat profile: Each sample counts as 0.01 seconds.
0.000.000.0010.000.00_ada_bench2 etc.
procedure htmltest is -- a demonstration of gnathtml function factorial( n : natural ) return natural is -- compute the factorial of n begin if n < 2 then return 1; else return n * factorial( n-1 ); end if; end factorial; -- main program begin put_line( "Browse this source!" ); new_line; put_line( "The factorial of 5 is" & natural'image( factorial( 5 ) ) ); end htmltest;
Files
q
htmltest.adb
Functions/Procedures
q q
factorial htmltest
File : htmltest.adb
with Ada.Text_IO; use Ada.Text_IO; procedure htmltest is -- a demonstration of gnathtml
function factorial( n : natural ) return natural is -- compute the factorial of n begin if n < 2 then return 1; else return n * factorial( n-1 ); end if; end factorial; -- main program begin put_line( "Browse this source!" ); new_line; put_line( "The factorial of 5 is" & natural'image( factorial( 5 ) ) ); end htmltest; The links use the .htm (not .html) extension for portability.
6.9 GnatFind
[To be written--KB] <--Last Chapter Table of Contents Next Chapter-->
Optimization is the customization of a program to run as small and/or as fast as possible on a particular type of computer. If you program is running slower than you expected, or is using more memory or disk space than you expected, you should first examine the approach you used in your Ada source code. Can you use better data structures, or implement faster algorithms? For example, a bubble sort is an easy way to sort relatively small amounts of data, but a quick sort is faster on thousands or millions of pieces of data. In large programs, the subprogram causing the biggest bottlenecks may not be obvious. Experimenting with different test data and timing the results can often narrow down the problem areas. You could also try the gprof profiling tool, which will give you statistics on your program performance and will show that you are on the right track. Why spend hours or days improving a section of your program that isn't causing the problem? This is especially important in a business environment: focus your time on the sections that will give the greatest improvements. Some optimizations can be done automatically by the Gnat compiler. There are both compiler switches and language pragmas for fine tuning your programs.
specification. These switches both require a -O switch for inlining to take effect. The -gnatp switch turns off all non-essential error checking such as constraint and range checks. This is the same as using pragma Suppress( All_Checks ) on every file in the entire program, making the program smaller and faster. There are some other gcc optimization switches which can sometimes be used: -ffast-math - gcc will ignore certain ANSI & IEEE math rules. For example, it will not check for negative numbers before invoking the sqrt function. This improves math performance but can cause sideeffects for libraries expecting the ANSI/IEEE rules to be honoured. -fomit-frame-pointer - gcc will free the register usually dedicated to hold the stack frame pointer. This improves performance, but makes debugging difficult--many debugging utilities require the frame pointer. IDE: TIA sets the proper switches for you based on your selections in the project parameters window.
There are six pragmas available to change the size and execution speed of your program. Pragma Pack compresses an array, record or tagged record so that it uses the minimum space possible. For example, a packed boolean array takes up one bit for each boolean. Pack only packs the aggregate, not any aggregate items that might make up the aggregate: if you have an array of records, you'll need to both pack the array and the records to use the minimum space possible. Packing aggregates usually slows down the execution of your program. type CustomerProfile is record Preferred : boolean; PreordersAllowed : boolean; SalesToDate : float; end record; pragma Pack( CustomerProfile );
Gnat can perform close packing, that is, packing right down to individual bits, for array elements or records of 64 bits or smaller. Pragma Optimize specifies how you want your statements to be optimized: to run as fast as possible (time), to be as small as possible (space), or no optimization at all. Optimize does not affect data structures. pragma Optimize ( space ); package body AccountsPayable is Pragma Inline makes Ada inline the subprogram whenever possible. That is, it physically inserts the subprogram whenever it's named instead of calling it in order the make your program run faster. This uses up a lot of space and is only practical for small procedures and functions. procedure Increment( x : integer ) is begin x := x + 1; end Increment; pragma Inline( Increment ); Compiling switch -O3 must be used or pragma inline is ignored. -O3 will also automatically inline short subprograms for you. Pragma Inline_Always forces inlining between packages (like -gnatn) regardless of whether or not -gnatn or -gnatN has been used. Pragma Discard_Names frees up space by discarding the ASCII images (names) of identifiers. For example, if you have a big enumerated type, Ada normally maintains strings for the names of each of the enumerated items in case you want to use the 'img attribute. You can discard these names if you never intend to use 'img. type aDogBreed is (Unknown, Boxer, Shepherd, MixedBreed ); pragma Discard_Names( aDogBreed ); When you discard names, the 'img is still available. Instead of returning the enumerated value's image, 'img returns the position of the enumerated type (for example, 0, 1, 2 and so forth). Fun Fact: The ASCII images of your variable names are stored as C strings at the end of your executable file. You can view them using the less (or strings) shell command.
q q q
Future versions of Gnat built for GCC 3.x or later will probably support: -mpentium - optimize for Pentium / Intel 586 -mcpu=i686 - optimize for Pentium II/ Intel 686 -mcpu=k6 - optimize for AMD K6
There are currently no switches newer CPUs such as Pentiums. Under GCC 2.8.1 (and Gnat), the GCC FAQ recommends the following switches for reasonable Pentium performance: "-m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -fno-strength-reduce". There are other switches that may or may not be helpful, depending on your program: read the gcc FAQ for full details. IDE: TIA sets the proper switches for you based on your selections in the project parameters window. Let's put all these flags together. Suppose you are trying to develop a program for the Intel Pentium CPU with an emphasis on speed. During development, the Gnatmake switches would be "-O1" since this setting suppresses pragma optimize warnings. For the final release, the Gnatmake switches should be "-m486 -O3 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -fno-strength-reduce -gnatp" for maximum performance on a Pentium processor.
-O2 -O3 -O3 -gnatp -O3 -gnatp Pent Max Max Max Max
Pent - GCC Pentium optimization switches Max - Pent + -ffast-math + -fomit-frame-pointer This test was conducted with a Pentium II 350, 64 Megs RAM and ALT Gnat 3.12p-9. As they say, your milage many vary (and probably will). By optimizing the application, Hartstone can be reduced to half its size and run about 2/3 faster than using no optimization. However, if we pack the arrays in Hartstone, we save two bytes but lose all the improvements in speed. Sometimes smaller programs are not faster. Let's try optimizing a convoluted program that uses integers, arrays, functions and mathematics and see what effect the optimization techniques have. procedure bench is --Simple benchmark program to test optimization pragma optimize( time ); type bench_integer is new long_integer range long_integer'range; type small_integer is new long_integer range 0..9; function p( param : bench_integer ) return bench_integer is divideby : constant bench_integer := 4; begin return param / divideby; end p; pragma inline( p ); j : bench_integer := bench_integer'last; -- deliberate error in main program for j * 2 type atype is array(0..9) of small_integer; --pragma pack( atype);
a : atype; begin for i in 1..100_000_000 loop j := abs( p( bench_integer( i ) ) - (j * 2) ); a( integer( j mod 10 ) ) := small_integer( j mod bench_integer( small_integer'last) ); end loop; end bench; Notice that j is assigned the largest bench_integer possible. This will force an overflow error the first time around the for loop, when j is multiplied by two. The following chart shows the effect of the different switches and pragmas, and indicates when gnat caught the overflow error. The test was conducted on a Pentium II 350 with 64 Megs of RAM using the gnat 3.11 NYU binaries and was timed with the time command. Gnatmake Switches gnatmake -gnato -gnatE gnatmake -gnato gnatmake -gnatE gnatmake gnatmake -O gnatmake -O2 gnatmake -O3 gnatmake -O3 -gnatp gnatmake -O3 -gnatp Pent gnatmake -O3 -gnatp Pent gnatmake -O3 -gnatp Pent gnatmake -O3 -gnatp Pent Pragmas Optimize( Space ) Optimize( Time ) Pack atype CPU Time 40.3 s 40.3 s 10.8 s 10.8 s 10.8 s 9.6 s 9.6 s 9.6 s 9.6 s 4.4 s Size 118162 118162 118162 117634 117426 117426 117426 117410 117410 117410 117410 117326 Error Caught? YES YES No No No No No No No No No No
Although the proper optimization can make this program run faster, but with overflow checking was turned on with -gnato, the overflow error is caught. The lesson here is that error checking only works when it's turned on. We can compare the results to the equivalent C program: int p( int param ) { return param / 4;
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/7.html (6 of 10) [7/20/2001 11:32:11 AM]
} int i; int j = 2147483647; int a[10]; int main() { for (i=1; i<=100000000; i++) { j = abs( p(i)-(j*2)); a[ j%10 ] = j%10; } return 0; } GCC Switches gcc -Wall gcc -O3 Pent Pragmas CPU Time 12.8 s 8.6 s Size 24541 24541 Error Caught? No No
In this case, notice that C never detected the overflow error. Secondly, notice that the Ada program ran twice as fast as the C program. In theory, an Ada compiler can take advantage of the typing information and the optimization hints provided by the pragmas. The C compiler has less information and this can hinders the optimization process. (I've never investigated whether or not Gnat does this or how much of an effect it has.) The optimization techniques will affect different programs differently. You need to chose the best approach for your particular project.
The following is the stderr.s file for the stderr.adb program described elsewhere in this document. .file"stderr.adb" .version"01.01" / GNU Ada version 2.8.1 (i686-pc-linux-gnu) compiled by GNU C version 2.8.1. / options passed:-I../texttools/ -mcpu=i486 -march=i486 -gnatp -gnatf -O3 / -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 / -fno-strength-reduce -fverbose-asm / options enabled:-fdefer-pop -fcse-follow-jumps -fcse-skip-blocks / -fexpensive-optimizations -fthread-jumps -fpeephole -fforce-mem / -ffunction-cse -finline-functions -finline -fkeep-static-consts / -fcaller-saves -fpcc-struct-return -frerun-cse-after-loop / -fschedule-insns2 -fcommon -fverbose-asm -fgnu-linker -m80387 / -mhard-float -mno-soft-float -mieee-fp -mfp-ret-in-387 / -mschedule-prologue -mcpu=i486 -march=i486 -malign-loops=2 / -malign-jumps=2 -malign-functions=2 gcc2_compiled.: .section.rodata .LC0: .string"This is an example of writing error messages to stderr" .align 4 .LC1: .long 1 .long 54 .LC2: .string"This message is on standard error" .align 4 .LC3: .long 1 .long 33 .LC4: .string"This message is on standard output" .align 4 .LC5: .long 1 .long 34
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/7.html (8 of 10) [7/20/2001 11:32:11 AM]
.LC6: .string"This is also on standard error" .align 4 .LC7: .long 1 .long 30 .LC8: .string"But this is on standard output" .text .align 4 .globl _ada_stderr .type_ada_stderr,@function _ada_stderr: pushl %ebp movl %esp,%ebp movl $.LC0,%eax movl $.LC1,%edx pushl %edx pushl %eax call ada__text_io__put_line__2 pushl $1 call ada__text_io__new_line__2 movl $.LC2,%eax movl $.LC3,%edx pushl %edx pushl %eax call ada__text_io__standard_error pushl %eax call ada__text_io__put_line movl $.LC4,%eax movl $.LC5,%edx pushl %edx pushl %eax call ada__text_io__put_line__2 addl $32,%esp pushl $1 call ada__text_io__new_line__2
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/7.html (9 of 10) [7/20/2001 11:32:11 AM]
call ada__text_io__standard_error pushl %eax call ada__text_io__set_output movl $.LC6,%eax movl $.LC7,%edx pushl %edx pushl %eax call ada__text_io__put_line__2 call ada__text_io__standard_output pushl %eax call ada__text_io__set_output movl $.LC8,%eax movl $.LC7,%edx pushl %edx pushl %eax call ada__text_io__put_line__2 movl %ebp,%esp popl %ebp ret .Lfe1: .size_ada_stderr,.Lfe1-_ada_stderr .ident"GCC: (GNU) 2.8.1"
<--Last Chapter
Table of Contents
Next Chapter-->
assert Debugging procedure call Disable pragma debug pragma Suppress_Debug_Info; #ifdef var...#endif Indicate a subprogram that pragma No_Return( subprogram ); never completes Initialize scalars to illegal pragma Normalize_Scalars; values GNAT provides two useful pragma for debugging programs. To use these pragmas, you need to use the -gnata option during compiling. Removing -gnata causes GNAT to ignore these pragmas, making it easy to compile a version of your program for public release without having to delete the debugging statements from your source code.Pragma Assert lets you test to make sure a certain condition is true. If it isn't, then an exception is raised. Use Assert to check for conditions which you assume are true during program development. This is especially useful when several programmers are working on a project and you don't know if a condition will change in the future. pragma Assert( ScreenHeight = 24 );
In this example, if the variable ScreenSize is not 24, an ASSERT_ERROR exception is raised. Pragma Debug lets you call a procedure for debugging purposes. For example, you can use this to print information about the program while it is running. Because this is a pragma, you can place it almost anywhere, even in the middle of variable declarations. x := 5; pragma Debug( PrintToLogFile( "X is now" & x'img ) ); If PrintToLogFile is a procedure that saves messages to a log file, this example saves the message "X is now 5" to the log file. Pragma debug can be disabled with pragma Suppress_Debug_Info. Pragma No_Return can be used to indicate subprograms that never complete. This suppresses the related compiler warnings. Pragma Normalize_Scalars initializes anything not an array, record or tagged record to illegal values wherever possible. This pragma helps expose variables used before they are initialized. Use this at the start of a program or package. Suppose you have a integer variable with a range between 1 and 100. Normally, Ada won't assign an initial value (unless you specify one). With Normalize_Scalars, your variable will be initialized to some value out of range, perhaps -1. If you attempt to use this variable, you'll probably raise a CONSTRAINT_ERROR exception.
package IntPtrs is new System.Address_To_Access_Conversions( integer ); -- Instantiate a package to convert access types to/from addresses. -- This creates an integer access type called Object_Pointer. five : aliased integer := 5; -- Five is aliased because we will be using access types on it int_pointer : IntPtrs.Object_Pointer; -- This is an Ada access all type int_address : System.Address; -- This is an address in memory, a C pointer begin int_pointer := five'unchecked_access; -- Unchecked_access needed because five is local to main program. -- If it was global, we could use 'access. int_address := five'address; -- Addresses can be found with the 'address attribute. -- This is the equivalent of a C pointer. int_pointer := IntPtrs.To_Pointer( int_address ); int_address := IntPtrs.To_Address( int_pointer ); -- Convert between Ada and C pointer types. end pointers; The -gnatG shows the compiler's analysis of your program. In this case, it displays the results of the instantiation of the generic package: with system; with ada; with ada.text_io; with system.address_to_access_conversions; use ada.text_io; with system; with system; with unchecked_conversion; procedure pointers is package intptrs is subtype object is integer;
package address_to_access_conversions renames intptrs; null; type object_pointer is access all object; for object_pointer'size use 32; function to_pointer (value : address) return object_pointer; function to_address (value : object_pointer) return address; pragma convention (intrinsic, to_pointer); pragma convention (intrinsic, to_address); freeze object_pointer [] freeze to_pointer [] freeze to_address [] end intptrs; package body intptrs is function to_address (value : object_pointer) return address is begin if value = null then return null_address; else return value.all'address; end if; end to_address; function to_pointer (value : address) return object_pointer is package a_to_pGP3183 is subtype source is address; subtype target is object_pointer; function a_to_pR (s : source) return target; end a_to_pGP3183; function a_to_p is new unchecked_conversion (address, object_pointer); begin return target!(source(value)); end to_pointer; end intptrs; package intptrs is new system.address_to_access_conversions (integer); five : aliased integer := 5; int_pointer : intptrs.object_pointer := null; int_address : system.address; freeze intptrs [] begin int_pointer := five'unchecked_access; int_address := five'address; int_pointer := intptrs.to_pointer (int_address);
q q q
q q q
Ada_83 - do not allow Ada 95 language features Ada_95 - (default) allow Ada 95 language features Controlled - turn off garbage collection for a type. This has no effect with Gnat since it does not implement garbage collection. Ravanscar - enforce Ravanscar run-time restrictions Restricted_Run_Time - similar to Ravanscar Restrictions - disable particular language features
no_run_time also enforces restrictions because the Ada run-time library is not available. More information on the usage of these pragmas is available in the Gnat documentation.
<--Last Chapter
Table of Contents
Next Chapter-->
9 Team Development
<--Last Chapter Table of Contents Next Chapter-->
diff -r1.1 f.c 0a1 > --test line Finally, you can check the file back in. RCS increments the version number and prompts you for a message for the change log. armitage:/home/ken/ada/rcs# ci f.c RCS/f.c,v <-- f.c new revision: 1.2; previous revision: 1.1 enter log message, terminated with single '.' or end of file: >> Added comments >> . done
CVS: CVS: ---------------------------------------------------------------------(make notes and exit vi) N project/currency.adb No conflicts created by this import The "N project/currency.adb" line indicates that CVS created a new project called "project" and added the Ada file currency.adb to it. currency.adb is now stored in the CVS repository, ready to be shared amongst the team members. To work with a project, you use co (or checkout). This CVS command will save a copy of the project in your directory. It will also create a CVS directory to save private data files used by CVS. To use co, move to your home directory and type: [root@redbase cvs]# cvs checkout currency.adb cvs checkout: Updating . U project/currency.adb The subdirectory project will contain your own, personal copies of project files to work on. CVS maintains the original copy of currency.adb. Another programmer can also checkout currency.adb while you are working on your copy. If you do a checkout right after an import, you may have to remove the original files: CVS will not overwrite any existing files. To add a file to CVS, use the add command. currency.adb, use [root@redbase cvs]# cvs add currency.adb To add a file called
Single files, directories or even CVS modules can also be added to your project using "add". As you work on your source code, you can check your work against the project using the update command. [root@redbase cvs]# cvs update cvs update: Updating . When updating, CVS checks the files in your copy of the project against its copies. If another team member made changes to one of the project Ada files, CVS will copy the new file to your directory. If another team member made changes to one of the Ada files you've been working on, CVS will attempt to update your copy without destroying your work.
Sometimes the changes involve the same part of the Ada file and CVS won't be able to combine the changes automatically. CVS calls this a conflict. For example, suppose your Ada file contained a function function ConvertCurrency( amount : integer ) return float; If you changed this function to use a float amount, and another team member has changed amount to a string, CVS will report a conflict. You will have to talk to the team member who made the change and make an agreement what amount should be. If there are no other problems after an update, you can continue working on your source code. To delete a file, remove it using "rm" and then "update". file is no longer in the project. CVS will see your
The CVS command release will permanently remove a file from a project (including the copy in CVSROOT), but it also prevents you from recovering the file from the CVSROOT directory in an emergency. Unless storage space is limited, consider using the rm/update method of removing files. While working on your source code, your changes are not distributed to the rest of your team until you are ready. When your source code is tested and ready to be made available, use ci (or commit). Before commiting your changes, delete non-essential files (such as .ali, .o or executable files) to save space in the repository. The log command gives information about a group of files: [root@redbase cvs]# cvs log -l project cvs log: Logging project RCS file: /usr/cvs/project/currency.adb,v Working file: project/currency.adb head: 1.1 branch: 1.1.1 locks: strict access list: symbolic names: p1: 1.1.1.1 keyword substitution: kv total revisions: 2; selected revisions: 2 description: ---------------------------revision 1.1 date: 1999/01/13 17:27:33; author: kburtch; state: Exp; branches: 1.1.1;
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/9.html (5 of 6) [7/20/2001 11:32:37 AM]
Initial revision ---------------------------revision 1.1.1.1 date: 1999/01/13 17:27:33; author: kburtch; state: Exp; lines: +0 -0 Project started ============================================================================= Status gives you an overview of a group of files: [root@redbase cvs]# cvs status project cvs status: Examining project =================================================================== File: currency.adb Status: Up-to-date Working revision: 1.1.1.1 Wed Jan 13 17:27:33 1999 Repository revision: 1.1.1.1 /usr/cvs/project/currency.adb,v Sticky Tag: (none) Sticky Date: (none) Sticky Options: (none)
<--Last Chapter
Table of Contents
Next Chapter-->
10 An Introduction to Ada
<--Last Chapter Table of Contents Next Chapter-->
Ada is a full-featured language with many capabilities, rules, and nuances. Although the fundamentals are easy to learn (Ada somewhat resembles BASIC), it is several times larger that C, and to truly master the language requires considerable practice. To make understanding easier, the discussion is broken up into two chapters. This chapter outlines the basics of the language, and the next chapter discusses features for team development, large projects, and other specialized tasks. This in no way covers everything there is to know about Ada. I've chosen to cover those features that have been the most use to me over the years in my projects. For example, array slicing alone could take up several pages of discussion, but I've never had a need for it in recent years. Of course, you may be involved in a project in which array slicing is crutial. In these cases, I recommend you get a good Ada 95 reference such as Barnes' Programming in Ada 95. Likewise this is not a complete introduction to computer programming. Some background knowledge is assumed. Ada also has many specialized features for specific tasks such as scientific computing and real-time systems. Where I deliberately skip a subject, I usually make a note that I have done so. I've also hilighted useful information for C programmers who are learning Ada. Now, on to main programs.
executing instructions. On the other hand, "firstProgram" is an identifier because it is the name of our program. All keywords in Ada are also reserved words: this means that the keywords cannot be used as identifiers. The main program can have any name, as long as the name matches the filename. In gnat, the source code for a main program ends in .adb (Ada body). This program should be saved as firstprogram.adb. C: The main program doesn't have to be "main" unless you save the program as "main.adb".
If you call a program "test.adb", remember that test is a built-in shell command. To run a program named test, you'll have to type "./test" instead of "test" to avoid running the shell command by mistake.
Comments are denoted by two minus signs (--). This is a note to the reader; Ada will ignore it. Everything you type to the right of the symbol is treated as a remark to the reader. C: Ada has no equivalent to the block comment /* and */.
10.2 Text_IO
Ada put( s ); put( n'img ); put_line( s ); new_line; Description Display a string Display a number C Equivalent printf( "%s", s ); printf( "%d", n );
get( c ); get_line( s, len ); Read a line of text from the keyboard etc.
Display a line of text and start a new line printf( "%s\n", s ); Start a new line printf( "\n" ); Read a character from the keyboard c = getc(); gets(&s);
Like many modern computer languages, Ada doesn't have any built-in methods of reading the keyboard or writing messages on the screen. It doesn't assume you're writing a program for a PC (you could be doing embedded programming, for example)--but in general, you need to interpret what people type and display the results to the screen. You have to add this functionality specifically. The standard input/output package for Ada is Text_IO. This package prints characters and strings to the screen and reads characters and strings from the keyboard. It can also read and write simple sequential text files. (Packages will be discussed in detail starting at 11.1 in the next chapter.) Text_IO is only useful for simple programs. It doesn't have the ability to draw buttons, windows or menus. For X Windows programming, you'll require other packages/libraries to perform input and output.
C: In C, printf and company can use an arbitrary number of parameters, where the parameters can be of different types. Text_IO's puts have one parameter, and the parameter must be a string or a character. Upcoming sections demonstrate how to print other types. The most commonly used operations are: q Put - write to the screen, but don't start a new line q Put_Line - write to the screen and start a new line q New_Line - start a new line q Get - read a character from the keyboard q Get_Line - read a string from the keyboard The following program is an example of Text_IO. with Ada.Text_IO; use Ada.Text_IO; procedure basicio is -- this program demonstrates basic input/output 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 basicio;
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. c The character you typed was 'c' Besides letters and numbers, there are special characters called control characters which, instead of displaying a character, change the Linux display. To print controls characters, you need to use one of Ada's built-in character sets. For example, ASCII is a predefined list of all the ASCII characters. To send an explicit form feed character, use Put( ASCII.FF ); Some common control characters are: q ASCII.NUL - C end of string character q ASCII.CR - carriage return - move to beginning of line q ASCII.HT - horizontal tab q ASCII.LF - line feed - start a new line q ASCII.FF - form feed - start a new page on a printer C: Put doesn't recognize C string escape codes like "\n" or "\r". Besides ASCII, Ada has a number of other character sets defined in the Ada.Characters packages.
The ASCII set is officially made obsolete in Ada 95 by Ada.Characters.Latin_1, but it's still often used because it's easier to type.
A big integer (same as long in Gcc) long (same as int in Gcc) A really big (64-bit) integer Long_Long_Integer long long A small (16-bit) integer Short_Integer short Short_Short_Integer A really small (8-bit) integer char A real number Float float
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/10.html (4 of 29) [7/20/2001 11:33:05 AM]
A big real number A really big real number A smaller real number A fixed-point real number An Ada fixed-length string
C: There are no built-in equivalents of unsigned types. Natural and Positive are integer values that aren't allowed to be negative, effectively requiring the sign bit to be zero. Characters cannot be used for small integer values--characters variables can only represent character values. Generally speaking, programs take data, process it in different ways, and create new information. Data is categorized into different data types. Data that is typed into a program is known as literals. Ada has several kinds of literals: q 'c' is a character. Character literals are enclosed in single quotes. q -5 is an integer q 45.5 is a float or a fixed with one decimal place q "This is a string" is a fixed string. Strings literals are enclosed in double quotes. C: Ada doesn't have long numerical literals, like "45L". Numeric literals are a special type called universal integer and adapt to fit the requirements of an expression. C: Ada strings do not end with an ASCII 0 character: they end with the upper bound of the array that encloses them. To change an Ada string into a C string, concatenate a null character like this: "This is my string" & ASCII.NUL; There are three kinds of real numbers. A fixed, or fixed point, number is a number that has a fixed number of decimal points. For example, U.S. dollars are often represented using fixed numbers because there are two decimal places. A float, or floating-point, number is a number that doesn't have a fixed number of decimal places. Decimal numbers are a variation of fixed numbers commonly used for currency. Some Ada programmers recommend that floats are used whenever possible because float calculations are usually faster than fixed calculations. This is because most computers today have floating point support in their hardware. Floating point numbers are very important for business and scientific applications. When floating point numbers are converted to integers, do the numbers round to the nearest integer or is the decimal part simply discarded? In C, this is system dependent: System V-based UNIX's usually round to the nearest integer, while some other systems discard the decimal part. (Others, like HP-UX, the number rounds towards the nearest even integer providing the floating point number is exactly half way between two integers.) On Linux, C truncates the decimal part. In Ada, the way numbers round are strictly defined by the language: you can be sure that, no matter what operating system you are using, floating point numbers converted to integers will always round to the nearest integer. If the floating point number is half way between two integers, it will round "up". The following program demonstrates floating point rounding:
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/10.html (5 of 29) [7/20/2001 11:33:05 AM]
with ada.text_io, ada.float_text_io; use ada.text_io, ada.float_text_io; procedure rounding is -- rounding example procedure ShowRounding( f : float ) is -- show the floating point value, and show the value -- after it's converted to an integer int_value : integer; begin Put( " Float number " ); Put( f, fore => 5, aft => 3 ); int_value := integer( f ); Put_Line( " rounds to " & int_value'img ); end ShowRounding; begin Put_Line( "This is a demonstration of how Ada 95 rounds" ); New_Line; ShowRounding( ShowRounding( ShowRounding( ShowRounding( ShowRounding( end rounding; 253.0 ); 253.2 ); 253.5 ); 253.8 ); -253.8 );
This is a demonstration of how Ada 95 rounds Float Float Float Float Float number number number number number 2.530E+02 2.532E+02 2.535E+02 2.538E+02 -2.538E+02 rounds rounds rounds rounds rounds to 253 to 253 to 254 to 254 to -254
You can compare the results with the following C program: #include <stdio.h> static void show_rounding( float f ) { int i; i = f; printf( " Float number %g", f ); printf( " rounds to %d\n", i );
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/10.html (6 of 29) [7/20/2001 11:33:05 AM]
} /* show rounding */ int main () { show_rounding( show_rounding( show_rounding( show_rounding( return 0; } 253.0 253.2 253.5 253.8 ); ); ); );
253 rounds to 253 253.2 rounds to 253 253.5 rounds to 253 253.8 rounds to 253
Rounding to integers is a common way in C business applications to round money to the nearest dollar or cent. This is accomplished by multiplying the floating point value by 100.0, adding .5, and then taking the integer value and converting it once more into a floating point value. In Ada, there's a built-in type attribute to round floating point numbers: this makes conversion to an integer unnecessary. C: The fundamental integer types don't "wrap around" the way C data types do. Values that grow too large produce overflow errors. However, gnat turns off integer overflow exceptions by default to improve performance. Ada provides properly behaved C types and conversion functions in the Interfaces.C package. Interfaces.C includes the following types: type int is new Integer; type short is new Short_Integer; type long is range -(2 ** lbits1) .. +(2 ** lbits1) - 1; type signed_char is range SCHAR_MIN .. SCHAR_MAX; for signed_char'Size use CHAR_BIT; type unsigned is mod 2 ** int'Size; type unsigned_short is mod 2 ** short'Size; type unsigned_long is mod 2 ** long'Size; type unsigned_char is mod (UCHAR_MAX + 1); for unsigned_char'Size use CHAR_BIT; GNAT has a second package, Interfaces.C.Extensions, that includes additional types, such as unsigned_long_long. As it's name suggests, the Text_IO package only performs I/O with text, not numbers or other types of information. If you want to print, say, and integer value using Text_IO, you must first convert the integer to a string using the 'img attribute (or 'image). (Attributes are discussed in the next section.) with Ada.Text_IO; use Ada.Text_IO; procedure basicio2 is
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/10.html (7 of 29) [7/20/2001 11:33:05 AM]
-- this program demonstrates more advanced input/output i : integer := 5; -- this variable contains an integer number -- i initially has the value of 5 s : string(1..20); -- this variable contains a 20 character string len : natural; begin Put_Line( "This program displays information on the screen" ); Put_Line( "and reads information from the keyboard"); New_Line; Put_Line( "'img returns the string representation of a variable's" ); Put_Line( "value. The value i is" & i'img); New_Line; s := "...................."; -- set s to 20 periods Put_Line( Put_Line( Put_Line( Get_Line( "The variable s is " & s); "Get_Line reads a string from the keyboard" ); "Type in a message up to 20 characters and press Enter:" ); s, len );
Put_Line( "After Get_Line copies your message to s, s is now '" & s & "'" ); Put_Line( "The message is" & len'img & " characters long." ); Put_Line( "The characters after your message remain unchanged." ); end basicio2;
This program displays information on the screen and reads information from the keyboard 'img returns the string representation of a variable's value. The value i is 5 The variable s is .................... Get_Line reads a string from the keyboard Type in a message up to 20 characters and press Enter: jingle bells After Get_Line copies your message to s, s is now 'jingle bells........' The message is 12 characters long. The characters after your message remain unchanged.
with Ada.Text_IO; use Ada.Text_IO; procedure basicio3 is -- this program demonstrates more even advanced input/output i : integer := 5; -- this variable contains an integer number -- i initially has the value of 5 s : string(1..5); -- this variable contains a 5 character string len : natural; begin Put_Line( "This program displays information on the screen" ); Put_Line( "and reads information from the keyboard"); New_Line; Put_Line( "The value i is" & i'img); New_Line; Put_Line( Put_Line( Put_Line( Get_Line( "integer'value changes a string into an integer value" ); "Type in a 4 character integer characters with a leading" ); "space or negative sign and press Enter:"); s, len ); -- length of string
i := integer'value( s ); Put_Line( "The value of i is " & i'img); New_Line; end basicio3;
This program displays information on the screen and reads information from the keyboard The value i is 5 integer'value changes a string into an integer value Type in a 4 character integer characters with a leading space or negative sign and press Enter: 2345 The value of i is 2345
Besides Text_IO, Ada provides additional "Text_IO" packages for the basic Ada data types. Using these packages, you don't need to use 'img to convert the variable to a string. For example, the package Ada.Integer_Text_IO can put and get integers, and Ada.Float_Text_IO can put and get floating point numbers. You can use these packages simultaneously with Text_IO. These additional packages do not have Put_Line or Get_Line because these are specifically for strings. The Put command has two additional capabilities: to space information to fit into specified field widths, and to display numbers in formats other than base 10. with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO; procedure basicio4 is -- this program demonstrates integer input/output i : integer := 5; -- this variable contains an integer number -- i initially has the value of 5 begin Put_Line( "This program displays information on the screen" ); Put_Line( "and reads information from the keyboard" ); New_Line; Put( "The value i is" ); Put( i ); New_Line; New_Line; Put_Line( "Type in an integer number." ); Get( i ); New_Line; Put( "The value i is" ); Put( i ); New_Line; New_Line; Put_Line( "'width =>' specifies the amount of room to display the number in." ); Put_Line( "This can be used to display columns of numbers." ); Put( "Using a width of 5, the value i is '" ); Put( i, width => 5 ); Put_Line( "'" ); New_Line; Put_Line( "'base =>' specifies a number system besides the normal base 10" ); Put( "Using binary notation, the value i is " ); Put( i, base => 2 ); New_Line; New_Line; Put_Line( "Set the variable Default_Width or Default_Base to avoid using" ); Put_Line( "'width =>' and 'base =>'." ); Put( "The Default_Width was " ); Put( Default_Width ); New_Line; Default_Width := 20; Put( "The Default_Width is now " ); Put( Default_Width ); New_Line;
Put( "The value i is '" ); Put( i ); Put_Line( "'" ); New_Line; end basicio4; This program displays information on the screen and reads information from the keyboard The value i is 5 Type in an integer number. 432 The value i is 432 'width =>' specifies the amount of room to display the number in. This can be used to display columns of numbers. Using a width of 5, the value i is ' 32' 'base =>' specifies a number system besides the normal base 10 Using binary notation, the value i is 2#110110000# Set the variable Default_Width or Default_Base to avoid using 'width =>' and 'base =>'. The Default_Width was 11 The Default_Width is now 20 The value i is ' 432'
Type Short_Short_Integer Short_Short Integer (wide) Short_Float Short_Float (wide text) Short_Integer Short_Integer (wide text) Integer Integer (wide text) Float Float (wide text) Long_Float Long_Float (wide text) Long_Integer
Text_IO Package Ada.Short_Short_Integer_Text_IO Ada.Short_Short_Integer_Wide_Text_IO Ada.Short_Float_Text_IO Ada.Short_Float_Wide_Text_IO Ada.Short_Integer_Text_IO Ada.Short_Integer_Wide_Text_IO Ada.Integer_Text_IO Ada.Integer_Wide_Text_IO Ada.Float_Text_IO Ada.Float_Wide_Text_IO Ada.Long_Float_Text_IO Ada.Long_Float_Wide_Text_IO Ada.Long_Integer_Text_IO
Long_Integer (wide text) Long_Long_Float Long_Long_Float (wide) Long_Long_Integer Long_Long_Integer (wide) Unbounded (String) Wide_Unbounded (String) Calender.Time
Digits Exponent External_Tag First - first index in an array First_Bit Floor - round down a floating-point value Fore Fraction Identity Image Input - convert value to a string Last - last index in an array Last_Bit Leading_Part Length - number of elements in an array Machine Machine_Emax - maximum real type exponent on your hardware Machine_Emin - minimum real type exponent on your hardware Machine_Mantissa - size of mantissa on your hardware in bits Machine_Overflows - true of your machine overflows real types [NQS] Machine_Radix Machine_Rounds Max - maximum value Max_Size_In_Storage_Elements Min - minimum value Model Modulus Output Partition_ID - for distributed processing Pos - position in a discrete type (such as an enumerated) opposite of val Position Pred - previous value in a discrete type Range - range of values for a type Read Remainder Round Rounding Safe_First Safe_Last Scale
Scaling Signed_Zeros Size - size of storage in bytes Small Stoarge_Pool - used to set the storage pool for a pointer Storage_Size Succ - next value in a discrete type Tag - tag for a tagged record Terminated - true if task has terminated Truncation Unbiased_Rounding Unchecked_Access - return access type, but ignore scope checks Val - value of a discrete type at a certain position opposite of pos Valid - determine if the expression evaluates to a legal result Value - convert string to a value opposite of image Version Wide_Image - same as image, but for a 16-bit string Wide_Value - same as value, but for a 16-bit string Wide_Width Write Here's a list of additional gnat-specific attributes: [should fold these in above] Abort_Signal - task abort exception Address_Size - number of bits in an address Bit - offset to first bit in object Default_Bit_Order - whether or not CPU uses high order first Elab_Body - the procedure that elaborates a package body Elab_Spec - the procedure that elaborates a package spec Enum_Rep - the numerical value of an enumerated identifier Fixed_Value - unchecked conversion of integer to a fixed type Img - shorthand for image Integer_Value - the reverse of Fixed_Value Machine_Bits - for compatibility with other Ada compilers Max_Interrupt_Priority - the maximum interrupt priority Max_Priority - the maximum task priority Maximum_Alignment - determine the bit alignment of an external object Mechanism_Code - how a parameter is passed to a subprogram Null_Parameter - for passing null pointer for a composite object Object_Size - for fixed and discrete types, default allocation size Passed_By_Reference - true if type is normally passed by reference
Range_Length - number of values in a discrete type Storage_Unit - same as System.Storage_Unit Tick - same as System.Tick Type_Class - return type basic class of an identifier (such an enumerated or array) Universal_Literal_String - return a string literal for a number Unrestricted_Access - like access, but has no accessibility or aliased view checks Value_Size - number of bits to represent a value of a given subtype Word_Size - same as System.Word_Size The following program demonstrates some of the basic Ada attributes. with text_io; procedure attrib is type enum is ( dog, mica, megabyte ); begin Text_IO.Put_Line( "Some Basic Ada Attributes:" ); Text_IO.New_Line; Text_IO.Put_Line( "Boolean bits is " & boolean'size'img ); Text_IO.Put_Line( "Short short integer bits is" & short_short_integer'size'img ); Text_IO.Put_Line( "Short integer bits is " & short_integer'size'img ); Text_IO.Put_Line( "Integer bits is " & integer'size'img ); Text_IO.Put_Line( "Long integer bits is " & long_integer'size'img ); Text_IO.Put_Line( "Long long integer bits is " & long_long_integer'size'img ); Text_IO.Put_Line( "Natural bits is " & natural'size'img ); Text_IO.Put_Line( "Positive bits is " & positive'size'img ); Text_IO.Put_Line( "Short float bits is " & short_float'size'img ); Text_IO.Put_Line( "Float bits is " & float'size'img ); Text_IO.Put_Line( "Long float bits is " & long_float'size'img ); Text_IO.Put_Line( "Long long float bits is " & long_long_float'size'img ); Text_IO.Put_Line( "Our 3 item enumerated bits is " & enum'size'img ); Text_IO.New_Line; Text_IO.Put_Line( "First integer is " & integer'first'img ); Text_IO.Put_Line( "Last integer is " & integer'last'img ); Text_IO.New_Line; Text_IO.Put_Line( "First enumerated is " & enum'first'img ); Text_IO.Put_Line( "Last enumerated is " & enum'last'img ); Text_IO.Put_Line( "Mica is in position" & enum'pos( mica )'img ); Text_IO.Put_Line( "The third enumerated is " & enum'val(2)'img );
Text_IO.New_Line; Text_IO.Put_Line( "The smallest float is" & float'small'img ); Text_IO.Put_Line( "The largest float is" & float'large'img ); Text_IO.Put_Line( "The number of digits in float is" & integer'image(float'digits) ); Text_IO.Put_Line( "The size of the mantissa in bits is" & float'mantissa'img ); Text_IO.Put_Line( "However, the CPU's mantissa is" & float'machine_mantissa'img ); end attrib;
Here are the results of the program on a Pentium II with gnat 3.11: Some Basic Ada Attributes: Boolean bits is 1 Short short integer bits is 8 Short integer bits is 16 Integer bits is 32 Long integer bits is 32 Long long integer bits is 64 Natural bits is 31 Positive bits is 31 Short float bits is 32 Float bits is 32 Long float bits is 64 Long long float bits is 9 Our 3 item enumerated bits is 2 First integer is -2147483648 Last integer is 2147483647 First enumerated is DOG Last enumerated is MEGABYTE Mica is in position 1 The third enumerated is MEGABYTE The smallest float is 1.17549435082228751E-38 The largest float is 1.93428038904620299E+25 The number of digits in float is 6 The size of the mantissa in bits is 21 However, the CPU's mantissa is 24
Boolean operations: and, or, not, xor Comparisons: >, >=, <, <=, =, /= Unary operations: +, -, abs Binary Operations: +, -, *, /, mod, rem, &, ** C: C boolean operators always short circuit. In Ada, there are both short circuiting operations and operations that do not short circuit.
Short circuiting operations are not considered true operators, and as such, can't be overloaded (see below).
Membership Tests (in and not in) are not considered true operators and can't be overloaded. if dog in aDogBreed then Put_Line( "The dog is a breed in the enumerated aDogBreed" ); end if; if i in 1..10 then Put_Line( "I is between 1 and 10" );
end if; 1..10 is called a range. The range attribute returns the range of values for a type. if salary not in MiddleManagementSalary'range then Put_Line( "The salary is not in the middle management type's range" ); end if; C: Assignment is considered a statement, not an operator.
New types are defined with the type statement. type aSalary isnew float; type aSmallSalary isnew Salary range 0.0 .. 35_000.0;
When you create a new type, the type is considered to be incompatible with the type it is derived from. If you want to add a small salary to a salary, you'll have to use type casting, even though they are both floats. totalSalary, BigSalary : aSalary; aSmallSalary : aSmallSalary; totalSalary := bigSalary + aSalary( smallSalary ); To type cast one type into another, use the type name and the value to convert in parantheses after it.
C: "(type) value" style of type casting doesn't work in Ada. C: "Ada has stronger restrictions on typecasting. In C, for example, you can cast a character pointer as an integer pointer. Although this is considered a bad programming practice, it is allowed. Ada will not allow this kind of type casting. Use subtype to create a type aSmallSalary that is compatible with aSalary. subtype aSmallSalary is aSalary range 0..35_000.0; Subtype can also be used to rename types. subtype sb is aSalaryBonusForEmployeesNamedBobStevens; In this example, sb is a short form for aSalaryBonusForEmployeesNamedBobStevens. "sb" is techncially called a "subtype mark", a term which sometimes appears in Gnat error messages. One common error, "subtype mark required in this context", indicates that there are several different types that could be used and you have to indicate to the compiler which should used.
Table : Predefined generic Text_IO packages for performing Input/Output Base Type Complex Numbers Complex Numbers (wide text) Decimals (NQS) Decimal Numbers (wide text) Enumerateds Enumerateds (wide text) Fixed Points Fixed Points (wide text) Floating Points Floating Points (wide text) Integers Integers (wide text) Modulars Modulars (wide text) Package Ada.Text_IO.Complex_IO Ada.Wide_Text_IO.Complex_IO Ada.Text_IO.Decimal_IO Ada.Wide_Text_IO.Decimal_IO Ada.Text_IO.Enumeration_IO Ada.Wide_Text_IO.Enumeration_IO Ada.Text_IO.Fixed_IO Ada.Wide_Text_IO.Fixed_IO Ada.Text_IO.Float_IO Ada.Wide_Text_IO.Float_IO Ada.Text_IO.Integer_IO Ada.Wide_Text_IO.Integer_IO Ada.Text_IO.Modular_IO Ada.Wide_Text_IO.Modular_IO
type peopleHeight is new integer range 1..10; type peopleHeightList is array( peopleHeight ) of integer; This creates an array from 1 to 10, the range of possible values for the type peopleHeight. This is the same as using array( peopleHeight'range ). You can create a multidimensional array by using more than one index to the table. type peopleStats is array( peopleHeight, peopleAge ) of integer; You can assign default values to an array using :=, the assignment operator. The list of values is enclosed in brackets. You can specify a specific value using =>, or specify a default with others =>. PeopleHeights1 : peopleHeightList := (others => 0); -- looks strange, but assigns 0 to all the heights in the entire list peopleHeights2 : peopleheightList := ( 10, others => 0 ); -- first height is 10, others are 0 peopleHeights3 : peopleHeightList := (5 => 15, others => 0 ); -- fifth height is 15, others are 0 Arrays are accessed by specifying values for the indices. To get the height for the fifth element in the PeopleHeights1 array, you'd type: Put_Line( PeopleHeights1( 5 )'img ); Records are collections of related information. Each subsection is referred to as a field. type employeeProfile is record name : string( 1..80 ); salary : aSalary; age : anAge; end record; You can assign default values to the fields in a record using :=, the assignment operator. type employeeProfile isrecord name: string( 1..80 ) := (others => ' '); salary : aSalary := 30_000.0; age: anAge := 30; end record; Default values for whole records can be specified when record variables are declared. Bob : employeeProfile := ("Bob Smith", 35_000.0, 37 ); Denise : employeeProfile := ( name => "Denise Jones", salary => 39_000.0, age => 42 ); In the above examples, we are creating a temporary record and then assigning that record to the variable. You can use this in the executable part of your program, not just in declarations. Ada will require a "subtype mark", an indication of what type of record you are making. NewRec := employeeProfile'("Bob Smith", 35_000.0, 37 ); employeeProfile' indicates that the record we've built should be treated as an employeeProfile record.
Although this looks almost exactly the same as type casting, it isn't type casting. Consider the following: J := long_integer'( 5 ); J := long_integer( 5 ); The first statement clarifies that 5 is a long_integer: this is a hint to the compiler that 5 should be treated as an long_integer. The second converts 5 from an integer to a long_integer. Record fields are accessed using a period and the field name. Bob.age := 37; A variant record is a record that contains different sets of mutually exclusive information. type employeeProfile( sex : aSex ) is record name : string( 1..80 ); salary : aSalary; age : anAge; case sex is when male => BeardLength : integer; when female => null; end record; (check syntax) In this example, a male employee has an additional field called BeardLength. (when you create a variant record, you must specify the descriminant).
procedure enumeration_fun is -- a demonstration of Ada 95 enumerated types type vowels is ( 'a', 'e', 'i', 'o', 'u', none ); -- characters may be used as well as identifiers. -- character sets are implemented this way.
The standard
type aDogBreed is ( Jack_Russel, Labrador, German_Shepherd, Other ); type aCanadianRegion is ( West_Coast, Arctic, Labrador, Other ); subtype coldPlaces is aCanadianRegion range Arctic..Other; -- names may overlap between enumerated types type anErrorCode is ( None, IOerror ); for anErrorCode use ( None => 0, IOerror => 7 ); -- specific values may be assigned to enumerated identifiers function toInteger is new unchecked_conversion( anErrorCode, integer ); begin -- Basic enumerated type operations put( "The vowel 'u' has a position of" ); put( integer'image( vowels'pos( 'u' ) ) ); put_line( " in the list." ); put( "The vowel after 'a' is" ); put( vowels'image( vowels'succ( 'a' ) ) ); put_line( "." ); -- Using a regular enumerated. Where an identifer belongs to -- two enumerated types, we have to apply a type qualifier when -- ambiguity comes up. put( "The item before German_Shepherd is " ); put( aDogBreed'image( aDogBreed'pred( German_Shepherd ) ) ); put_line( "." ); put( "The item after Labrador (the region) is " ); put( aCanadianRegion'image( aCanadianRegion'succ( Labrador ) ) ); put_line( "." ); put_line( "Listing of cold regions between 'Labrador' and 'Other':" ); for cr in coldPlaces'(Labrador)..other loop put_line( aCanadianRegion'image( cr ) ); end loop; -- Using an enumerated with assigned numbers. -- we assigned, we need unchecked_conversion. To get the number
put( "Error code " & anErrorCode'image( IOerror ) ); put( " is in position" & integer'image(anErrorCode'pos( IOerror ) ) );
put_line( "." ); put( "IOerror has a value of" & integer'image( toInteger( IOerror ) ) ); put_line( "." ); put( "The code before IOerror is " ); put( anErrorCode'image( anErrorCode'pred( IOerror ) ) ); put_line( "." ); put( "The code after NONE is " ); put( anErrorCode'image( anErrorCode'succ( None ) ) ); put_line( "." ); end enumeration_fun;
The vowel 'u' has a position of 4 in the list. The vowel after 'a' is'e'. The item before German_Shepherd is LABRADOR. The item after Labrador (the region) is OTHER. Listing of cold regions between 'Labrador' and 'Other': LABRADOR OTHER Error code IOERROR is in position 1. IOerror has a value of 7. The code before IOerror is NONE. The code after NONE is IOERROR. The boolean type is implemented as an enumerated with two values, true and false. False is always the predecessor of true. C: Ada enumerated types have more features than C's. You can use 'pred and 'succ to move through the list without casting the enumerated as an integer and using arithmetic. The position of an enumerated identifer is independent of its assigned value.
There are several ways to break up an Ada program. First, a procedure, such as the main program, is a subprogram which returns no value. procedure print_test is begin Text_IO.Put_Line( "This is a test" ); end print_test;
C: a procedure is a void function. The second is a function. A function is a procedure that can be used in a expressions because it returns a value. The value is returned with a return statement. function AddOne( X : integer ) return integer is begin return X+1; end AddOne; AddOne adds one to whatever is in the brackets. To add one to a variable called subtotal, you'd use it like this: Total := AddOne( SubTotal ); The value in the brackets is the parameter to the function. Parameters have modes: in, out or in out. In, which is the default if you specify a mode, means that the variable is treated as a constant. Out means the value is returned when the subprogram is finished. In out means the value goes into the subprogram, is changed, and is returned again when the subprogram is finished. In Ada, functions can only have in parameters, but procedures can have all three. procedure AddOne( x : in out integer ) is begin X := x + 1 end AddOne; ... AddOne( Subtotal );
C: There is no equivalent of pass-by-copy or pass-by-reference. See the section on interfacing C and Ada. There is also a special access mode, which means that the parameter must be an access variable. This is especially useful with tagged records. You can also get around the in out restriction on functions with the access mode. It is discussed in 11.10.2. C: An access variable is basically a pointer. These are described later. An example of multiple parameters: procedure DisplayCurrency( c : aCurrency; fieldWidth : integer; useDollarSign : boolean := true ) is useDollarSign, the third parameter, has a default value of true. Here's now you can call this procedure: DisplayCurrency( 1.97, 8 );
DisplayCurrency( 1.97, 8, false ); DisplayCurrency( 1.97, 8, useDollarSign => false ); DisplayCurrency( c => 1.97, fieldWidth => 8, useDollarSign => false ); If you really wanted to, you can also change the order of the parameters using the => convention. DisplayCurrency( fieldWidth => 8, useDollarSign => false, c => 1.97 ); You can also declare arbitrary blocks in Ada. These let you declare variables in the middle of a procedure or function or set apart the designated source code in it's own block. The form of a block is an optional declare section, and the block denoted by a begin and end. procedure nested is Date : integer; begin Date := 0; declare DaysInYear : constant integer := 365; begin Date := Date + DaysInYear; end; end nested; Here, DaysInYear only exists for the assignment statement that follows it. The main reason for blocks is to add an exception hander to a particular line without having to write a one line procedure. begin Total := Total / Average; exception when numeric_error => Text_IO.Put_Line( "Division by zero!" ); Total := 0; end; Here, if there's a numeric error during the division statement, it's caught and handled. Operators are in-fix functions, ones that take parameters on their left and right. For example, "+" is an operator. Ada lets you redefine most of the standard operators so they work with types of your choosing. You enclose the operators' symbol in double quotes. function "+"( e : employeeRecord, s : aSalary ) returns aSalary is begin return e.salary + s; end function; The above function will let you add a salary to an employee record, which assumes you are referring to the salary field in the employee record. Ada subprograms can have the same name as long as their parameters or return values are different. This is called overloading. If Ada can't determine which subprogram you are referring to, you'll receive an error when compiling. In the above example, "+" is overloaded since there's integer addition, floating addition, and the other built-in
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/10.html (26 of 29) [7/20/2001 11:33:07 AM]
meanings for "+", and our special salary addition we just defined. C: Assignment is not an operator in Ada. Assignment overloading can be simulated with controlled tagged records and the Adjust procedure.
The if statement is, well, a standard if statement which you can find in many languages. Here's an example: if x > 0 then Text_IO.Put_Line( "X is positive" ); elsif x < 0 then Text_IO_Put_Line( X is negative ); else Text_IO.Put_Line( "X is zero" ); end if; Ada provides two expression short-circuiting operators: "or else" and "and then". Short-circuiting means that the expression will not be evaluated if the left side doesn't satisfy the condition. if x > 0 or else y > 0 then In this case, y > 0 is only checked if x is not greater than zero. There is a general purpose loop statement, loop. Loops are exited with an exit statement. Ada provides a shorthand, exit when, to exit on a condition. loop X := X / 2.5; exit when X < 4.0; X := X + 1.0; end loop;
C: There's no equivalent of the C continue statement. There is a pretest loop, while, which determines whether or not the loop should be entered or reentered based on an expression at the top of the loop; while X >= 4.0 loop x := ( x / 2.5 ) + 1.0; end loop;
C: This is the equivalent of a C while loop. There is no post-test loop, like C's do loop. There is the standard for loop as well, to loop through a range of numbers. For loops in Ada may only loop by discrete numbers one unit at a time: no real numbers and no arbitrary stepping values. To go backwards through a range, use the word reverse. for I in 1..10 loop Total := Total + 1; end loop; To loop through an entire range of a type, use the 'range attribute. To loop through all the dogs in an enumerated type called aDogBreed, for dog in aDogBreed'range loop Also note that dog is implicitly defined. You don't have to declare it. Ada understands the type from the loop and the loop variable exists for the duration of the loop.
C: For is much more structured than C's for. Any loop can be exited with exit (or exit when). Ada allows you to label loops in order to exit out of several loops at once. In the following example, exit will exit the current loop and all loops up to and including OuterLoop. In this case, that's both loops. OuterLoop: while y > 0 loop while x > 0 loop x := x - y; if x = 37 then exit OuterLoop; end if; end loop; y := y * 2; end loop; There is a case statement as well, for testing a lot of different individual values. Ada requires a when others case to
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/10.html (28 of 29) [7/20/2001 11:33:08 AM]
make sure that all possible cases are handed. case DogBreed is when Unknown => Text_IO.Put_Line( "I don't know the breed" ); when Shepherd => Text_IO.Put_Line( "It's a shepherd" ); when others => Text_IO.Put_Line( "It's something else" ); end case; Ada also as a null statement, which is a placeholder to use when a statement is expected but none is needed. For example, you can't have an empty if statement--there must be at least a "null;". Multiple cases can be included with the vertical bar, or a range can be specified with an ellipsis. case TaxType is when local_tax => Tax := Tax + LocalTax; when federal_tax | govt_taxable => Tax := Tax + FederalTax; when others => null; -- perhaps a warning would be better here instead end case;
C: case is like switch, but the cases don't fall through. Cases can also use ranges, such as 1..10 or TaxSubtype'range. I really like goto's, and through a stroke of good luck, Ada includes a goto. Goto labels are denoted with double angle brackets (unlike loop labels that use a colon). for I in 0..10 loop -- some computations here if emergency then goto Help; end if; end loop; -- stuff that must not be executed in an emergency <<Help>> Text_IO.Put_Line( "We are now down here" );
<--Last Chapter
Table of Contents
Next Chapter-->
11.1 Packages
Ada package Description Define a package C Equivalent -
C:In C++, classes serve two purposes. First, they hide declarations. Second, they implement objects. In Ada, declaration hiding is done separately with packages: you do not need to use tagged records to use packages. Large Ada programs are divided up into packages. These are collections of procedures, functions, variables and other declarations that serve a similar purpose. You can have packages to draw text on the screen, or packages to do accounts payable, or package to work with complex numbers. To make it easier for different people to work on different packages, and to make packages self-documenting, Ada packages are divided into two parts. The package specification (or package spec) contains everything that other programs need to use your package. It contains all variables they can access, all the subprograms they can call, etc. For subprograms, you only use the subprogram header, the procedure or function line, to let the programmers know the name of the subprogram and what the parameters are. C: Subprograms declared in package specs are similar to C (non-static) function prototypes in header files. Unlike prototypes, which are optional, declarations in the package specs are required for subprograms to be exported outside of the package.
package Currency is -- This package defines US and Canadian dollars, and converts money between -- these two countries. subtype aMoney is float; type USDollars is new aMoney; type CanadianDollars is new aMoney; -- aMoney is separate so we can change the base type, if necessary, for -- US and Canadian dollars. USDollars and Canadian dollars deliberately -- incompatible because it could be messy if we mix them up procedure SetExchangeRates( USToCanadian, CanadianToUS : float ); function ToCanada( money : USDollars ) return CanadianDollars; function ToUS( money : CanadianDollars ) return USDollars;
-- Set the exchange rate between US and Canada, and two functions to -- convert between currencies. end Currency; In GNAT, you must save this package under the filename "currency.ads" (.ads for Ada Spec ). Here we create different money values two functions to convert between the different rates and a procedure to set the exchange rates to use in the functions. Notice there is no main program. With the spec complete, you can compile it to check for errors. To complete the package, we create a package body with the rest of the details, including the completed subprograms. With the implementation details hidden in the package body, other programmers don't have to worry about how currency is actually handled. package body Currency is
-- This package defines US and Canadian dollars, and converts money between -- these two countries. USToCanadaExchangeRate : float; CanadaToUSExchangeRate : float; procedure SetExchangeRates( USToCanadian, CanadianToUS : float ) is begin USTOCanadaExchangeRate := USToCanadian; CanadaToUSExchangeRate := CanadianToUS; end SetExchangeRates; function ToCanada( money : USDollars ) return CanadianDollars is begin return CanadianDollars( money * USToCanadaExchangeRate ); end ToCanada; function ToUS( money : CanadianDollars ) return USDollars is begin return USDollars( money * CanadaToUSExchangeRate ); end ToUS; -- Set the exchange rate between US and Canada, and two functions to -- convert between currencies. end Currency; Notice we have access to everything we defined in the package spec--we don't need to repeat anything in the body. Because the two exchange rate variables are defined inside the package body, they are invisible to other programmers. Save this package body as "currency.adb" (.adb for AdaBody). Make sure all pragma Stubbed's are removed for the finished subprograms. Compile both and you have a working package. To use your package in a program, use the with statement. with text_io, currency; procedure currencyTest is begin Currency.SetExchangeRate( 1.5, 0.7 );
Text_IO.Put_Line( "1 Canadian dollar is " & Currency.ToUS( 1.0 )'img ); end currencyTest; To have Ada check which package a subprogram belongs to, and avoid typing the package name constantly, use the use statement. with currency; use currency; ... SetExchangeRate( 1.5, 0.7 ); If the use statement creates an ambiguity, Ada will warn you that it can't determine which package SetExchangeRate is in. Package bodies may have main programs, a block at the end of all declarations marked with begin. This allows you to setup your package before it's actually used. In this case, we don't need one. Package specs may have private sections at the end of the spec. There will be times when you will have to provide information so that Ada can compile your spec, but you don't want the application programmer to be able use this information. For example, you might create a package to maintain a customer list, but you don't want the programmer to access the internal fields of a customer list since you might change them at some point. Just about anything in a package spec can be marked private, and the compiler expects the details to be specified in the private section. Declarations can also be limited private, meaning that besides having the details inaccessible to the programmer, the programmer can't assign between variables of the type. For example, use limited private if you think you may include pointers in the type at some time in the future. package CustomerList is
type aCustomerNumber is new positive range 1..1000; type aCustomerList is limited private; private type aCustomerArray is array( aCustomerNumber ) of string(1..120); type aCustomerList is record CurrentCustomer : aCustomerNumber; Customers : aCustomerArray; end record; -- no pointers yet, but we may some day, so it's limited private end CustomerList; In this example, a programmer can declare customer lists, but he cannot access the fields CurrentCustomer or Customers (because it's private), nor can he copy lists with assignment statements (because it's limited private). C: In C++, privacy is limited to classes. In Ada, virtually anything can be private. Packages can have children. A child package is a package that extends the capabilities of the parent package. You can use child packages to add features to existing packages, such as the standard Ada libraries, or to break up large packages into groups of related functions. Suppose you had a package called accounting that contains tools to handle accounting in general. You can create a child package for accounts payable begins like this: package Accouting.Accounts_Payable is In GNAT, save this package spec as "accounting-accounts_payable.ads", with a minus sign instead of a period.
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/11.html (3 of 39) [7/20/2001 11:33:44 AM]
A child package inherits the package spec from it's parent package. You can access anything in the accounting package, including anything private to which access is normally denied. When a program uses the statement with Accouting.Accounts_Payable; the parent package, accounting, is automatically with'ed as well (although you still have to use separate use's).
Elaboration is the initialization of a package that's done before the main program of the package is executed. Assigning values to constants, for example, is elaboration. C: Since C is a has no packages, the order of elaboration between files is determined strictly by the compilation order. For most projects, gnat will work out a good elaboration order on its own. However, large projects with packages referring to each other in complicated ways may require complex elaboration orders. gnat searches every possible elaboration order until it finds one that solves any ambiguities. To speed up projects with slow elaboration times, Ada and gnat provide a number of pragmas to give the compiler hints on the best compilation order and to solve any potential ambiguities.
end money_types; Pragma Preelaborate tells Ada that the package requires minimal elaboration. You should try pragma pure, and if it fails, try pragma preelaborate. gnat 3.11 introduced the gnat specific pragma No_Elaboration_Code. Sometimes this will work when Preelaborate will not. Sometimes you can declare a function as pure using pragma Pure_Funciton. A pure function is one with no side effects and one that's value doesn't change when executed with the same parameters each time. If the only thing standing in your way to using pure or preelaborate are some functions used to assign initial values, try declaring them as pure functions. If your package or program fails to meet the elaboration restriction requirements, the compiler will issue an error.
11.3 Objects
Ada type...tagged record type...new parenttype with record type...access all sometype Description Define an object Extend an object Define a pointer to a class C Equivalent class class ...: parenttype sometype *
'class abstract
C: Ada developed its own object oriented terminology because C's terminology can be ambiguous and confusing. Ada differentiates between a class and the structure that defines that class. An object in Ada is known as a tagged record. That is, it is a normal Ada record that has an invisible tag attached to it. The record type anEmployeeProfile is record name : string(1..80); age : anAge; end record; can be changed to a tagged record by adding the keyword tagged: type anEmployeeProfile is tagged record name : string(1..80); age : anAge; end record; Although these two records look the same, if we use the 'size attribute to see how much memory the records take up, we'll see that the tagged record is bigger. The extra space is used to store the invisible tag. Unlike normal records, fields can be added tagged record and a new tagged record can be created. This is called extending the record. To create a related record with additional fields, we use the keyword new: type anHourlyEmployee is new anEmployeeProfile with record hourlyRate : float; end record; A tagged record extended in this way has all the fields of anEmployeeProfile, but has the additional field of hourlyRate. anEmployeeProfile and anHourlyEmployee are said to be in the anEmployeeProfile class: the class is the collection of anEmployeeProfile and all record extended from it. Now we can create a access type (commonly called a pointer, though technically it isn't a pointer) to any record in the class: type anEmployeeProfilePtr is access all anEmployeeProfile'class; This pointer can be assigned either anEmployeeProfile record or anHourlyEmployee record. This is the purpose of the tagged record's invisible tag. The tag indicates the type of tagged record a pointer points to since these kinds of pointers can refer to more than one type of tagged record. This is sometimes called late binding. ptr1 : anEmployeeProfilePtr := new anEmployeeProfile( "Bob Smith", 45 ); ptr2 : anEmployeeProfilePtr := new anHourlyEmployee( "Denise Jones", 37, 17.50 ); Access variables are null until assigned a different value. They are the only variables in Ada to have a default value. There may be cases where you want to extend a type without adding new fields. Ada provides a shorthand phrase for this. For example, if you want to distinguish hourly employees that work at night as being separate from other hourly employees, use type aNightHourlyEmployee is new anHourlyEmployee with null record; In complex classes, there will be times when you'll want to define a record that you never intend to assign variables to. For example, anEmployeeProfile doesn't contain enough fields to completely describe any employee: only the tagged records
derived from anEmployeeProfile are usable. When particular record exists only to be extended it called an abstract record. You declare abstract records with the keyword abstract: type anEmployeeProfile is abstract tagged record name : string(1..80); age : anAge; end record; If you try to create anEmployeeProfile record, Ada will report an error since you said that this record can only be extended into other records. Ada requires that subprograms that work with tagged records be declared immediately after the type declaration. Each procedure or function can only take one tagged record as a parameter. C: Methods are normal subprograms with an object being referred to as a parameter. myobj.method( x ) would be method (myobj, x) or method( x, myobj) in Ada. type aSalaryEmployee is new anEmployeeProfile with record salaryRate : float; end record; procedure SetSalaryRate( s : in out aSalaryEmployee'class; rate : float ) is begin s.salaryRate := rate; end SetSalaryRate; function GetSalaryRate( s : aSalaryEmployee'class ) return float is begin return s.salaryRate; end GetSalaryRate; We've declared two one line subprograms that will work on any tagged record derived from aSalaryEmployee. C: Ada does not require constructors or destructors. Creating objects with these are discussed below. Subprograms can be marked as unusable in the same way as abstract tagged records. An abstract procedure or function is a placeholder. Declaring one requires that all types extended from this type must have this subprogram defined. If any do not have this subprogram, Ada will report an error. For example, if anEmployeeProfile had a procedure like procedure WriteEmployeeName( e : anEmployeeProfile ) is abstract; all employee profile records would be required to have a procedure called WriteEmployeeName. ASalaryEmployee will have a compilation error unless we add such a function: procedure WriteEmployeeName( e : aSalaryEmployee )is begin Text_IO.Put_Line( "Employee Name: " & e.Name ); end WriteEmployeename; WriteEmployeeName could also use aSalaryEmployee'class to refer aSalaryEmployee or any records we extend from it. To avoid ambiguity, only one tagged record subprogram can refer to any one type. This is different from some other object oriented languages where you can override a classwide subprogram with one that refers to a specific type. The advantage of no overriding is that someone reading your class knows exactly which subprogram will be used for a particular tagged record type--they don't need to read the entire class to make sure the subprogram isn't overridden later on. The disadvantage is that if
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/11.html (7 of 39) [7/20/2001 11:33:45 AM]
you can't use a classwide type, you'll have to write subprograms for each and every type in that class. In these cases, typecasting is useful. Tagged record types can be typecast as other tagged record types (in the same class) using typecasting. You need to do this if you want a dispatching call inside of a dispatching call. For example, if anEmployeeProfile has a GetSalaryRate function, we could call it by: procedure WriteEmployeeSalary( e : aSalaryEmployee'class ) is begin Text_IO.Put_Line( "The salary is" & GetSalaryRate( anEmployeeProfile( e ) ) ); end WriteEmployeeSalary;
Basic Ada tagged records don't do anything special when they are created or destroyed. If you want special actions to be executed automatically when a tagged record is created or destroyed, you can use the Ada.Finalization package. In this package is defined a special tagged record called Controlled. When you extend this tagged record, you can overload special procedures that Ada will automatically execute when necessary. This saves you from having to explicitly call such procedures yourself. type businessDepartment is new Finalization.Controlled with record departmentHead : aEmployeeProfilePtr := new aSalaryEmployee ( "Bob Smith", age => 45, rate => 42_000.0); end record; In this example, every time you allocate a businessDepartment variable, DepartmentHead is initialized with a dynamic allocation. We could write a procedure to free up this memory, but then we would have to remember to call it every time a variable is about to be discarded. A better way to handle this is to let Ada do the discarding for us. Finalize is the name of the procedure Ada calls when cleaning up a controlled tagged record. We create our own Finalize for our businessDepartment tagged record: procedure Finalize(bd : in out businessDepartment) is begin -- Finalize Free( bd.departmentHead ); end Finalize; Now Ada will automatically run this procedure before any businessDepartment variable is destroyed and we never have to worry about forgetting to free up the memory used by departmentHead. C: Finalize is the destructor. There are two other such automatic procedure we can use with controlled tagged records. Procedure Initialize is used when an object is first created, and procedure Adjust is used after an object is assigned in an assignment statement.
Adjust is very smart. Temporary storage is used for self-assignment. If Adjust fails because of an exception, Finalize is not executed. C:Adjust is like a C++ copy constructor, except that Ada will copy the object before calling Adjust. With a copy constructor, you must copy the object yourself in the body of the constructor. Unlike a copy constructor, Adjust only executes during assignment. with text_io, sample; use text_io, sample; procedure controlledtest is srp1, srp2 : SampleRecPtr; sr3 : SampleRec; begin Put_Line( "(This is the first line of the program)"); New_Line; Put_Line( "This is an example of controlled tagged records:" ); New_Line; Put_Line( "Executing 'srp1 := new SampleRec'"); srp1 := new SampleRec; New_Line; Put_Line( "Executing 'srp2 := srp1' (copying a pointer)" ); srp2 := srp1; New_Line; Put_Line( "Executing 'sr3 := srp1.all' (copying a record pointed to)" ); sr3 := srp1.all; New_Line; Put_Line( "(This is the last line of the program)"); end controlledtest;
with ada.finalization; use ada.finalization; package sample is type SampleRec is new Controlled with null record; type SampleRecPtr is access all SampleRec; -- sample controlled tagged record (and a pointer to same) procedure Initialize( sr : in out SampleRec ); procedure Finalize( sr : in out SampleRec ); procedure Adjust( sr : in out SampleRec ); -- these are required for controlled tagged records end sample;
package body sample is -- just print messages to show that these are working procedure Initialize( sr : in out SampleRec ) is begin Put_Line( "Initialize: Initialized tagged record" ); end Initialize; procedure Finalize( sr : in out SampleRec ) is begin Put_Line( "Finalize: Finalized tagged record " ); end Finalize; procedure Adjust( sr : in out SampleRec ) is begin Put_Line( "Adjust: Adjusted tagged record " ); end Adjust; end sample; Here is the output. The records being affected are noted in bold. Initialize: Initialized tagged record [sr3] (This is the first line of the program) This is an example of controlled tagged records: Executing 'srp1 := new SampleRec' Initialize: Initialized tagged record [srp1.all] Executing 'srp2 := srp1' (copying a pointer) Executing 'sr3 := srp1.all' (copying a record pointed to) Finalize: Finalized tagged record [sr3 (before record is copied)] Adjust: Adjusted tagged record [sr3 (after record is copied)] (This is the last line of the program) Finalize: Finalized tagged record [srp1.all] Finalize: Finalized tagged record [sr3] Ada also provides a second tagged record, Limited_Controlled, which is a controlled record that can't be assigned. Consequently, it has no adjust procedure.
type aClass is tagged record F1 : integer; end record; type anUnrelatedClass is tagged record U1 : integer; end record; type anMIExample is new aClass with UC : anUnrelatedClass; -- a field, not an extension end record; anMIExample is a tagged record belonging to aClass, not to anUnrelatedClass. If you extend anMIExample, it will inhert F1 from its parent class. Since anUnrelatedClass UC is nested, you can still access it as a field using the prefix "UC.". [ BETTER EXAMPLE -- KB ]
11.7 Generics
Ada allows you to create source code templates, called generics. With generics, you can specify a routine with only general information about the kinds of types the routine can work on. Later a programmer instantiates the generic by giving a type. Generics are useful for things like sort routines, which work basically the same way on different data types. C: Generics are similar to C++ templates. Ada contains several standard generic procedures. The most important one is unchecked_deallocation. This long winded procedure deallocates memory allocated with new. In order to free up memory, you must instantiate an unchecked_deallocation for each access type you are going to use new on. You have to specify both the access type and type of data that is being pointed to. with unchecked_deallocation; type booleanPtr is access all boolean; procedure Free is new unchecked_deallocation( boolean, booleanPtr ); Free is a new version of unchecked_deallocation compiled for booleanPtr's to a boolean type. Ada was designed for the possibility automatic storage recovery, that everything that was allocated in a subprogram would be deallocated automatically when the subprogram is left. Unfortunately, gnat was implemented without this feature and all memory has to be explicitly deallocated. Another standard generic you'll run into is unchecked_conversion. This converts a variable of one type to another by physically copying to data from one to the other, such as an array of 8 bits to a short_short_integer. Although you can write generic subprograms, most of the time you use generics will be for creating generic packages. Generic packages have a spec and body like normal Ada packages, but they begin with a list of parameters to the package that must be filled in when the generic is instantiated. generic -- these are the parameters for the generic type ListElement is <>; -- unspecified list element procedure ">="( left, right : ListElement); -- a procedure to sort by package SimpleList is type List procedure procedure procedure is array( 1..100) of ListElement; Add( l : list; e : ListElement); Sort( l : list ); Display( l : list );
end SimpleList; (check--KB) In this example, SimpleList takes some kind of data type called a ListElement, the items that compose the lists. Besides <>, Ada offers a number of other non-specific type descriptors to give the compiler an idea of what kind of types are acceptable.
Since the ListElement could be an aggregate and we can't assume we can do simple comparisons, the programmer must also specify a procedure to sort the elements by. Once you write the package body (no generic section in the package body, just a regular package body), you can instantiate the generic package in a program. After instantiation, use the package like any other package. with SimpleList; procedure ListTest is package BooleanList is new SimpleList( boolean, ">=" ) -- in this case, the normal ">=" will sort booleans begin BooleanList.Add( mylist, true ); BooleanList.Add( mylist. False ); end ListTest; Now you'll notice that generics and tagged records share a lot of capabilities. You can use both to write subprograms that work on a variety of types. Tagged records are referred to as dynamic polymorphism, because which subprogram to call gets determined while the program is running. Generics are referred to as static polymorphism, because the subprograms are generated when the generic is instantiated by the compiler and which subprogram to call is known when the program is compiled. The better approach depends on what you are doing. In general, generics run faster but take up more space because the compiler generates separate subprograms for each type. Tagged records take up less space but tend to run slower. Variant records and tagged records, likewise, share much in common. Although variant records can be simulated with tagged records, you'll need to decide which is the best choice depending on what you are trying to accomplish. Variant records tend to be smaller and faster, but are harder to extend than tagged records. Since tagged records are naturally used to create variables that are similar to one another, you might wonder if you'd ever create a single variable of a tagged record type. These are called singletons, and are used in frequently in languages like C++. They are popular because they have a specific elaboration order and provide access to features only available in objects (such as private members). Programmers doing this have no need to create a class of several objects. However, Ada has an easily controlled elaboration process and features such as privacy are not specific to tagged records. As a result, there is rarely a need for singletons in Ada programs.
11.8 Exceptions
Ada e : exception raise e exception clause Description Declare an exception Raise/throw an exception Handle/catch an exception C Equivalent throw type try...catch
C:In C++, exceptions are performed by throwing types (typically objects). In Ada, an exception is a separate type. Only exceptions can be thrown--you can't throw types. Exceptions aren't related to objects in any way. When you throw in C++, you do so in a try block. In Ada, you can throw an exception in any block. The exception clause (equivalent to catch) at the end of the block is optional.
What do you do when something unexpected error occurs? Unexpected errors are called exceptions in Ada. For example, running out of memory is an exception. Ada has a number of predefined exceptions, and examples of when they occur: q CONSTRAINT_ERROR - number out of range, like assigning -1 to a positive variable q NUMERIC_ERROR - dividing by zero q SELECT_ERROR - caused by task select statement with no else part q STORAGE_ERROR - running out of memory q TASKING_ERROR - failure of a task to handle an entry call q PROGRAM_ERROR - hitting the end of a function without returning anything q ASSERT_ERROR - a pragma assert failed You can turn off checking for most of these errors using pragmas, but they are usually a sign that something if fundamentally wrong with your program. Gnat provides a compiler option to turn these checks off for the release version of a program. The standard libraries have additional exceptions defined. To handle an exception, you need an exception part at the end of a subprogram. When the exception is raised (occurs), execution immediately jumps down to the exception part. The exception part contains a list of exceptions to check for, plus an others part, similar to a case statement. procedure exceptional( Total : out integer) is begin -- do some stuff exception when constraint_error => Total := 0; when storage_error => Total := -1; when others => raise; end exceptional; Raise causes the exception to be reraised to the calling subprogram. If one subprogram doesn't handle the exception, it's raised in the subprogram that called it. This continues until the exception is handled by an exception part, or the main program is it. In the worst case, if the main program has no exception part, the program terminates and the exception name is printed on the screen. One use for exception parts to deallocate access types so the memory isn't lost when unexpected errors occur. You can define and raise your own exceptions. procedure exceptional2 is Accounting_Error : exception; C: Ada exceptions do not carry any additional information when raised. You can simulate error messages and other information with global variables. Pragma Assert provides an exception for debugging programs. Assert is described in 8.2. For more advanced manipulation of exceptions, you'll need to use Ada.Exceptions and its related packages. These are described in 12.15.
Dynamic allocation is reserving memory for variables while the program is running. The memory is allocated from a region called the default storage pool. GNAT sometimes refers to this as the unbounded no reclaim pool because the only limit on the amount of memory you can allocate is the physical limit of the machine, and memory is not reclaimed through garbage collection. C: The default storage pool is effectively the "heap". Ada uses access types to create a handle to dynamically declared variables. type IntegerPtr is access all Integer; ip : integerPtr := new Integer; In this example, IP access a dynamically declared integer. IP is only large enough to hold the address of where the integer is located. To access the integer, we have to add the suffix .all to IP. This is called dereferencing. ip.all := 5; If you are dereferencing multiple pointers, the all is only required at the end to indicate that the final pointer is to be dereferenced ( for example,. ptr1.ptr2.ptr3.all). The word all in access all is not strictly required. If all is included, the IntegerPtr type will be compatible on any other integer pointer. Without all, Ada imposes certain restrictions on what the access type can point to, but in general always use access all. Memory allocated with new is freed with unchecked_allocation (see the section on generics). You can assign initial values when you create a new dynamic variable. In the following example, we declare, allocate memory and assign an initial value all at once. ip : integerPtr := new Integer( 5 ); To create a record with a pointer to itself, have an empty type statement with the record first: type LinkedListRecord; type LinkedListPtr is access LinkedListRecord; type LinkedListRecord is record info : string(1..80); next : LinkedListPtr; end record; To point to variables you've declared, you must declare those variables as aliased to indicate they can be pointed to. To get the address of something, use the 'access attribute. type CustomerArray is array(1..100) of CustomerRecord; type CustomerArrayPtr is access all CustomerArray; ca : aliased CustomerArray; cp : CustomerArrayPtr := ca'access; cp now points to ca. Individual array elements can also be aliased.
type CustomerArray is array(1..100) of aliased CustomerRecord; type CustomerArrayPtr is access all CustomerArray; ca : CustomerArray; c15p : CustomerArrayPtr := ca(15)'access; Ada will give you an error when you try to use 'access when the object pointing to may disappear after the pointer does. If you're absolutely certain that this won't happen, you can circumvent the error by using 'unchecked_access. An access type is necessarily just the address of a dynamic object. To get the address of an access type, it's best to use gnat's generic package System.Address_To_Access_Conversions. type intacc is access all integer; package strConvert is new System.Address_To_Access_Conversions(intacc); ... string_address := strConvert.To_Address( SomeIntAccVar ); Ada 83: The original use of .all created too many ambiguities. Ada 95 requires greater use of .all.
11.10 Callbacks
A callback is a pointer to a subprogram. They are called callbacks because you usually give the pointer to another subprogram that calls the procedure pointed to whenever it needs to. You can declare callbacks using type. type UpdateWindow is access procedure; type DisplayNewValue is access procedure( newval : integer ); One important restriction is that Ada requires that callbacks to refer to global subprograms. This is done to ensure that the access variable always points to an existing subprogram. You cannot create a callback to a local procedure or function, even if it's perfectly safe to do so. If you try, you'll get an obscure error message about one level being deeper than another. The gnat equivalent for 'unchecked_access for callbacks is 'unrestricted_access, which you can use if you're absolutely sure the subprogram you're using will not save the access when it's finished running. You can get the address of a procedure using 'access. Suppose MyUpdateProcedure is a procedure fitting the description of UpdateWindow, a procedure with no parameters. updatePtr : UpdateWindow := MyUpdateProcedure'access; procedure DoComplexSlowComputation( updatePtr); To call a callback, use the dereference operator .all. UpdatePtr.all;
for AccountingRecPtr'storage_pool use my_pool; Gnat defines several storage pools besides the default storage pool. Perhaps the most useful is the debug pool. This storage pool, available in version of Gnat before 3.12, works the same as the default storage pool except that it performs run-time checks for several different pointer related problems. If a check fails, an exception is raised. The following program illustrates the errors caught by debug pool access types. with Ada.Text_IO, System.Pool_Local, System.Debug_Pools; use Ada.Text_IO; with unchecked_deallocation; -- This is an example of using the GNAT-specific debug_pool -- storage pool. procedure debpools is type sales_record is record salesman_code : integer; sales_amount : float; end record; -- just a typical record type salesptr_normal is access all sales_record; --- This is a normal access type. It is allocated -- in the default storage pool (aka "the heap"). -- The default storage pool is called -- Unbounded_No_Reclaimed_Pool. That is, there's -- no size limit, and memory is not reclaimed by -- garbage collection. -- A debug pool ----------------------------------------sales_debug_pool : System.Debug_Pools.Debug_Pool; -- declare a new debug pool --- Debug_Pool is a GNAT-specific pool. type salesptr_debug is access all Sales_Record; for salesptr_debug'storage_pool use Sales_Debug_Pool; ----This access type has no garbage collection but raises exceptions on allocation or deallocation errors, useful for tracking down storage leaks. All 4 possible exceptions are
-- shown in this program. procedure Free is new Unchecked_Deallocation( sales_record, salesptr_debug ); -- procedure to deallocate salesptr_debug access types sr : aliased Sales_Record; spd, spd2, spd3 : salesptr_debug; begin Put_Line( "Fun with debug storage pools!"); New_Line; -- Debug Pool Exception #1 begin Put_Line( "Accessing a non-allocated access type is an exception:" ); Put_Line( "spd.salesman_code := 1"); spd.salesman_code := 1; -- error: not allocated exception when System.Debug_Pools.Accessing_Not_Allocated_Storage => Put_Line( "***Accessing_Not_Allocated_Storage raised" ); when others => Put_Line( "***Unexpected exception" ); raise; end; New_Line; -- Debug Pool Exception #2 begin Put_Line( "Freeing a non-allocated access type is an exception:" ); Put_Line( "spd2 := sr'access --not allocated in pool" ); Put_Line( "Free( spd2 )" ); spd2 := sr'access; Free( spd2 ); exception when System.Debug_Pools.Freeing_Not_Allocated_Storage => Put_Line( "***Freeing_Not_Allocated_Storage raised" ); when others => Put_Line( "***Unexpected exception" ); raise; end; New_Line; spd := new Sales_Record'( salesman_code => 1, sales_amount => 55.50 ); -- Debug Pool Exception #3 begin Put_Line( "Accessing deallocated access type is an exception:" ); Put_Line( "spd := new Sales_Record..."); Put_Line( "Free( spd )" ); Put_Line( "spd.salesman_code := 1"); Free( spd ); spd.salesman_code := 1; -- error: not allocated
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/11.html (18 of 39) [7/20/2001 11:33:46 AM]
exception when System.Debug_Pools.Accessing_Not_Allocated_Storage => Put_Line( "***Accessing_Deallocated_Storage raised" ); when others => Put_Line( "***Unexpected exception" ); raise; end; New_Line; spd := new Sales_Record'( salesman_code => 1, sales_amount => 55.50 ); -- Debug Pool Exception #4 begin Put_Line( "Freeing deallocated access type is an exception:" ); Put_Line( "spd := new Sales_Record..."); Put_Line( "spd2 := spd" ); Put_Line( "Free( spd )" ); Put_Line( "Free( spd2 )" ); spd2 := spd; Free( spd ); Free( spd2 ); exception when System.Debug_Pools.Freeing_Deallocated_Storage => Put_Line( "***Freeing_Deallocated_Storage raised" ); when others => Put_Line( "***Unexpected exception" ); raise; end; New_Line; end debpools; Program Result: Fun with debug storage pools! Accessing a non-allocated access type is an exception: spd.salesman_code := 1 ***Accessing_Not_Allocated_Storage raised Freeing a non-allocated access type is an exception: spd2 := sr'access --not allocated in pool Free( spd2 ) ***Freeing_Not_Allocated_Storage raised Accessing deallocated access type is an exception: spd := new Sales_Record... Free( spd ) spd.salesman_code := 1 ***Accessing_Deallocated_Storage raised Freeing deallocated access type is an exception: spd := new Sales_Record... spd2 := spd Free( spd ) Free( spd2 ) ***Freeing_Deallocated_Storage raised
To create your own storage pools, you need to extend the Root_Storage_Pool tagged record found in the System.Storage_Pools package. [give example--KB]
procedure chargeInterest( cp : access aCustomerAccount ) is -- update the customer record by charging the interest begin cp.moneyOwing := cp.moneyOwing * money(1.0 + cp.interest ); end chargeInterest; procedure chargeInterest2( c : in out aCustomerAccount ) is -- update the customer record by charging the interest begin c.moneyOwing := c.moneyOwing * money(1.0 + c.interest ); end chargeInterest2; function chargeInterest3( cp : access aCustomerAccount ) return boolean is -- update the customer record by charging the interest -- if under 1000, don't charge interest and return false begin if cp.moneyOwing < 1000.0 then
return false; end if; cp.moneyOwing := cp.moneyOwing * money(1.0 + cp.interest ); return true; end chargeInterest3; cp : aCustomerPtr; begin Put_Line( "An Example of Access Parameters" ); New_Line; cp := new aCustomerAccount; Put_Line( "chargeInterest uses an access parameter" ); cp.moneyOwing := 1500.0; Put_Line( "Charging interest on" & cp.moneyOwing'img ); chargeInterest( cp ); Put_Line( "After interest, money owing is" & cp.moneyOwing'img ); New_Line; Put_Line( "chargeInterest2 uses an in out parameter" ); cp.moneyOwing := 1700.0; Put_Line( "Charging interest on" & cp.moneyOwing'img ); chargeInterest2( cp.all ); Put_Line( "After interest, money owing is" & cp.moneyOwing'img ); New_Line; Put_Line( "chargeInterest3 is a function with an access parameter" ); cp.moneyOwing := 1900.0; Put_Line( "Charging interest on" & cp.moneyOwing'img ); if chargeInterest3( cp ) then Put_Line( "After interest, money owing is" & cp.moneyOwing'img ); else Put_Line( "No interest was charged" ); end if; New_Line; Put_Line( "A null pointer for an access parameter causes an exception" ); cp := null; Put_Line( "Charging interest on a null pointer" ); if chargeInterest3( cp ) then Put_Line( "After interest, money owing is" & cp.moneyOwing'img ); else Put_Line( "No interest was charged" ); end if; New_Line; exception when constraint_error => Put_Line( Standard_Error, "Constraint error exception raised" ); when others => Put_Line( Standard_Error, "Unexpected exception raised" );
end accparm;
An Example of Access Parameters chargeInterest uses an access parameter Charging interest on 1.50000E+03 After interest, money owing is 1.72500E+03 chargeInterest2 uses an in out parameter Charging interest on 1.70000E+03 After interest, money owing is 1.95500E+03 chargeInterest3 is a function with an access parameter Charging interest on 1.90000E+03 After interest, money owing is 2.18500E+03 A null pointer for an access parameter causes an exception Charging interest on a null pointer Constraint error exception raised
11.11 Multithreading
Gnat comes with two alternative libraries for multithreading support. It can either use the native Linux pthreads (built into libc6), or it can use FSU (Florida State University) threads that are included with gnat. By default, the Linux version of gnat is compiled for Linux native threads.
11.11.2 Tasks
In Ada, a thread is referred to ask a task. It has nothing to do with multitasking, as its name might imply. A task runs independently of the main program, either by true parallelism on a multiprocessor computer, or as a separate job on a single processor computer. There is no way to specify which processor will receive a task: Linux takes care of this automatically.
Multithreaded programs have limits on the stack size for each thread--this is true for all Linux computer languages. Gnat 3.13 has an 8 Meg stack size limit per thread. Older versions had limits as low as 1 Meg per thread because of limits imposed by the Linuxthreads library. A task can take on several forms. In its simplest form, a task is structured like a package, with a specification and a body. The following is an example of a simple thread that waits 5 seconds after a program is started and displays a message.
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/11.html (22 of 39) [7/20/2001 11:33:46 AM]
task SimpleTask; task body SimpleTask is begin delay 5.0; Put_Line( "The simple task is finished"); end SimpleTask; The specification, like a package, indicates what identifiers are available to the outside world. The SimpleTask thread doesn't communicate with the main program: it's specification is only one line long. Communicating with the rest of the program can be difficult. For example, with the tasks and main program running in parallel, sharing variables can be difficult. How does one task know when another task or the main program is finished changing the value of a variable? If a task works with a variable that's only partially been udpated, the data will be corrupt. Ada provides two ways for a thread to communicate with the rest of the program. The first communication method is called a rendezvous. One task communicates with another by sending a request. A task may send a request to another task to update a certain variable, or to perform a certain action, that would otherwise risk data corruption. Because this communication happens "on the fly", it's declared in two parts. First, in the task specification, a list of all requests the task is prepared to accept. These are called entry statements and look much like procedure declarations. Suppose we write a task to keep a running total, to be shared between several other tasks. We use a separate task to keep the total from being corrupted. task CountingTask is entry add( amount : integer ); end CountingTask; In the task body, a task indicates when it's ready to accept messages using the accept statement. This statement checks for outstanding requests from the rest of the program. task body CountingTask is runningTotal : integer := 0; begin loop accept add( amount : integer ) do runningTotal := runningTotal + amount; end add; end loop; end CountingTask; When this thread runs, accept statement will check for an add request. If there are no outstanding add requests, the thread suspends itself until a request is sent, waiting indefinitely for a new request. Suspending for a request is known as blocking. An accept statement with do part will cause your task to wait for that request and then do nothing. You can do this to synchronize two tasks. accept WaitUntilITellYouToGo; Suppose we add another entry statement to read the current value of the running total. task CountingTask is entry add( amount : integer ); entry currentTotal( total : out integer);
end CountingTask; In this case, we want the task to check for two different requests. The Ada select statement keeps a task from blocking and instead checks for multiple messages. task body CountingTask is runningTotal : integer := 0; begin loop select accept add( amount : integer ) do runningTotal := runningTotal + amount; end add; or accept currentTotal( total : out integer ) do total := runningTotal; end currentTotal; end select; end loop; end CountingTask; In this example, the task will repeatedly check for an add request or a currentTotal request. To communicate with the task, we make calls to the task as if it were a package. For example, to send a message to add 5 to the running total, we'd use CountingTask.Add( 5); Because accept is a statement that executes at run-time, you can create any kind of message policy you want. Some messages can block. Some messages can be checked. You can force certain message to be handled before others. The final "or" part of a select can contain instructions to execute when none of the accepts statements are executed. For example, a task can end itself with the terminate command. If you want a task to terminate when there are no more requests, add a or terminate at the end of your select statement. If select statement doesn't give you enough control over blocking, select can include when clauses. The clauses work like an if statement, executing the accept statement only if a condition is true. If time2accept is a boolean variable, you could write select when time2accept => accept add( amount : integer ) do A when clause is referred to as a "guard" because, like a crossing guard, the accept will not execute unless the guard gives permission. Ada also has a delay statement. Delay forces a task (or a program) to suspend itself for a number of seconds. To wait for three-and-a-half seconds, use delay 3.5; You can place a delay statement in the last "or" part of a select statement to force the task to suspend itself for a few seconds if no requests were found.
Delay can also wait for a particular time. The time is expressed using the Ada.Calendar time format. If Tomorrow is a variable with the time of midnight tomorrow in the Ada.Calendar time format, you can delay the start of a task with delay until Tomorrow; In an emergency, one task can terminate another with the abort statement. abort CounterTask; Ada provides a variation of the select statement for tasks that timeout when they cannot complete their work in time. This version has two parts: the first part consists of what to do if the task isn't completed in time, and the second consists of the task to complete. For example, if BigCalculation is a slow process that we wish to timeout after 30 seconds, select delay 30.0; Put_Line( "Timeout!" ); then abort BigCalculation; end select; In this example, BigCalculation will continue for up to 30 seconds. If it doesn't finish, "Timeout!" is displayed and BigCalculation is aborted.
Tasks can also be declared in records as well, or allocated dynamically using the new operator. type aTaskPtr is access CountingTask; tp : aTaskPtr; ... tp := new CountingTask; Using new, you can create as many copies of a particular task as you need.
entry add( amount : integer ); ... protected body CountingType is entry add( amount : integer ) when runningTotal<100 is begin runningTotal := runningTotal + amount; end add; ... Like tasks, you can create protected types by declaring the specification with "protected type" instead of "protected". You can then create arrays of protected items, or declare protected items dynamically. This covers the basics of Ada's multithreading capabilities. There's much more that Ada can do. If you are writing complicated multithreading programs, you're encouraged to investigate the Ada Reference Manual for more information.
type office_number is new contact_info with record office : integer; end record; procedure free is new ada.unchecked_deallocation( contact_info'class, contact_ptr); -- A Stream File and Its Stream stream_file : Ada.Streams.Stream_IO.File_Type; the_stream : Ada.Streams.Stream_IO.Stream_Access; contact : contact_ptr; begin Put_Line( "An example of Ada.Streams.Stream_IO and a Class" ); Put_Line( "-----------------------------------------------" ); New_Line; Create( stream_file, out_file, "contact_list.stream" ); Put_Line( "Created a stream file" ); -- open the stream file the_stream := stream( stream_file ); -- get a stream representing the file's contents contact := new phone_number'( phone => "1-905-555-1023" ); contact_info'class'output( the_stream, contact.all ); free( contact ); Put_Line( "Wrote a phone number" ); -- write a record contact := new office_number'( office => 8023 ); contact_info'class'output( the_stream, contact.all ); free( contact ); Put_Line( "Wrote an office number" ); -- write a record Close( stream_file ); New_Line; -- close the stream file -- Read Them Open( stream_file, in_file, "contact_list.stream" ); Put_Line( "Opened a stream file" ); -- open the stream file the_stream := stream( stream_file ); -- get a stream representing the file's contents while not End_of_File( stream_file ) loop declare contact : contact_info'class := contact_info'class'input( the_stream );
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/11.html (28 of 39) [7/20/2001 11:33:47 AM]
begin -- if this were more sophisticated, we could write a Put procedure -- for each tagged record and use dynamic dispatching if contact in phone_number then Put_Line( "read a phone number" ); elsif contact in office_number then Put_Line( "read an office number" ); else Put_Line( "read something else" ); end if; end; end loop; Close( stream_file ); -- close the stream file end class_stream;
An example of Ada.Streams.Stream_IO and a Class ----------------------------------------------Created a stream file Wrote a phone number Wrote an office number Opened a stream file read a phone number read an office number Files of items of the same time are more easily created with Ada.Sequential_IO or Ada.Direct_IO. Custom streams can be created to save or trasmit data in other ways such as in memory or through a network connection. Custom streams are created as tagged records extended from a root class, Ada.Streams.Root_Stream_Type'class. type My_Stream is new Root_Stream_Type with record ... Your stream type must override the abstract Red and Write subprograms to add and remove data from the stream. The following is an in-memory stream creating by Warren Gay. This stream can share data between programs, buffering the data as text in memory. If a buffer overflow occurs, an END_ERROR is raised. -- $Id: memory_stream.ads,v 1.1 2000/11/26 05:00:18 wwg Exp $ -- (c) Warren W. Gay VE3WWG [email protected], [email protected] --- Protected under the GNU GPL License with Ada.Finalization, Ada.Streams; use Ada.Finalization, Ada.Streams; package Memory_Stream is type Stream_Access is access all Ada.Streams.Root_Stream_Type'Class; type Memory_Buffer(Max_Elem: Stream_Element_Offset) is new Controlled with private;
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/11.html (29 of 39) [7/20/2001 11:33:47 AM]
--------------------------------------------------- The new Stream Type, which must be derived from -- Root_Stream_Type. Note that Root_Stream_Type is -- NOT derived from Controlled, so if -- controlled attributes are necessary, they must -- be defined separately, and embedded into this -- object, as is done with Memory_Buffer here. -------------------------------------------------type Memory_Buffer_Stream(Max_Elem: Stream_Element_Offset) is new Root_Stream_Type with record Mem_Buf: Memory_Buffer(Max_Elem); -- Object with Finalization end record; type Memory_Buffer_Stream_Ptr is access all Memory_Buffer_Stream; -- The overloaded abstract for Read procedure Read(Stream: in out Memory_Buffer_Stream; Item: out Stream_Element_Array; Last: out Stream_Element_Offset); -- The overloaded abstract for Write procedure Write(Stream: in out Memory_Buffer_Stream; Item: in Stream_Element_Array); -- Rewind the Read Memory Buffer Index procedure Rewind_Read(Stream: Stream_Access); -- Rewind the Write Memory Buffer Index procedure Rewind_Write(Stream: Stream_Access); -- To permit easy destruction of this stream procedure Free(Stream: Stream_Access); private --------------------------------------------------- To create a Memory_Buffer stream with an -- Initialize procedure, it must be derived from -- a Controlled type. Unfortunately, the type -- Root_Stream_Type is not derived from the -- Controlled type, so it is done privately here. -------------------------------------------------type Memory_Buffer(Max_Elem: Stream_Element_Offset) is new Controlled with record Read_Offset: Stream_Element_Offset; Write_Offset: Stream_Element_Offset; Buffer: Stream_Element_Array(1..Max_Elem); end record; procedure procedure procedure Item: Last: procedure procedure Initialize(Buf: in out Memory_Buffer); Write(Buf: in out Memory_Buffer; Item: in Stream_Element_Array); Read(Buf: in out Memory_Buffer; out Stream_Element_Array; out Stream_Element_Offset); Rewind_Read(Buf: in out Memory_Buffer); Rewind_Write(Buf: in out Memory_Buffer);
end Memory_Stream;
-- $Id: memory_stream.adb,v 1.1 2000/11/26 05:00:18 wwg Exp $ -- (c) Warren W. Gay VE3WWG [email protected], [email protected] --- Protected under the GNU GPL License with Ada.Text_IO; use Ada.Text_IO; with Ada.Finalization; use Ada.Finalization; with Ada.IO_Exceptions; use Ada.IO_Exceptions; with Ada.Unchecked_Deallocation; package body Memory_Stream is --------------------------------------------------- Read from a Memory Buffer Stream : -------------------------------------------------procedure Read(Stream: in out Memory_Buffer_Stream; Item: out Stream_Element_Array; Last: out Stream_Element_Offset) is begin Read(Stream.Mem_Buf,Item,Last); end Read; --------------------------------------------------- Write to a Memory Buffer Stream : -------------------------------------------------procedure Write(Stream: in out Memory_Buffer_Stream; Item: in Stream_Element_Array) is begin Write(Stream.Mem_Buf,Item); end Write; --------------------------------------------------- Rewind the Read Memory Buffer Index -------------------------------------------------procedure Rewind_Read(Stream: Stream_Access) is Mem_Str: Memory_Buffer_Stream_Ptr := Memory_Buffer_Stream_Ptr(Stream); begin Rewind_Read(Mem_Str.Mem_Buf); end Rewind_Read; --------------------------------------------------- Rewind the Write Memory Buffer Index -------------------------------------------------procedure Rewind_Write(Stream: Stream_Access) is Mem_Str: Memory_Buffer_Stream_Ptr := Memory_Buffer_Stream_Ptr(Stream); begin Rewind_Write(Mem_Str.Mem_Buf); end Rewind_Write;
--------------------------------------------------- Free a Memory Buffer Stream : -------------------------------------------------procedure Free(Stream: Stream_Access) is type Memory_Buffer_Stream_Ptr is access all Memory_Buffer_Stream; procedure Free_Stream is new Ada.Unchecked_Deallocation(Memory_Buffer_Stream,Memory_Buffer_Stream_Ptr); Str_Ptr: Memory_Buffer_Stream_Ptr := Memory_Buffer_Stream_Ptr(Stream); begin Free_Stream(Str_Ptr); end Free; --------------------------------------------------- Private Implementation : ---------------------------------------------------------------------------------------------------- Initialize a Memory_Buffer Object : -------------------------------------------------procedure Initialize(Buf: in out Memory_Buffer) is begin Buf.Read_Offset := Buf.Buffer'First; Buf.Write_Offset := Buf.Buffer'First; end Initialize; --------------------------------------------------- Write to a Memory Buffer Object : -------------------------------------------------procedure Write(Buf: in out Memory_Buffer; Item: Stream_Element_Array) is Count: Stream_Element_Offset := Item'Last + 1 - Item'First; Last: Stream_Element_Offset := Buf.Write_Offset + Count - 1; begin if Last > Buf.Buffer'Last then raise Ada.IO_Exceptions.End_Error; end if; Buf.Buffer(Buf.Write_Offset..Last) := Item; Buf.Write_Offset := Buf.Write_Offset + Count; end Write; --------------------------------------------------- Read from a Memory Buffer Object : -------------------------------------------------procedure Read(Buf: in out Memory_Buffer; Item: out Stream_Element_Array; Last: out Stream_Element_Offset) is Xfer_Count: Stream_Element_Offset := Item'Last + 1 - Item'First; Data_Count: Stream_Element_Offset := Buf.Write_Offset - Buf.Read_Offset; begin if Xfer_Count > Data_Count then Xfer_Count := Data_Count; end if; Item(1..Xfer_Count) :=
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/11.html (32 of 39) [7/20/2001 11:33:47 AM]
Buf.Buffer(Buf.Read_Offset..Buf.Read_Offset+Xfer_Count-1); Buf.Read_Offset := Buf.Read_Offset + Xfer_Count; Last := Item'First + Xfer_Count - 1; end Read; --------------------------------------------------- Rewind the Read offset in the Memory Buffer -------------------------------------------------procedure Rewind_Read(Buf: in out Memory_Buffer) is begin Buf.Read_Offset := Buf.Buffer'First; end Rewind_Read; --------------------------------------------------- Rewind the Write offset in the Memory Buffer -------------------------------------------------procedure Rewind_Write(Buf: in out Memory_Buffer) is begin Buf.Read_Offset := Buf.Buffer'First; -- Implies a Read offset rewind Buf.Write_Offset := Buf.Buffer'First; -- Rewind the write offset end Rewind_Write; end Memory_Stream;
-- $Id: main.adb,v 1.1 2000/11/26 05:00:18 wwg Exp $ -- (c) Warren W. Gay VE3WWG [email protected], [email protected] --- Protected under the GNU GPL License with Ada.Text_IO; use Ada.Text_IO; with Memory_Stream; use Memory_Stream; --------------------------------------------------- This is a demo main program, that makes use of -- our home-brewed Memory_Buffer_Stream. --- To demonstrate, a record of type my_rec is -- written to the stream with known values, and -- then is read back twice, into records T and U. --- Then the write offset is rewound, and a new -- float variable F is written, and then read -- back into float variable G. -------------------------------------------------procedure Main is type my_rec is record -- A demonstration record A: natural; B: integer; S: string(1..8); Z: float;
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/11.html (33 of 39) [7/20/2001 11:33:47 AM]
end record; Str: R: T: U: F: G: begin put_line("Demonstration has begun:"); Str := new Memory_Buffer_Stream(4096); -- Create in-memory buffer stream (4096 bytes) my_rec'write(Str,R); my_rec'read(Str,T); put_line("T.A put_line("T.B put_line("T.S put_line("T.Z :=" & natural'image(T.A)); :=" & integer'image(T.B)); := '" & T.S & "'"); := " & float'image(T.Z)); -- Write record R to stream -- Read stream back to record T -- Dump out T Stream_Access := null; my_rec := ( 23, -95, "oink my_rec := ( 0, 0, " my_rec := T; float := 29.99; float := 0.0; -- A Stream ", 1.414 ); -- An initialized record ", 0.0 ); -- For 1st read -- For 2nd read -- An initialized float -- For 3rd read
Rewind_Read(Str); my_rec'read(Str,U); put_line("U.A put_line("U.B put_line("U.S put_line("U.Z :=" & natural'image(U.A)); :=" & integer'image(U.B)); := '" & U.S & "'"); := " & float'image(U.Z));
-- Rewind the read pointer -- Now read into record U -- Dump out U
Rewind_Write(Str); float'write(Str,F); float'read(Str,G); put_line("G :=" & float'image(G)); Free(Str); put_line("Demonstration complete."); end Main;
-- Implies a read rewind also -- Write F to stream -- Read stream into G -- Report G for verification -- Delete stream
T.Z := 1.41400E+00 U.A := 23 U.B :=-95 U.S := 'oink ' U.Z := 1.41400E+00 G := 2.99900E+01 Demonstration complete.
11.13 Pragmas
Pragmas, sometimes called compiler directives, are statements that provide additional information to the compiler build a better executable. Pragmas never change the meaning of a program. The following are the predefined Ada 95 pragmas: Abort_Defer Ada_83 Ada_95 All_Calls_Remote Annotate Assert Asynchronous Atomic Attach_Handler C_Pass_By_Copy Comment Common_Object Complex_Representation Component_Alignment Controlled Convention CPP_Class CPP_Constructor CPP_Destructor CPP_Virtual CPP_Vtable Debug Discard_Names Elaborate Elaborate_All Elaborate_Body Eliminate Error_Monitoring Export Export_Function defer abouts over a block of statements* enforce Ada 83 conventions, even if compiler switches say otherwise* enforce Ada 95 conventions, even if compiler switches say otherwise* All subprograms in a RPC package spec are RPC callable add information for external tools* create an assertion* call to remote subprogram can complete before subprogram is done identifier must be read/written without interruption install a signal handler procedure when calling C functions, use pass by copy (not by reference) when able* same as Ident * for Fortran, create variables that share storage space* use gcc's complex number format (for speed)* indicate how record components should be stored* turn off garbage collection for a type (no effect in gnat) apply a convention to an identifier treat a record or tagged record as a C++ class* treat imported function as a C++ class constructor* treat imported function as a C++ class destructor* import a C++ virtual function* specify a virtual function table* specify a debugging procedure call* discard ASCII representation of identifiers, as used by 'img elaborate a certain package before this one elaborate all with'ed packages before this one elaborate a package's body immediate after it's spec indicate an identifier that is not used in a program, created by gnatelim * treat errors as warnings during a compile* export an identifier from your program so it can be used by other languages export an Ada function with additional information over pragma Export*
export an Ada tagged record with additional information over pragma Export* Export_Procedure export an Ada procedure with additional information over pragma Export* export an Ada side effect function with additional information over pragma Export_Valued_Procedure Export* Extend_System obsolete* Finalize_Storage_Only no finalize on library-level objects, primarily for gnat's internal use * Ident object file identification string (no effect with Linux) * Export_Object Import Import_Function Import_Object import an identifer from another language so it can be used in your program import a non-Ada function with additional information over pragma Import* import a non-Ada object with additional information over pragma Import* import a non-Ada procedure with additional information over pragma Import_Procedure Import* import a non-Ada side effect function with additional information over Import_Valued_Procedure pragma Import* Inline the indicated subprogram may be inlined. Inline_Always forces inter-unit inlining, regardless of compiler switches* Inline_Generic for compatibility with other Ada compilers* specify that an identifier's value must readable at the given point in the Inspection_Point program (for code validation) Interface_Name for compatibility with other Ada compilers* Interrupt_Handler declare a signal handler procedure Interrupt_Priority Linker_Alias Linker_Options Linker_Section List Locking_Policy Machine_Attribute No_Return No_Runtime Normalize_Scalars Optimize Pack Page Passive Polling Priority Preelaborate Propagate_Exceptions Psect_Object Specify the task/protected object's priority where blocking occurs select an alternative linker[?] for a package (or other llinkable unit)* pass a string of options to the linker the gcc linker section to use * list source code while being compiled Spcify how protected objects are locked and when blocking occurs specify GCC machine attributes* specify a procedure that is deliberately never returned from, to avoid compiler warnings* ensures no gnat run-time routines are use (e.g. for creating device drivers)* set scalars variables to illegal values whenever possible indicates how statements should be optimzed indicates that the type should be compressed as much as possible start a new page in a program listing for compatibility with other Ada compilers* if on, enables exception polling* Specify the priority of a task preelaborate the specified package specify imported subprogram that can handle Ada exceptions; used with zero cost handling * Same as common_object*
Pure Pure_Function Queuing_Policy Ravenscar Remote_Call_Interface Remote_Types Restricted_Run_Time Restrictions Reviewable Share_Generic Shared_Passive Source_File_Name Source_Reference Storage_Size Stream_Convert Subtitle Suppress Suppress_All Suppress_Initialization Task_Dispatching Task_Info Task_Storage Time_Slice Title Unchecked_Union Unimplemented_Unit Unsuppress Use_VADS_Size Volatile Volatile_Components Warnings Weak_External * - GNAT specific
specifies the package is pure specify a function without side-effects* How task/protected objects are sorted when queued enforce the Ravenscar real-time policies* Ensure a package can be callable by remote procedure calls used for communication between RPC partitions like Ravenscar, turns on a number of restrictions for real-time programming * Disable certain language features Provide a run-time profiling (like gprof) for compatibility with other Ada compilers* used for sharing global data with separate RPC partitions overrides normal Gnat file naming conventions* for use with gnatchop* amount of storage space for a task simplified way of creating streams I/O subprograms for a given type* for compatibility with other Ada compilers* turn off specific checks for common exceptions for compatibility with other Ada compilers* disable initialization of variables of a given type* specify how tasks sort dispatches (e.g. FIFO_Within_Priorities) specify information about a task* specify the guard area for a task* specify tasking time slice for main program [in Linux?]* for compatibility with other Ada compilers* treat a record as a C union type* for unfinished units, produces a compiler error if they are compiled* opposite of suppress* for older Ada code, 'size is equivalent to 'vads_size * value of variable may change unexpectedly array components may change unexpectedly turn compiler warnings on or off* specify an identifier that doesn't have to be resolved by the linker *
The use of these pragmas are covered in detail in the GNAT and Ada 95 reference manuals.
Variable is influnced outside of program Specify an absolute address for a for x'address use a pointer Position the identifier x on b byte for x'alignment use b boundaries for x'bit_order use o; Store record using bit order o Specify a octal numeric literal 8#value# Specify a hexadecimal numeric 16#value# literal Assemble an instruction asm( inst, in, out) pragma volatile
Ada contains a number of low-level features and libraries, such as the ability to add machine code to a program or to access a hardware register in memory. I'll mention a few of these features here. If you need to specify a specific number of bits, say to access a hardware register, use the for statement. type Reg is new integer; for Reg'size use 4; -- register is 4 bits type RegPtr is access all Reg; You can refer an access type to a particular address using for var'address clause. Together with 'size , you can theoretically refer to an bit in the computer's memory. However, on Linux the address refers to a location in your address space and doesn't refer to a real physical location. var'alignment will align storage to a particular byte boundary. var'bit_order can specify a different bit order for a record than the default for your machine. The bit orders are defined in the System package. (Gnat 3.12 doesn't fully support bit orders.) for Reg'address use 16#2EF#; -- hex address 2EF in your memory space for Reg'alignment use 2; -- align to 16-bit boundaries for myRecord'bit_order use system.low_order_first; -- low bits to high machine If the register value can change independently of the Ada program (usually true), we need to mark the pointer with pragma volatile to make sure the compiler will make no assumptions while optimizing. pragma volatile( RegPtr); Ada will do bit-wise logic operations, but it will only do them on packed arrays of booleans or modular types. You can't do bit-wise operations on integers, for example. The bit-wise operators are and, or, xor and not. type byte is array(1..8) of boolean; pragma pack( byte ); b1, b2, b3 : byte; ... b3 := b1 and b2; If you need octal or hexadecimal numbers, Ada denotes these by using a leading base designation between 2 and 16, with the value delimited by number signs. Hex := 16#0FE9#; Bin := 2#01101010#; The System.Machine_Code package can embed assembly language instructions in an Ada program. Since Gnat is based on
Gcc, it uses Gcc's inlining features. If you've inlined assembly code in a C program, you'll find Gnat's capabilities virtually identical. Assembly code is embedded using the Asm. Asm( "nop" ); -- do nothing A tutorial by Jerry van Dijk is available from AdaPower.com. Large sections of assembly code should be linked in separately.
<--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.
Description Character operations Ada string operations Bounded strings and operations Unbounded strings and operations Just case conversions 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
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/12.html (1 of 35) [7/20/2001 11:34:25 AM]
-- 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.Character.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.Character.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 );
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/12.html (3 of 35) [7/20/2001 11:34:25 AM]
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.Character.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 & "'" );
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/12.html (5 of 35) [7/20/2001 11:34:26 AM]
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 );
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: q Is_Open is true if the file has been opened q End_Of_File is true if you have read the last record in the file. (This is unavailable in out mode.) q Name is the path of the file q Mode is the current file mode q Size is the number of rows in the file q Index is the number of the current row 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 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. to row 7
Alternately, you can write your own database package using a the Linux kernel. seqio, a sequential IO package, is developed in chapter 16.
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( put_line( put_line( put_line( new_line; "Default "Default "Default "Default currency symbol is " & Default_Currency ); fill character is '" & Default_Fill & "'" ); separator character is '" & Default_Separator & "'" ); radix mark is '" & Default_Radix_Mark & "'" );
ShowValues( "99999.99" ); New_Line; ShowValues( "ZZZZ9.99" ); New_Line; ShowValues( "****9.99" ); New_Line; ShowValues( "-$$$9.99" );
This is an example of Formatted Output -------------------------------------Default Default Default Default currency symbol is $ fill character is ' ' separator character is ',' 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. q Currency - the currency string to use q Fill - the fill character to use q Separator - the separator character to use q Radix_Mark - the radix mark to use There is also a Wide_Text_IO.Editing for wide string.
St. Jean Baptiste Day (Quebec/Canada), last Saturday in June [KB?]. Parents' Day, fourth Sunday in July (36 USC Sec. 142c). Grandparents' Day, Sunday after Labor Day (36 USC Sec. 142b). Columbus Day (U.S., traditional), October 12. United Nations Day (U.S.), October 24. Halloween, October 31. Boxing Day, December 26.
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;
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 );
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/12.html (20 of 35) [7/20/2001 11:34:27 AM]
4 and day
23
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]
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 -file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/12.html (22 of 35) [7/20/2001 11:34:27 AM]
------
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]
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 New_Line; Put_Line( "The table begins as:" ShowTable; IntSort.Sort( it'last ); -- sort New_Line; Put_Line( "The sorted table is:" ShowTable; end bubble1; bubble sorting an integer table" ); ); elements 1 to top of it array );
This is an example of bubble sorting an integer table The table begins as:
0 1 2 3 4 5
= = = = = =
0 13 4 5 16 8
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 1 2 3 4 5 = = = = = = 0 13 4 5 16 8
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 beforeMS Word bug?]
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.
basic math for short_float type basic math for Float Ada.Numerics.Elementary_Functions float type basic math for Long_Float Ada.Numerics.Long_Elementary_Functions long_float type basic math for Long_Long_Float Ada.Numerics.Long_Log_Elementary_Functions long_long_float type Short_Float Ada.Numerics.Short_Elementary_Functions 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: Function Sqrt( x ) Log( x ) Log( x, b ) Exp( x ) ** Sin( x ) Sin( x, c ) Cos( x ) Cos( x, c ) Tan( x ) Tan( x, c ) Description Square Root Natural Logarithm (ln in some other languages) Logarithm to base b Raise e by power x Power operator Sine for x radians Sine for x where cycle range is c (eg. 360 for degrees) Cosine for x radians Cosine for x where cycle range is c Tangent for x radians 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 The The The 50% to the power 3.0 is 6.40000E+01 sine of 0.4 radians is 3.89418E-01 cosine of 180 degrees is 0.00000E+00 square root of 81 is 9.00000E+00 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: Function Rotate_Left Rotate_Right Shift_Left Shift_Right Shift_Right_Arithmetic Description Rotate bits in integer types leftward Rotate bits in integer types rightward Shift bits in integer types leftward Shift bits in integer types rightward Arithmetic 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
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-->
13 Linux Introduction
<--Last Chapter Table of Contents Next Chapter-->
UID 0
PID 579
PPID 1
C PRI 0 60
NI ADDR 0 -
TTY tty1
100 S 0 589 579 0 69 0 457 wait4 tty1 00:00:00 bash 100 R 0 624 589 0 70 0 634 tty1 00:00:00 ps In this case, there are three processes in one family. The ps process (PPID 589) has a bash shell as its parent (PID 589). Likewise, the parent of the bash shell is the login command. Process Groups For complex programs with many processes, Linux can organize processes into process groups. The ps lfw options (long, full, wide) will show the PID, the PPID and a simulated graph of groups of related processes. $ ps lfw F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND 100 0 579 1 0 0 2196 1148 wait4 S tty1 0:00 login -- root 100 0 589 579 15 0 1828 1060 wait4 S tty1 0:00 -bash 100 0 689 589 17 0 2484 824 R tty1 0:00 \_ ps lfw Here, it shows that "ps lfw" is a related to the bash shell process, but the bash shell is not related to the login command. The PPID number shows you which process started another, but not which group it belongs to. Because the bash shell process and the ps command are both members of the same group, if you were to kill the bash process, Linux would automatically kill the ps command as well because they are in the same group. With this arrangment, if the bash shell crashed, Linux can return control to the login program and allow you to log in again. In the same way, if you have many processes in your program, you can group your processes so if they crash unexpectedly, the main program can continue running and take emergency actions. Process groups also provides an easy way to stop several processes at once. For example, a web server could put all the child processes into one group to make it easy to stop them when the server is being shut down. Stopping Processes Normally, a process stops when it reaches the end of the instructions it needs to execute. You can stop a runaway program (or process) with the kill command, giving the command the PID number returned by the ps command. kill 624 #killing ps Below we'll discuss stopping processes from inside a program. A process that runs continually, performing some kind of regular system functions, is called a daemon (pronounced "day-mon", a variation on the word "demon" ). It's referred to as a daemon as if a little evil creature was running around doing work on its own. If you have a web server running, for example, you could refer to it's process as the web daemon.
Put_Line( "Spool file initialized" ); end if; end CreateFile; -- the text file to print SpoolPath : constant string := "/tmp/spool.txt"; SpoolFile : File_Type; begin -- To open an out_file in text_io, it must exist. -- Create file will create a new spool file. -- Set_Output will redirect all output to the -- spool file. CreateFile( SpoolPath ); Open( SpoolFile, out_file, SpoolPath); Set_Output( SpoolFile ); -- write the report to printer -- Linux normally will not eject a page when -- printing is done, so we'll use New_Page. Put_Line( "Sales Report" ); Put_Line( "------------" ); New_Line; Put_Line( "Sales were good" ); New_Page; -- Now, restore output to the screen, close -- the file and queue the file for printing -- using lpr. Set_Output( Standard_Output ); Close( SpoolFile ); PrintFile( SpoolPath ); Put_Line( "Program done...check the printer" ); end printer; Although this program will work for simple applications, another improved program to print using pipes is discussed below. The system function is convenient but it has a couple of important drawback: q It's slow: it always opens a shell even if you're only using the shell execute some other program q It's a security risk: the shell could have aliases defined and what you thought you were running may not be what actually runs. GNAT's OsLib provides a subprogram like system called spawn. It executes a Linux program without starting a shell first. Spawn requires a bit of setup to use. You have to define an array of access type arguments for the command. Once you invoke spawn, it returns a boolean value indicating whether the spawn succeeded or failed. The following excerpt is from an example program in the OSLib section covered below.
Arguments : Argument_List( 1..1 ); -- an argument list for 1 argument Ls : constant string := "/bin/ls"; -- the program we want to run WasSpawned: boolean; RootDir : aliased string := "/"; begin Arguments(1) := RootDir'unchecked_access; -- unchecked to avoid useless (in this case) accessibility warning Spawn( Ls, Arguments, WasSpawned ); if WasSpawned then New_Line; Put_Line( "End of ls output -- Spawned worked" ); else Put_Line( "Spawn failed"); end if; This fragment runs the ls command, prints the results on the screen, and then displays the success of the command on the screen. Notice there are differences between spawn and system: q spawn only gives you a boolean result q spawn does not start a shell, so it is more secure q because there is no shell, you can't run built-in shell commands nor can you redirect output using ">" If spawn is too limited, many UNIX and Linux programming books tell you how to create your own spawn style subprograms using fork, wait and the exec family of standard C commands. Here is an example of a short C function that executes a program and puts the results (standard output and standard error) to a text file of your choosing, allowing up to three parameters for the command. int CRunIt( char * path, char * outfile, char * param1, char * param2, char * param3 ) { pid_t child; int fd0, fd1, fd2; int status; int i; if ( !(child = fork()) ) { /* Redirect stdin, out, err */ for (i=0; i< FOPEN_MAX; ++i ) close( i ); fd0 = open( "/dev/null", O_RDONLY ); if (fd0 < 0) exit( 110 ); fd1 = open( outfile, O_WRONLY | O_CREAT | O_TRUNC ); if (fd1 < 0) exit( 111 ); fd2 = dup( 1 ); if (param1[0]=='\0') { execlp( path, path, NULL ); } else if (param2[0]=='\0') { execlp( path, path, param1, NULL ); } else if (param3[0]=='\0') {
execlp( path, path, param1, param2, NULL ); } else { execlp( path, path, param1, param2, param3, NULL ); } /* if we got here, file probably wasn't found */ exit( errno ); } wait( &status ); if ( WIFEXITED( status ) != 0 ) status = WEXITSTATUS( status ); return status; }
It is possible to rewrite this subprogram into Ada, but it's easier in C because of the constants, macros and execlp takes a variable number of parameters. This function returns some special exit status values: 110 if /dev/null couldn't be opened for standard input, and 111 if the output file you specified couldn't be opened. function CRunIt( cmd, outfile, parm1, parm2, parm3 : string ) return integer; pragma Import( C, CrunIt, "CRunIt" ); .. Result := CrunIt( "/bin/ls" & ASCII.NUL, -- executable to run "/tmp/ls.out" & ASCII.NUL, -- where output should go "" & ASCII.NUL, -- parameter 1 (none) "" & ASCII.NUL, -- parameter 2 (none) "" & ASCII.NUL ); -- parameter 3 (none) An important part of running commands like this is deciding on temp file names that won't be used if two copies of the program are run at the same time. There's two ways to do this: q Use the standard C function tmpfile (discussed below) q Find the process number of your program with the standard C function getpid and add it to your filename If you are interested in accessing Linux more directly, read the next section.
To understand the differences between these layers, consider the problem of allocating dynamic memory. Usually you allocate memory with the Ada new statement. Where does new get its memory? It uses the standard C library's malloc function. But where does malloc get its memory? It gets it from the Linux kernel call mmap (memory map). The most direct way to get memory, and the method that gives you the most control, is mmap. On the other hand, the easiest way would be to use new and to let Ada allocate the memory and manage the details for you. It's the same with multithreading and sequential files. Multitasking is based on LinuxThreads, a part of the standard C library, which in turn is based on the kernel's clone function. Sequential files are based on the standard C library's stream functions, which in turn are implemented using the kernel's file handling functions. Figure: Ways of Doing the Same Thing
As you move down the list from the standard Ada libraries to the Linux kernel, your program becomes more platform specific. A program that uses new will run on any operating system that can run Ada 95. If you use malloc, your program will run on any operating system that uses has the standard UNIX C libraries available. If you use mmap, your program will run on any Linux computernot just Intel-based Linux, but any flavour of Linux, include Sun UltraLinux or Apple Mklinux. Remember that Linux is a portable operating system: all versions of Linux will have mmap available.
The kernel calls are documented in the online manual pages, but these are sometimes out of date due to the ever-changing nature of Linux.
13.5.2 Devices
The /dev directory defines device files. These files let you work with devices connected to your computer by using standard file operations. Devices can include hard drives, sound cards and the system console (the text display). Devices are recognized by their names: q /dev/hd devices: IDE hard drives, CD-ROMs, etc. q /dev/sd devices: SCSI hard drives, CD-ROMs, etc. q /dev/fd devices: floppy drives q /dev/console device: the text display q /dev/tty devices: the pseudo terminals. /dev/tty is an alias for the current tty terminal or ttyS serial port q /dev/ttyS devices: the serial ports q /dev/lp devices: the parallel ports (lp stands for line printer) In addition, most distributions define the following links: q dev/cdrom: an alias for the main CD-ROM device q /dev/modem: an alias for the the port/device the modem is connected to q dev/mouse: an alias for the port/device the mouse is connected to q /dev/fd0: an alias for the first floppy drive There are more device files than there are devices on a computer. For example, there may be 32 serial port device files defined, but that doesn't mean that there are actually 32 serial port on the computer. You will have to open the device and check for an error if it does not exist. For example, opening /dev/lp1 and writing a file to it writes the file as raw data to the first parallel port printer. Information on how these devices work is usually found in the How-To's and other system documentation. Special functions specific to a device are programmed with the ioctl() function. For example, you'd use ioctl() on /dev/dsp to set the sound volume on your sound card. The documentation for device files are often difficult to find. Sometimes documentation is contained in the kernel documentation (the /usr/doc/kernel.. directory) or in the kernel C header files (the /usr/src/linux/include/... directories). A list of some of the ioctl operations are listed in an appendix.
-- DEVICES --- This section deals with device files, in particular, -- the cdrom device DevCDROM : constant string := "/dev/cdrom"; -- path to the CDROM device, usually /dev/cdrom type ioctlID is type aFileID is -- Define these -- A ioctlID is new integer; new integer; as separate types for error checking. never the same as a FileID.
type byte is new integer range 0..255; for byte'size use 8; CDROMPLAYTRKIND : constant ioctlID := 16#5304#; CDROMSTOP : constant ioctlID := 16#5307#; CDROMSTART : constant ioctlID := 16#5308#; -- various CDROM ioctl functions as mentioned in the -- CDROM documentation in /usr/doc/kernel... type aDummyParam is new integer; -- define this as a separate type to make sure nothing -- important is used as a third parameter to ioctl_noparam -- a version of ioctl for functions that don't -- use a third parameter procedure ioctl_noparam( result : out integer; fid : aFileID; id : ioctlID; ignored : in out aDummyParam ); pragma import( C, ioctl_noparam, "ioctl" ); pragma import_valued_procedure( ioctl_noparam ); type cdrom_ti is record start_track, start_index : byte; end_track, end_index : byte; end record; -- from /usr/src/linux/include/linux/cdrom.h -- PLAYTRKIND ioctl function uses cdrom_ti record procedure ioctl_playtrkind( result : out integer; fid : aFileID; id : ioctlID; info : in out cdrom_ti ); pragma import( C, ioctl_playtrkind, "ioctl" ); pragma import_valued_procedure( ioctl_playtrkind ); -- KERNEL CALLS --- Calls to the Linux kernel (besides ioctl).
procedure open( id : out aFileID; path : string; flags : integer ); pragma import( C, open, "open"); pragma import_valued_procedure( open ); -- open is a kernel call to open a file procedure close( result : out integer; id : aFileID ); pragma import( C, close, "close"); pragma import_valued_procedure( close ); -- close is a kernel call to close a file -- C LIBRARY CALLS --- Calls to the standard Linux C libraries procedure perror( prefixstr : string ); pragma import( C, perror, "perror"); -- perror is a standard C library call to print -- the last error message from a kernel call or the -- standard C libraries on the screen cd : aFileID; playinfo : cdrom_ti; dummy : ADummyParam; ioctl_result : integer; close_result : integer; ch : character; begin Put_Line( "This program plays an audio CD in your CDROM drive" ); New_Line; -- open the /dev/cdrom file so we can control the CDROM drive -- using ioctl Put_Line( "Openning " & DevCDROM & "..." ); Open( cd, DevCDROM, 0 ); if cd < 0 then perror( "Error openning CDROM drive" ); end if; -- start the CDROM drive Put_Line( "Spinning up cdrom..." ); ioctl_noparam( ioctl_result, cd, CDROMSTART, dummy ); if ioctl_result < 0 then perror( "Error spinning up the CDROM drive" ); end if; -- display menu New_Line; Put_Line( "1 = Play, 2 = Quit" ); New_Line;
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/13.html (10 of 15) [7/20/2001 11:35:52 AM]
-- Main loop. Repeat until 2 is selected. loop Put( Get( case when
"Select a function (1-2): " ); ch ); ch is '1' => playinfo.start_track := 1; -- first track playinfo.start_index := 0; -- no effect playinfo.end_track := 9; -- final track (inclusive) playinfo.end_index := 0; -- no effect ioctl_playtrkind( ioctl_result, cd, CDROMPLAYTRKIND, playinfo ); when '2' => ioctl_noparam( ioctl_result, cd, CDROMSTOP, dummy ); exit; when others => Put_Line( "Pardon?" ); end case;
if ioctl_result < 0 then perror( "Error controlling CDROM drive" ); end if; end loop; -- Close the CDROM device Close( close_result, cd ); if close_result < 0 then perror( "Error controlling CDROM drive" ); end if; end audiocd;
Put_Line( Standard_Error, "This message is on standard error" ); Put_Line( "This message is on standard output" ); New_Line; -- you can use Set_Output to send all Put_Line's to Standard_Error Set_Output( Standard_Error ); Put_Line( "This is also on standard error"); Set_Output( Standard_Output ); Put_Line( "But this is on standard output"); end stderr;
This is an example of writing error messages to stderr This message is on standard error This message is on standard output This is also on standard error But this is on standard output Everything looks normal until you redirect the output of the program. This is the result when the standard output is redirected to a file called "out.txt". The error messages aren't redirected. [root@armitage trials]# stderr > out.txt This message is on standard error This is also on standard error
static library, just include it's name with the gnatlink command (or gnatmake on simple projects). By default, gnat's own libgnat library is always static, because of concerns over multithreading. For example, if you install and compile the official JPEG library sources, a static library file is created named libjpeg.a. To link this library into your program, you'd include -ljpeg when linking. Note that you don't use the entire name of the file: gnat assumes there is a beginning "lib" and ending ".a". One use for static libraries is to create a library for others to use. As a small example, suppose you have a package p and you want to create a static library to link into a program t. You have to put the library's object file (p.o) into a static library (eg. libp.a) and change p.ali to read-only so gnat knows there is no object file included with p. Then include -lp when making or linking. armitage:/home/ken/ada/test# ls p.ads p.adb p.ali p.o t.adb armitage:/home/ken/ada/test# ar cr libp.a p.o armitage:/home/ken/ada/test# rm p.o armitage:/home/ken/ada/test# chmod -w p.ali armitage:/home/ken/ada/test# ls libp.a p.ads p.adb p.ali t.adb armitage:/home/ken/ada/test# ar t libp.a p.o armitage:/home/ken/ada/test# gnatmake t -lp (Strickly speaking, -lp should use gnatmake's linker options option, but this works.) If the library is in a place other than your current directory, you'll need to use the -L option to indicate the directory to check. A shared library ( or DLL, dynamic link library) is a library that is loaded into memory and shared between programs. It's not actually saved as part of the executable. All Linux shared libraries end in .so.a ("shared object archive??"). They are loaded when a program is executed. [UNTESTED!] To create a shared library, compile the source code with the -fPIC (position independent code) option and link using -shared. You also need to include -W1,-soname,nameofobject -- is this necessary under gnat if you use gnatlink? I think it probably is. [see Linux Application programming, pg 72]
if you forget the fPIC switch, your shared library will still load, but you won't be able to share the code between applications that use the library.
-fpic will also work on Intel processors because there is no maximum imposed for the global offset table, but it may not work on other processors: the fPIC switch is the preferred method. The shared libraries are usually stored in standard Linux directories, like /lib or /usr/lib. Once you copy a shared library into one of these directories, you have to run ldconfig to register the new shared library, otherwise Linux will not load it.
To load a shared library from the current directory instead of the standard Linux directories, use -L. (where period is the current directory). Shared libraries have the advantage of making executable's smaller, but they are slower to load and execute and use up more memory than static libraries. The also have the advantage of being able to be ugraded separately from your program, provided you don't change the format of any of the subprograms. Too many shared libraries mean that you have small files scattered throughout the lib directories, making your program harder to maintain. Generally speaking, only subprograms that will be shared between programs should be shared libraries, and you should combine small, related libraries into one shared library. For example, all the standard C libraries are compiled into one shared library on Linux: libc.so.a. [from usenet] makedll.bat gcc -c beep.adb gnatbind -n beep.ali gnatlink beep.ali -o beep.jnk -mdll -Wl,--base-file,beep.base dlltool --dllname beep.dll --def beep.def --base-file beep.base --output-exp beep.exp --output-lib libbeep.a gnatbind -n beep.ali gnatlink beep.ali -o beep.jnk beep.exp -mdll -Wl,--base-file,beep.base dlltool --dllname beep.dll --def beep.def --base-file beep.base --output-exp beep.exp --output-lib libbeep.a gnatbind -n beep.ali gnatlink beep.ali beep.exp -o beep.dll -mdll This is a def file I am using. beep.def EXPORTS DllGetClassObject=DllGetClassObject@12 @2 DllCanUnloadNow=DllCanUnloadNow@0@3 DllRegisterServer=DllRegisterServer@0@4 DllUnregisterServer=DllUnregisterServer@0@5
NOTE: Since the exported functions or STDCALL, I need to provide the number of bytes used for parameters. If you were using standard pragma C stuff it would be: MYFunction@XXXX where XXXX is what ordinal in the DLL - BTW you can leave this out all together if you don't need it and just put the name of the function on the line. [end] Gnat has an option called -static which will link all shared libraries into your executable as if they were static libraries. This makes your executable completely self-contained, but may violate GPL licensing restrictions on certain libraries. Gnat comes with one shared library, libgnat.a. If you link a gnat program without the -static option, you have to copy this file into a standard library directory (e.g. /lib) and run ldconfig so that Linux will be able to find the gnat library when executing your programs. Gnat always automatically links in the library: you never have to type "-lgnat" explicitly when linking.
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/13.html (14 of 15) [7/20/2001 11:35:52 AM]
14 Linux Programming
<--Last Chapter Table of Contents Next Chapter-->
Fun Fact:The original Apple Macintosh operating system was written in Pascal, the ancestor language of Ada.
The gnat OS library, gnat.os_lib, provides common UNIX operations independent of what flavour of UNIX gnat is running on. It provides an extensive set of file utilities as well as the ability to run blocked and non-blocked child processes.The price for this low-level OS support is the need to use a lot of addresses, 'access and C strings.
there is also a thin binding available for basic C stream functions, described below. with text_io, gnat.os_lib; use text_io, gnat.os_lib; procedure ostest is fd : File_Descriptor; FilePath : constant string := "testfile.xxx" & ASCII.NUL; -- for write test FirstLine : constant string := "This is the first line in the file"; AmountWritten : integer; -- for time stamp test ts : OS_Time; Year : Year_Type; Month : Month_Type;
Day : Day_Type; Hour : Hour_Type; Minute : Minute_Type; Second : Second_Type; -- for location test sp : String_Access; -- for delete test WasDeleted : boolean; -- for spawn test Arguments : Argument_List( 1..1 ); Ls : constant string := "/bin/ls"; WasSpawned: boolean; RootDir : aliased string := "/"; begin Put_Line( "This is an example of the Gnat's OS library:" ); New_Line; Put_Line( "Creating a new file..." ); fd := create_file( FilePath'address, Binary ); if fd = invalid_fd then Put_Line( "Unable to create " & FilePath ); else Put_Line( "Created " & FilePath ); end if; New_Line; Put_Line( "Getting the timestamp on the file..." ); ts := File_Time_Stamp( fd ); GM_Split( ts, Year, Month, Day, Hour, Minute, Second ); Put_Line( "The time stamp is" & Year'img & "/" & Month'img & "/" & Day'img & Hour'img & ":" & Minute'img & ":" & Second'img ); New_Line; Put_Line( "Writing to the file..." ); Put_Line( FirstLine ); AmountWritten := Write( fd, FirstLine'Address, FirstLine'Length ); Put_Line( "Wrote" & AmountWritten'img & " bytes" ); Put_Line( "The file length is" & File_Length( fd )'img ); New_Line; Close( fd );
Put_Line( "Closed the file" ); New_Line; Put_Line( "Locating the file we just made..." ); sp := Locate_Regular_File( File_Name => FilePath, Path => GetEnv( "PATH" ).all ); Put_Line( "The file is '" & sp.all & "'" ); New_Line; Put_Line( "Deleting the file..." ); Delete_File( FilePath'address, WasDeleted ); if WasDeleted then Put_Line( "File was deleted" ); else Put_Line( "File was not deleted" ); end if; New_Line; Put_Line( "Running ls / ..." ); New_Line; Arguments(1) := RootDir'unchecked_access; -- unchecked to avoid unless accessibility warning Spawn( Ls, Arguments, WasSpawned ); if WasSpawned then New_Line; Put_Line( "End of ls output - Spawned worked" ); else Put_Line( "Spawn failed" ); end if; New_Line;
end ostest;
This is an example of the Gnat's OS library: Creating a new file... Created testfile.xxx Getting the timestamp on the file... The time stamp is 1998/ 12/ 18 23: 42: 24 Writing to the file...
This is the first line in the file Wrote 34 bytes The file length is 34 Closed the file Locating the file we just made... The file is './testfile.xxx' Deleting the file... File was deleted Running ls / ... STARTUP System.map System.old bin boot cdrom dev dosc etc fd home lib lost+found mnt opt proc root sbin tmp usr var vmlinuz vmlinuz.old End of ls output - Spawned worked
q q q
Make sure that the filename endings are the right ones for gnat. Different compilers use different conventions. Compile the binding package(s). Install the library the binding is for (if necessary) Include -lname on the link line, where name is the library. Remember that the order of the -l's is important.
A complete list of Linux signals is listed in an appendix. The package Ada.Interrupt.Names defines the names of these signals for you. Signal Handlers are protected type procedures with no parameters. The body of the procedure performs whatever actions you want to do when you receive a signal. For example, to catch the SIGTERM signal, the signal that indicates that the program has been killed with the "kill" shell command, you can write a handler like this: protectedbodySignalHandler is procedure HandleSIGTERM is -- normal kill signal handler begin Put_Line( "Ouch! I've been killed!" ); -- perform any other cleanup here end HandleSIGTERM; end SignalHandler;
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/14.html (5 of 15) [7/20/2001 11:36:19 AM]
To put the handler in place permanently, use pragma Attach_Handler. pragma Attach_Handler( HandleSIGTERM, SIGTERM ); Now whenever your program receives a SIGTERM signal, your handler will automatically run. If you don't want to install a permanent handler, a handler can be installed or changed while the program is running. To indicate that a procedure is an interrupt handler that can be installed at a later time, use pragma Interrupt_Handler. pragma Interrupt_Handler( HandleSIGTERM ); Gnat automatically handles one signal for you: SIGINT, the interrupt signal. This is the signal that is sent to your program when control-c is pressed. If you want to handle control-c presses yourself, you have to use pragma Unreserve_All_Interrupts. Despite it's long name, this pragma simply tells Gnat to ignore SIGINT's. Certain signals can never be caught. SIGUNUSED, the unused signal, can't be caught for obvious reasons. Some signals are used by the multithreading software and are not available for use in applications. In particular, if you are running native Linux threads, you can't catch SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGTRAP, SIGABRT, SIGINT, SIGVTALRM, SIGUNUSED, SIGSTOP, or SIGKILL. On 2.0 kernels or older, native Linux threads use SIGUSR1 and SIGSUR2 and are not available. If you're running FSU threads, then SIGALRM is also not available. Ada.Interrupts also contains several subprograms for signal handling. Is_Reserved is true if a particular signal is uncatchable. q Is_Attached is true if a particular signal has a handler attached. q Current_Handler returns a pointer to the handler for a particular interrupt. q Exchange_Handler will put a new handler in place and return a pointer to the previous handler. q Detach_Handler will uninstall a handler The following package sets up three signal handlers, which display a message at set the EMERGENCY_SHUTDOWN variable to true. The demo program demonstrates some of the Ada.Interrupts subprograms and enters into a slow loop. The main program was killed with the "kill SIGPWR" shell command, simulating a power failure signal.
q
with Ada.Interrupts.Names; use Ada.Interrupts, Ada.Interrupts.Names; package SigHand is -- Package to handle basic Linux signals pragma Unreserve_All_Interrupts; -- Gnat will no longer handle SIGINT for us EMERGENCY_SHUTDOWN : boolean := false; -- set in the event of a signal to shut down the program -- SignalHandler will handle the signals independently -- from the main program using multithreading protected SignalHandler is
procedure HandleControlC; pragma Attach_Handler( HandleControlC, SIGINT ); -- SIGINT (Control-C) signals will be intercepted by -- HandleControlC procedure HandleKill; pragma Attach_Handler( HandleKill, SIGTERM ); -- SIGTERM (kill command) signals will be intercepted by -- HandleKill procedure HandlePowerFailure; pragma Attach_Handler( HandlePowerFailure, SIGPWR ); -- SIGPWR (power failure signal) intercepted by -- HandlePowerFailure end SignalHandler; end SigHand; with Ada.Text_IO; use Ada.Text_IO; package body SigHand is -- Package to handle basic Linux signals protected body SignalHandler is -- This protected type contains all our signal handlers procedure HandleControlC is -- Control-C signal handler begin if EMERGENCY_SHUTDOWN then Put_Line( "HandleControlC: The program is already shutting down" ); else Put_Line( "HandleControlC: Control-C was pressed, shutting down" ); end if; EMERGENCY_SHUTDOWN := true; end HandleControlC; procedure HandleKill is -- normal kill signal handler begin if EMERGENCY_SHUTDOWN then Put_Line( "HandleKill: The program is already shutting down" ); else
Put_Line( "HandleKill: Program is shutting down" ); end if; EMERGENCY_SHUTDOWN := TRUE; end HandleKill; procedure HandlePowerFailure is -- power failure handler begin if EMERGENCY_SHUTDOWN then Put_Line( "HandlePowerFailure: The program is already shutting down" ); else Put_Line( "HandlePowerFailure: Program is shutting down" ); end if; EMERGENCY_SHUTDOWN := TRUE; end HandlePowerFailure; end SignalHandler; end SigHand; with Ada.Text_IO, SigHand, Ada.Interrupts.Names; use Ada.Text_IO, SigHand, Ada.Interrupts, Ada.Interrupts.Names; procedure SigDemo is Handler : Parameterless_Handler; Counter : integer := 2; begin Put_Line( "This program demonstrates signal handling." ); Put_Line( "To stop this program, type Control-C or " ); Put_Line( "kill it with the shell kill command." ); New_Line; -- Is_Reserved example if Is_Reserved( SIGTERM ) then Put_Line( "The SIGTERM handler is reserved" ); else Put_Line( "The SIGTERM handler isn't reserved" ); end if; -- Is_Reserved example if Is_Attached( SIGINT ) then Put_Line( "There is a SIGINT handler installed" );
else Put_Line( "There is no SIGINT handler installed" ); end if; -- Current_Handler example Put_Line( "Testing SIGTERM handler..." ); Handler := Current_Handler( SIGTERM ); -- Current_Handler gives a callback to the handler Handler.all; -- run the handler callback if EMERGENCY_SHUTDOWN then Put_Line( "Handler works" ); else Put_Line( "Handler doesn't work" ); end if; -- test complete: reset emergency shutdown flag EMERGENCY_SHUTDOWN := false; -- a long loop New_Line; Put_Line( "The number is " & Counter'img ); loop exit when EMERGENCY_SHUTDOWN; Counter := Counter * 2; Put_Line( "Doubling, the number is " & Counter'img ); delay 1.0; end loop; Put_Line( "The program has shut down" ); end SigDemo; This program demonstrates signal handling. To stop this program, type Control-C or kill it with the shell kill command. The SIGTERM handler isn't reserved There is a SIGINT handler installed Testing SIGTERM handler... HandleKill: Program is shutting down Handler works The number is 2 Doubling, the number is 4
Doubling, the number is 8 Doubling, the number is 16 Doubling, the number is 32 Doubling, the number is 64 Doubling, the number is 128 Doubling, the number is 256 Doubling, the number is 512 HandlePowerFailure: Program is shutting down The program has shut down
Ada interacts with the outside world through the standard Ada package Ada.Command_Line. Suppose you have an Ada program called "myprog" and a user types in the following command: "myprog -v sally.com".
"myprog" is the name of the command. q "-v" and "sally.com" are arguments to the command. Command_Name returns the name of the command. If the program was run from a shell, it returns the name as typed in by the user. In the above example, Command_Name returns "myprog". ArgumentCount returns the number of arguments, not including the name of the program. The shell determines how arguments are grouped together, but typically each argument is separated by a space. In the above example, there are two arguments, "-v" and "sally".
q
Argument returns an argument. In the above example, argument( 1 ) returns "-v". Set_Exit_Status gives Ada the error code you want to return when the program is finished running. Ada defines two Exit_Status values, Success and Failure . Since Exit_Status is just an integer, you can return other values. Zero indicates that the program ran without error, non-zero values indicate an error. The predefined values of Success and Failure are 0 and 1. Properly flagging errors is important for shell programming. For example, you have to return the proper exit status for "myprog && echo 'all is well'" to work properly. You can retrieve the exit status of the last command using "$?". For example: #!/bin/bash
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/14.html (10 of 15) [7/20/2001 11:36:19 AM]
myprog -v sally if [ $? -eq 0 ] ; then echo "There were no errors" else echo "The program returned error code = $?" fi See the example program in the next section for an example using this package.
Description The number of environment variables The name value of the nth variable
C Equivalent ? getenv(n)
The Environment_Count function returns the number of environment variables. The Environment_Value function returns the name and value of a variable, separated by an equals sign. For example, Environment_Value( 5 ) returns the name and value of the fifth environment variable. The following program is an example of Ada.Command_Line and Ada.Command_Line.Environment. The results assume that you started the program by typing "cmdtest -v". with text_io, Ada.Command_Line.Environment; use text_io, Ada.Command_Line, Ada.Command_Line.Environment; procedure cmdtest is begin Put_Line( "This is an example of Ada.Command_Line" ); New_Line; Put_Line( "The command to invoke this example was '" & Command_Name & "'" ); Put_Line( "There is/are" & Argument_Count'img & " command line arguments" ); if Argument_Count > 0 then Put_Line( "The first argument is '" & Argument(1) & "'" ); end if; New_Line;
Put_Line( "There is/are" & Environment_Count'img & " environment variables." ); Put_Line( "The first environment variable is '" & Environment_Value( 1 ) & "'" ); Set_Exit_Status( Success ); end cmdtest;
This is an example of Ada.Command_Line The command to invoke this example was 'cmdtest' There is/are 1 command line arguments The first argument is '-v' There is/are 24 environment variables. The first environment variable is 'LESSOPEN=|lesspipe.sh %s' Environment variables can be removed using the Gnat Ada.Command_Line.Remove package.
Change_Dir( "work" ); Get_Current_Dir( dir, len ); Put( "Moving down to 'work', the current working directory is " ); Put_Line( dir(1..len) ); end dir;
The current working directory is /home/kburtch/work/ Moving up, the current working directory is /home/kburtch/ Moving down to 'work', the current working directory is /home/kburtch/work/ For viewing directories, the package opens directories like a Text_IO file. Dir_Type is a limited private directory type corresponds to a file_type in Text_IO. Directories can only be read. q Open - Open a directory for reading q Close - Close a directory q Read - Read a directory entry. When a null string is returned, there are no more entries q Is_Open - True if the directory is open q Read_Is_Thread_Safe - True if the directory can be read by separate tasks (threads). That is, if there is a readdir_r kernel call Any error will raise a DIRECTORY_ERROR exception with ada.text_io, gnat.directory_operations; use ada.text_io, gnat.directory_operations; procedure gdir2 is dir : Dir_Type; dirname : string( 1..80 ); len : natural; begin if Read_Is_Thread_Safe then put_line( "Tasks may read the same directory" ); else put_line( "Tasks may not read the same directory" ); end if; New_Line; Open( dir, "." ); if Is_Open( dir ) then put_Line( "The directory was opened" ); else put_Line( "The directory was not opened" ); end if; loop Read( dir, dirname, len ); exit when len = 0; Put_Line( dirname( 1..len ) ); end loop;
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/14.html (13 of 15) [7/20/2001 11:36:19 AM]
Tasks may not read the same directory The directory was opened . .. gdir.ads gdir.ali gdir.adb gdir.o gdir gdir2.adb gdir2.ali gdir2.o gdir2 End of directory The directories "." and ".." are always returned. New directories can be made with Make_Dir. Make_Dir( "logs" ); -- make a new "logs" directory If you need more features that these, the Linux kernel calls for directories are described in 16.9. The section includes a command to remove directories which cannot be done with Gnat.Directory_Operations.
Lock_File( "/var/lock/customers.txt", Wait => 5.0, Retries => 10 ); Files are unlocked using Unlock_File. This procedure deletes the lock file. Unlock_File( LockDir, "customers.txt" ); Unlock_File( "/var/lock/customers.txt" ); The lock file approach is a voluntary convention. Programs that honour the convention can share the file in an orderly way. A program that doesn't use the package will not be denied access. For true file locking, use the Linux kernel calls described in 16.7.
<--Last Chapter
Table of Contents
Next Chapter-->
Older versions of Florist for Gnat 3.11 work best with a version of Gnat compiled for FSU threads. Native threads require some patching to work, and not all Florist features are supported--see the Florist documentation for details. For more information on FSU threads, read the multitasking section above. Newer versions of Florist, such as the ALT RPM, works with the normal (native threads) version of Gnat. Commercial support for Florist is available form ACT. Because the Linux kernel largely adheres to the POSIX standard, many of Florist functions have the same parameters as their Linux counterparts. Florist divides the POSIX functions into a set of 73 Ada packages, all prefixed with the name "posix". The main package is called "posix.ads" and contains the definition of data types and many of the basic POSIX functions. To write Florist applications, you'll need to link in the Florist library with "-lposix" (check?) and, if necessary, use "-I" to indicate where you've installed the package specifications.
15.2.1 Installation
1. In the C_code directory, type "gcc -O -c *.c" to compile the C files. 2. The Ada files should compile when you build your project with Gnatmake. If TextTools are installed in a different directory than your project, you will need to use the gnatmake -I switch. When linking, you'll need to include the "-lm" and "-lcurses" switches as well as the object files from C_code. TextTools uses the C math library and ncurses 4.0. For example, gnatlink -lm -lncurses C_code/*.o ...
15.2.2 Introduction
Although there are over 600 procedures and functions in TextTools, to open window is fairly uncomplicated. Everything in TextTools is drawn in a window. Everything in a window is a control (sometimes called a "widget"). To display a window, you must create a window, fill in the window with controls to display, and run the window manager's DoDialog command. The following program opens a simple window. ------------------------------------------------------------------with common, os, userio, controls, windows; use common, os, userio, controls, windows; procedure ttdemo is -- Define Window Controls OKButton : aliased ASimpleButton; MessageLine : aliased AStaticLine; -- The Dialog Record
DT : ADialogTaskRecord; begin -- Start TextTools StartupCommon( "demo", "demo" ); StartupOS; StartupUserIO; StartupControls; StartupWindows; -- Create a new window. The window will not appear until the -- DoDialog procedure is used. OpenWindow( To255( "Demo Window" ), -- title at top of window 0, 0, 78, 23, -- the coordinates of the window Style => normal, -- type of window, usually "normal" HasInfoBar => true ); -- true if control information is -- displayed at the bottom of the -- window -- Setup the controls in the window -- OK Button located near bottom of window Init( OKButton, 36, 20, 44, 20, -- coordinates in window 'o' ); -- hot key for OK button SetText( OKButton, "OK" ); -- button will have "OK" SetInfo( OKButton, To255( "Select me to quit" ) ); AddControl( SimpleButton, OKButton'unchecked_access, IsGlobal => false ); -- Message at top of window in bright red Init( MessageLine, 1, 1, 78, 1 ); SetText( MessageLine, "Welcome to TextTools" ); SetStyle( MessageLine, Bold ); SetColour( MessageLine, Red ); AddControl( SimpleButton, MessageLine'unchecked_access, IsGlobal => false ); -- Display the window and handle any input events. When dialog
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/15.html (3 of 13) [7/20/2001 11:36:37 AM]
-- is finished, return control which completed the dialog. loop DoDialog( DT ); exit when DT.Control = 1; -- first control is the OK button end loop; -- close the window CloseWindow; -- Shutdown TextTools ShutdownWindows; ShutdownControls; ShutdownUserIO; ShutdownOS; ShutdownCommon; end ttdemo; ------------------------------------------------------------------Package Overview TextTools is broken into 5 main packages, based on what they do. Common - this package contains all the basic data types used by TextTools, plus subprograms that work with those types. In particular, two important types are defined: Str255 - most TextTools subprograms use this bounded, 255 character string type instead of the standard Ada fixed strings. The function To255 converts an Ada string to a Str255. ToString converts in the other direction. q Str255List - some list controls display a block of text. These controls use the Str255List.List type, a linked list of Str255 strings. The subprograms for this type are defined the generic package gen_list. Most TextTools calls do not return errors. There are some exceptions, such in the OS package. Error numbers are returned in the LastError variable. LastError is 0 if there is no error.
q
OS - this package contains subprograms for working with the Linux operating system: that is, for reading the current time, deleting files, and the like. Texttools pathnames are defined in this package. A path is a Str255 string. The OS package can define path prefixes, beginning with a "$". For example, "$HOME" is predefined as the user's home directory. To delete a file called "temp.txt" from the user's home directory, you can use the OS erase command: Erase( To255( "$HOME/temp.txt" ) );
$SYS is another predefined prefix. This refers to a directory in the user's home directory named with the "short name" you specify in the StartupCommon procedure. Sounds, keyboard macros and the session_log file are located here. UserIO - this package contains all the input/output routines for TextTools: it handles mouse clicks, draws text, and so forth. Normally, only people writing controls will need access to this package. However, the pen colours, beep sounds and text styles, are also defined here. Controls - this package contains all the window controls and related subprograms. Currently defined controls are: Thermometer q ScrollBar q StaticLine q EditLine (and family) q CheckBox q RadioButton q WindowButton q Rectangle q Line q HorizontalSep q VerticalSep q StaticList q CheckList q RadioList q EditList q SourceCodeList (used by TIA) Windows - this is the window manager. It creates and draws windows, and DoDialog procedure lets a user interact with the window. It also handles the "Accessories" window that appears when ESC is pressed.
q
Each package is started with a "Startup" procedure, and shutdown with a "Shutdown" procedure. The only procedure to take parameters is StartupCommon: you need to specify a program name and a short name to use for temporary files.
AddControl - adds a control to the current window. If IsGlobal is false, the coordinates you specified in the control's Init call will be treated as relative to the top-left corner of the window, as opposed to the top left corner of the screen. CloseWindow - closes the last window you created DoDialog - this procedure displays the window and handles all interaction between the user and the window. It has one parameter, ADialogTaskRecord, which lets you set up callbacks (if necessary) and returns the number of the control which terminated the dialog.
15.2.6 Alerts
Alerts are small windows that show a short message. NoteAlert - displays a message with an "OK" button. The status sound is played, if installed. CautionAlert - displays a message with an "OK" button. The text is drawn to emphasize the message. The warning sound is played, if installed. StopAlert - displays a message with an "OK" button. The text is drawn to emphasize the message. The warning sound is played, if installed. YesAlert - display a message with "yes" (default) and "no" buttons. Plays an optional sound. NoAlert - display a message with "yes" and "no" (default) buttons. Plays an optional sound. CancelAlert - display a message with cancel button and a customized button (default). Plays an optional sound. YesCancelAlert - display a message with "yes", "no", and "cancel" buttons and returns the number of the button selected. Plays an optional sound. Example: NoteAlert( "The database has been updated" );
This is an unchanging line of text. EditLine (and family) This is an editable line of text. AdvanceMode - if set, the cursor will move to the next control when the edit field is full. This is useful in business applications where fixed-length product numbers are typed in. BlindMode - if set, hides the characters typed. This is useful for typing in passwords. SimpleButton This is a button that, when selected, terminates the dialog. Instant - if set, the button acts like a menu item. Pressing the hot key will immediately select the button and terminate the dialog. Otherwise, pressing the hot key only moves the cursor to the button. CheckBox A check box is an option which may be turned on or off. RadioButton A radio button is one of a set of options which may be turned on or off. Every radio button has a family number defined in the Init procedure. When a radio button is turned on, all other buttons in the family are turned off. WindowButton Loads a window from disk and displays it. The window must have been saved with the Window Manager's SaveWindow procedure. Rectangle A box which can be drawn around controls. Line A line--what else would it be--drawn between two corners of the enclosing rectangle defined by the Init procedure. HorizontalSep A horizontal line, often used to separate controls into groups. VerticalSep A vertical line, often used to separate controls into groups. StaticList A scrollable box of unchanging text.
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/15.html (8 of 13) [7/20/2001 11:36:38 AM]
CheckList A scrollable box of check boxes. RadioList A scrollable box of radio buttons. EditList A scrollable box of editable text. SourceCodeList (used by PegaSoft's TIA) A scrollable box containing source code. OS Package This package contains various calls for working with the operating system. All calls support path prefixes as described above. Here are some of the subprograms:
q q q q q q q q q q q q
UNIX - run a UNIX shell command. The function variations return the result of the command. RunIt - runs a UNIX program. ValidateFilename - check for a syntactically correct filename. NotEmpty - true if a file is not empty IsDirectory - true if file is a directory IsFile - true if file is a "regular" file MakeTempFileName - creates a random file name for a temporary file Erase - deletes a file LoadList - load a Str255List list from a file SaveList - save a Str255List list to a file MyID - return the PID for your program SessionLog - write to the session log. If a $SYS directory exists, SessionLog creates a file called "session_log" in that directory. All SessionLog calls write to this file.
Beep - play a .wav file. Requires Warren Gay's wavplay program. These files must be saved in the $SYS directory, with the name of the beep sound in upper case.
Keypress - get a keypress q DrawErr - draw an error message. DrawErr draws the text on the left-side screen in white. Use only for emergencies. q GetDisplayInfo - retrieve information about the current screen, such as whether it supports colour, and it's dimensions. Use this information to resize your windows for different screens. Example:
q
Beep( Startup ); -- play startup sound Keyboard Macros UserIO will load a set of keyboard macros at startup. These must be saved in the $SYS directory, in a file called macro_file. The first letter of each line is the key for the macro, and the rest of the line is the expanded macro. For example, if a line in macro_file contained pPegaSoft then typing control-A followed by "p" would put the word "PegaSoft" in the input queue as if the person had typed "PegaSoft".
< > Text - A button. Press Return to activate. Type the hilighted letter to go immediately to this button. q | > Text - An menu button. Enter Return to activate. Type the hilighted letter to immediately activate. q ( ) Text - A radio button. Press Return to select this item and deselect the previous item in the group. q [ ] Text - A check box. Press Return to switch on or off. q -----#------- - A scroll bar. q -----50%----- - A thermometer graph. Buttons with hyphens in them are not selectable.
q
Basic Keyboard Shortcuts: Movement Keys Up/Down Arrow - move up or down to the next menu item * in lists - move up or down one line in the list
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/15.html (10 of 13) [7/20/2001 11:36:38 AM]
* in scroll bars - adjust up or down by 10% Left/Right Arrows - move left or right to the next menu item * in lists - move up or down one line in the list * in scroll bars - adjust up or down by 1 Page Up (or Control-P) - move up one page in a list * in scroll bars - same as up and down arrows Page Down (or Control-N) - move down one page in a list * in scroll bars - same as up and down arrows Home Key (or Control-Y) - move to the top of a list * in scroll bars - go to the top End Key (or Control-E) - move to the bottom of a list * in scroll bars - go to the bottom Tab Key - move to the next item in the window Control-T - move to the previous item in the window Return Key (or Spacebar) - activate a button When inside of a list box, the movement keys move you around the list. If you are on the Linux console, pressing alt and the hilighted letter will always jump to the appropriate object, even if you're inside a list box or the notepad. Editing Keys Control-6 - mark text * only works in edit lists Control-X - clear text * in lists, clear the current line (or lines, if control-6 used) Control-B - copy text * in lists, copy the current line (or lines, if control-6 used) Control-V - paste text * in notepad, paste the last line copied
Misc. Keys ESC Key (or F1) - bring up the accessories menu Control-L - redraw the screen
their initial window. Toolsets such as Qt (used in KDE) and GTK+ (used in GNOME) have larger followings, and Motif support is primarily for older applications being ported to Linux. However, Motif, as a standard, is continuing to evolve.
15.8 Engine_3D
Engine_3D is a real-time 3D drawing package written entirely in Ada. The Linux port is by Duncan Sands. The Engine_3D package is at http://members.nbci.com/gdemont/e3d.htm. <--Last Chapter Table of Contents Next Chapter-->
Like import, but extra options Import a function that returns values as pragma import_valued_procedure parameters Like export, but extra options pragma export_function Like export, but extra options pragma export_procedure
extern?
Because gnat is tightly integrated with gcc, we can make certain assumptions that would otherwise be impossible. q the basic Ada data types are equivalent to their C counterparts: an Ada integer array is a C integer array q in parameters are the same as pass by copy parameters in C q in out parameters are the same as passing a pointer as a parameter in C q Ada string parameters ending in ASCII.NUL are the same as a C string q Ada procedures are the same as C void functions There are rare cases when these assumptions don't hold (e.g. certain cases when null pointer parameters are not allowed by Ada), but, generally speaking, these assumptions are valid under Linux. Gnat has general purpose interfacing pragmas and support for C types in the Interfaces.C package. Use these if you want maximum portability. Because of these assumptions, most C library calls are easily represented in Ada. For example, we check the man page for gettime and discover it returns the current time as a long integer. To call this from Ada, we use function gettime return long_integer; pragma Import( C, gettime ); Since there is no Ada body for the gettime function, we use pragma import to let gnat know gettime is a C function. When we link, we need to specify the C library that function is in. In the case for the GNU C library, this is unnecessary since it's automatically linked. We can now call the C function gettime as if we wrote it ourselves.In C, it's possible to call a function and discard the result by not assigning it to anything. You can call C functions from Ada this way by declaring them a procedure. For example: procedure gettime; pragma Import( C, gettime ); In this case, it's not particularly useful to call gettime and throw away the time it gives you. In general, you should avoid discarding the result because you may find it useful at some time in the future. However, there are certain C function where the result is provided only for flexibility, such as functions that return a pointer in a parameter and return the same pointer as the function result as well. These can safely be discarded by treating the function as a procedure.If we wanted to export an integer variable called TotalTimeEstimate to C, we'd use TotalTimeEstimate : integer; pragma Export( C, TotalTimeEstimate );
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/16.html (1 of 47) [7/20/2001 11:37:13 AM]
A C function that returns void corresponds to an Ada procedure. When importing or exporting to C, gnat converts the variable to lower case because C is a case-sensitive language. TotalTimeEstimate would be called totaltimeestimate in a C program. You can override this by providing a specific C name to link to. For example, pragma Export( C, TotalTimeEstimate, "TotalTimeEstimate" ); Import and Export don't require the name be the same at all. However, using entirely different names in C and Ada will make your program hard to understand. If you want to import functions from libraries other than the standard C library, you will have to explicitly link them in. For example, to use the C math library, libm.a, would have to be explicitly linked using -lm. In C, functions can have parameters that change value, while in Ada this kind of function is not allowed because functions can only have "in" parameters. To get around this problem, gnat defines an import_valued_procedure pragma. Suppose you have a C function like this: int SomeCFunction( char * param ) Normally, there is no way to represent this kind of function in an Ada program. However, we can import it by treating it as a procedure using the import_valued_procedure pragma: procedure SomeCFunction ( result : out integer; param : in out integer ); pragma import( C, SomeCFunction); pragma import_valued_procedure( SomeCFunction ); The import_valued_procedure pragma tells gnat that this procedure corresponds to a C function: the first parameter is the result of the C function, and the remaining parameters correspond to the parameters of the C function. The first import pragma is not strictly required, but ACT recommends using it. You can't import identifiers created by the #define statement since they only exist before a C program is compiled. You also can't import types (except for C++ classes) since types have no address in memory. [KB-true?] There is one case where these tricks fail: when the C function returns a pointer to a C variable that it declared. In this case, the function is returning a new C pointer. Luckily, Ada provides a package called Address_To_Access_Conversions to convert between C pointers and Ada access types. You instantiate the package with the type you want to convert between, and the package creates an access type that can be converted to and from an address (which is a C pointer).The following program demonstrates conversions to and from C pointer types. with Ada.Text_IO, System.Address_To_Access_Conversions; use Ada.Text_IO; procedure pointers is package IntPtrs is new System.Address_To_Access_Conversions( integer ); -- Instantiate a package to convert access types to/from addresses. -- This creates an integer access type called Object_Pointer. five : aliased integer := 5; -- Five is aliased because we will be using access types on it int_pointer : IntPtrs.Object_Pointer; -- This is an Ada access all type int_address : System.Address; -- This is an address in memory, a C pointer begin int_pointer := five'unchecked_access; -- Unchecked_access needed because five is local to main program.
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/16.html (2 of 47) [7/20/2001 11:37:13 AM]
-- If it was global, we could use 'access. int_address := five'address; -- Addresses can be found with the 'address attribute. -- This is the equivalent of a C pointer. int_pointer := IntPtrs.To_Pointer( int_address ); int_address := IntPtrs.To_Address( int_pointer); -- Convert between Ada and C pointer types. end pointers; For example, the standard C library function get_current_dir_name returns a pointer to a C string which it declares. To use get_current_dir_name, we have to instantiate Address_To_Access_Conversions for an array of characters (a C string), and convert the address to an access type using something like CharArray_pointer := CharArrayPtrs.To_Pointer( get_current_dir_name ); There is no other way in Ada to access the array that get_current_dir_name points to. KB-If your main program is a C program, you need to call adainit before any Ada code.
In Multithreading programs, be aware errno may not be not "thread safe" because it can be shared between threads. [KB: document how to do it with threads]
Linux provides two functions for working with errno error numbers. type string255is new string(1..255); type strptr is access string255; -- error messages are no longer than 255 characters procedure perror( message : string); pragma import( C, perror ); Perror prints a standard error description with a leading message to standard error. function strerror( error_number : integer ) return strptr; pragma import( C, strerror); Retuns a C string standard error description. The following example program makes a deliberate error with the link function and prints the error message using perror and stderror.
with ada.text_io, ada.strings.fixed; use ada.text_io, ada.strings.fixed; procedure perr is -- an example of perror and strerror error messages procedure perror( message : string ); pragma import( C, perror ); -- print a standard error description with a leading message type string255 is new string(1..255); type strptr is access string255; -- error messages are no longer than 255 characters function strerror( error_number : integer ) pragma import( C, strerror); -- get a standard error description errno : integer; pragma import( C, errno ); -- last error number function link( path1, path2 : string ) return integer; pragma import( C, link); -- we'll use the link function to create an error LinkResult : integer; -- value returned by link ErrorMessagePtr : strptr; -- pointer to stderror message NullLocation : integer; -- location of NUL in stderror message begin Put_Line( "This is an example of perror and strerror"); New_Line; -- make a deliberate error and print it with perror Put_Line( "Trying to link a non-existent file to itself.." ); LinkResult := Link( "blahblah", "blahblah" ); if LinkResult = -1 then perror( "Link failed" ); end if; New_Line; -- Retrieve the last error message with strerror. -- Because strerror returns a C string, only print the -- string up to the first NUL character. ErrorMessagePtr := StrError( Errno ); NullLocation := Index( string( ErrorMessagePtr.all ), "" & ASCII.NUL ); Put( "The last error message was '" ); Put( Head( string( ErrorMessagePtr.all ), NullLocation-1 ) ); Put_Line( "'." ); end perr; return strptr;
This is an example of perror and strerror Trying to link a non-existent file to itself. Link failed: No such file or directory The last error message was 'No such file or directory'. A table of error numbers is in the appendix.
procedure settimeofday( result : out integer; tv : in out timeval; tz : in out timezone ); pragma import( C, settimeofday ); pragma import_valued_procedure( settimeofday ); Set the current time as the number of microseconds since January 1, 1970. Returns 0 for success. procedure tzset; pragma import( C, tzset ); Create the TZ environment variable, if it doesn't exist, and sets it to the current timezone as specified in /etc/localtime or /usr/lib/zoneinfo/localtime. This is automatically invoked by the standard C library time functions whenever necessary. procedure adjtimex( result : out integer; buf : inout timex ); pragma import( C, adjtimex ); Tunes the kernel's clock for specific time parameters
----------
seconds on the clock (0-59) minutes on the clock (0-59) hour on the clock (0-23) day of the month (1-31) month (0-11) year day of the week (0-6) day of the year (0-365) >0 is daylight savings time, 0=not, <0 unknown
You will also need the Address_To_Access_Conversions package to convert C pointers to tm record into Ada access type pointers. package TmPtrs is new System.Address_To_Access_Conversions( tm ); function localtime( time : in out time_t ) return system.address; pragma import( C, localtime ); Change the time into a tm record, making changes for the current time zone. time is the C pointer to the seconds since 1970. function gmtime( time : in out time_t ) return system.address; pragma import( C, gmtime ); Change the time into a tm record for UTC (Coordinated Universal Time). time is the C pointer to the seconds since 1970. function mktime( tm : system.address ) return time_t; pragma import( C, mktime ); Convert a tm record into the seconds since 1970. To get the current time in tm format, seconds_since_1970 : long_integer;
16.4.1 Ownership
The owner of a program is referred to as the UID (user identification number). Under Linux, there are actually three owners to any given program: the effective UID, the real UID and the saved UID. Normally, these three are all the same login. The real and saved uids are provided for programs that must temporarily pretend to be somebody else, like a daemon that needs special login for a short period of time, or setuid/setgid programs that must temporarily switch between owners. These special
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/16.html (7 of 47) [7/20/2001 11:37:13 AM]
functions are not covered here. function getuid return integer; pragma import( C, getuid ); Get the (real) UID of a process. Example: Put_Line( "My UID is " & getuid'img ); function setuid (uid : integer ) return integer; pragma import( C, setuid ); Change the effective (and saved and real) UID of a process to a new owner. The GID (group identification number) is the group the program belongs to. In Linux, there's a main, effective group number, and any number of secondary groups that a program can belong to. There is also real and saved GIDs, just like UIDs. procedure getgroups( result : out integer; num : integer; gidlist ); pragma import( C, getgroups); pragma import_valued_procedure( getgroups ); Return a list of group numbers that a process belongs to. Gidlist is the address of a list of C strings. function getgid return integer; pragma import( C, getgid ); Get the (real) UID of the process. Example: Put_Line( "My GID is " & getgid'img ); function setguid( gid : integer ) return integer; pragma import( C, setgid ); Change the effective GID (and saved and real) of a process to a new group. Linux also allows you to arrange processes into groups for easier management of multiple processes at once. Each process group as a, no surprise, a process group identification number (PGID). function setpgid( pid, pgid : integer ) return integer; pragma import( C, setpgid ); Place a process into a new process group. PID 0 is the current process. PGID 0 creates a new process group. function getpgid( pid : intger ) return integer; pragma import( C, getpgid ); Example: Put_Line( "My PGID is " & getpgid'img ); Returns the process group number for a process. PID 0 is the current process. Every program and process group also belongs to a session (as in a login session). When you log off the computer, Linux automatically stops all programs that were running in your session. The session leader is the top process in a session, such as your login shell. However, if you want to create a new session for some reason, you can use the following function: function setsid return integer; pragma import( C, setsid ); Start a new session and return a new session identification number (SID). Example: NewSID := etsid; 16.4.2 Other Functions function kill( uid, signal : integer ) return integer; pragma import( C, kill ); Stop a child process that your process has started, the same as using the kill command at the shell prompt. (More accuately, send a signal to a child processsome signals won't stop the child process.) Example: Result := kill( MyRunawayChildUID, 15 ); -- send SIGTERM (terminate) signal
Signal handling, in general, is easier through Ada.Interrupts than through Linux kernel calls because of their heavy reliance on C macros.--KB function alarm( seconds : Interfaces.C.unsigned ) return Interfaces.C.unsigned; pragma import( C, alarm ); After the specified number of seconds, cause a SIGALRM interrupt in the current process.
16.6 Multitasking
Multitasking creates child processes to do tasks for your main program. On multiprocessor machines, different processes can be assigned to different processors allowing work to be done simultaneously. On single processor machines, the processor switches several times a second between processes and does a little work on each. The function to create a new child process is called fork. When Linux creates a new process, it doesn't start by creating a blank process. Instead, it makes a copy of the original process so there are effectively two copies of your program running. The fork function returns a value to tells your program if it is the original program or the new copy. If you want to run a different program, you'll have to use one of the exec family of functions to load and run the new program. The exec functions destroy the old program and run a new program in its place. The above section, Using System and OSLib.Spawn, has an example C function called CrunIt that uses fork to start a new process and run a new program. type pid_t is new integer; function fork return pid_t; pragma import( C, fork ); Create a new child process identical to the original process and return 0 if the program is running as the child process or the PID of the parent if the program is running as the original parent process. Example: myPID := fork; procedure wait( pid : out pid_t; status : in out integer ); pragma import( C, wait); pragma import_valued_procedure( wait ); Wait until a child process have finished running. Pid is the PID of the child. status is an integer code indicating whether the child finished normally, and if it was stopped by a signal, which signal terminated the program. Status can be a null pointer if you don't want status information. Example: wait( wait_pid, wait_status ); procedure waitpid( pid : out pid_t, pid_or_gid : in out pid_t; status : in out integer; options : integer ); pragma import( C, waitpid); pragma import_valued_procedure( waitpid); Wait for a specific child. If pid_or_gid is less than -1, waitpid waits for any child in the specified group id. If pid_or_gid is -1,
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/16.html (9 of 47) [7/20/2001 11:37:14 AM]
it waits for any child (the same as wait). If pid_or_gid is 0, it waits for any child in the same group id as the parent. If pid_or_gid is greater than zero, waits for the child with the specified pid. Status can be a null pointer if you don't want status information. Options can determine whether waitpid returns immediately or blocks indefintely. Example: waitpid( child_pid, -children_gid, wait_status, 0 ); Wait3 and Wait4 are BSD UNIX variations which perform the same functions as wait and waitpid but with slightly different parameters. When multitasking, if a child process stops, it's retained in memory so that the parent can use wait to find out the reason it stopped running. These children are called "zombies". If the parent process doesn't use wait, the zombies will remain indefinitely, using up system resources. For any large multitasking program, make sure you handle SIGCHLD signals: these are created when a child stops running. The SIGCHLD handler only needs to call wait and Linux will then remove the child process from memory. The following is a simple multitasking example that multitasks two Put_Line statements. -- a simple example of multitasking that multitasks -- two put_line statements with ada.text_io; use ada.text_io; procedure multitask is type pid_t is new integer; function fork return pid_t; pragma import( C, fork); -- create a new process errno : integer; pragma import( C, errno); -- the last error code procedure wait( pid : out pid_t; status : in out integer); pragma import( C, wait); pragma import_valued_procedure( wait); -- wait until all child processes are finished myPID : pid_t; wait_pid : pid_t; wait_status : integer; begin Put_Line( "Welcome to this multitasking example" ); Put_Line( "This is the original process." ); New_Line; -- the fork function duplicates this program into -- two identical processes. Put_Line( "Splitting into two identical processes..." ); Put_Line( "-----------------------------------------" ); myPID := fork; -- split in two! -- This program is now the original process or the
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/16.html (10 of 47) [7/20/2001 11:37:14 AM]
-- new child process. myPID tells you which process -- you are. if myPID < 0 then Put_Line( Standard_Error, "Fork has failed. Error code " & errno'img ); elsif myPID = 0 then Put_Line( "This is the child process" ); else Put_Line( "This is the original process." ); -- wait until child is finished wait( wait_pid, wait_status); if wait_pid < 0 then Put_Line( Standard_Error, "Wait error: wait returned PID " & wait_pid'img & " and error number " & errno'img); end if; end if; end multitask;
Welcome to this multitasking example This is the original process. Splitting into two identical processes... ----------------------------------------This is the original process. This is the child process
Linux never shortens files. If your file gets smaller, you must shorten it yourself using truncate.
The following bindings assume these types have been defined. type file_id is new integer; -- file ID number are discussed below type mode_t is new integer; type gid_t is new integer; type uid_t is new integer; type size_t is new long_integer; function unlink( pathname : string ) return integer; pragma import( C, unlink );
Delete a file. Example: Result := unlink( "/tmp/temp.txt" & ASCII.NUL ); function link( oldpath, newpath : string) return integer; pragma import( C, link ); Make a shortcut (hard link) to a file. Example: Result := link( "/tmp/temp.txt" & ASCII.NUL, "/tmp/newtemp.txt" & ASCII.NUL ); procedure getcwd( buf1 : out StringPtr; buf2 : in out stringptr; size : integer ); pragma import( C, getcwd ); pragma import_valued_procedure( getcwd) Return the current working directory. function mkdir( pathname : stringPtr; mode : mode_t ) return integer; pragma import( C, mkdir ); Create a new directory and set default permissions. function rmdir( pathname : string ) return integer; pragma import( C, rmdir ); Delete a directory. Example: Result := rmdir( "/tmp/tempdir" & ASCII.NUL ); function umask( mask : integer ) return integer; pragma import( c, umask ); Sets the default file permissions. function stat( filename : stringPtr; buf : stat_struct ) return integer; pragma import( C, stat ); Get information about a file, such as size and when it was last opened. function lstat( filename : stringPtr; buf : stat_struct ) return integer pragma import( C, lstat ); Same as stat function, but doesn't follow symbolic links. function tmpnam( s : stringPtr ) return stringPtr; pragma import( C, tmpnam ); Create a random name for a temporary file. function chown( path : string; owner : uid_t; group : gid_t) return integer; pragma import( C, chown ); function fchown( file : file_id; owner : uid_t; group : gid_t return integer; pragma import( C, fchown ); Change the ownership of a file to the specified owner and group. Example: Result := chown( "root.txt" & ASCII.NUL, 0, 0 ); function chmod( path : string; mode : mode_t ) return integer; pragma import( C, chmod ); function fchmod( file : file_id; mode : mode_t ) return integer; pragma import( C, fchmod ); Change the read/write/execute permissions on a file. Example: Result := chmod( "secure.txt" & ASCII.NUL, #8#640 ); Other low-level file operations are all done with the fcntl (file control) function. There are three variations to fcntl: it may have an operation code, an operation code and a long integer argument, or an operation code and a locking record argument. The operation numbers are defined in /usr/src/linux/asm-i386/fnctl.h:
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/16.html (12 of 47) [7/20/2001 11:37:14 AM]
F_DUPFD : constant integer := 0; F_GETFD : constant integer := 1; F_SETFD : constant integer := 2; F_GETFL : constant integer := 3; F_SETFL : constant integer := 4; F_GETLK : constant integer := 5; F_SETLK : constant integer := 6; F_SETLKW : constant integer := 7; F_SETOWN : constant integer := 8; F_GETOWN : constant integer := 9; F_SETSIG : constant integer := 10; F_GETSIG : constant integer := 11; function fcntl( fd : file_id; operation => F_DUPFD ) pragma import( C, fcntl ); Duplicates a file descriptor (same as dup2, but different errors returned). New descriptor shares everything except close-on-exec. New descriptor is returned. function fcntl( fd : file_id; operation => F_GETFD ) pragma import( C, fcntl ); Get close-on-exec flag; low bit is zero, file will close on exec kernel call. function fcntl( fd : file_id; operation => F_SETFD; arg : long_integer ) pragma import( C, fcntl ); Set the close-on-exec flag; low bit is 1 to make file close on exec kernel call. function fcntl( fd : file_id; operation => F_GETFL ) pragma import( C, fcntl ); Get flags used on open kernel call used to open the file function fcntl( fd : file_id; operation => F_SETFL; arg : long_integer ) pragma import( C, fcntl ); Set flags for open kernel call. Only async, nonblock and appending can be changed. procedure fcntl( result : out integer; fd : file_id; operation => F_GETLK; lock : in out lockstruct ) return integer pragma import( C, fcntl ); pragma import_valued_procedure( fcntl ); Return a copy of the lock that prevents the program from accessing the file, or else if there is nothing blocking, the type of lock procedure fcntl( result : out integer; fd : file_id; operation => F_SETLK; lock : in out lockstruct ) return integer pragma import( C, fcntl ); pragma import_valued_procedure( fcntl ); Place a lock on the file. If someone else has locked the file already, -1 is returned and errno contains the locking error. procedure fcntl( result : out integer; fd : file_id; operation => F_SETLKW; lock : in out lockstruct ) return integer pragma import( C, fcntl ); pragma import_valued_procedure( fcntl ); Place a read or write lock on the file, or to unlock it. If someone else has locked the file already, wait until the lock can be
placed. Additional information about locks are found in /usr/src/linux/Documentation/locks.txt type aLock is new short_integer; F_RDLCK : constant aLock := 0; -F_WRLCK : constant aLock := 1; -F_UNLCK : constant aLock := 2; -F_EXLCK : constant aLock := 3; -F_SHLCK : constant aLock := 4; -type aWhenceMode is SEEK_SET : constant SEEK_CUR : constant SEEK_END : constant
read lock write lock unlock (remove a lock) exclusive lock shared lock
new short_integer; aWhenceMode := 0; -- absolute position aWhenceMode := 1; -- offset from current position aWhenceMode := 2; -- offset from end of file
type lockstruct is record l_type : aLock; l_whence : short_integer; l_start : integer; l_len : integer; l_pid : integer; end record;
------
type of lock how to interpret l_start offset or position number of bytes to lock (0 for all) with GETLK, process ID owning lock
To lock a file, create a lockstruct record and fill in the details about the kind of lock you want. A read lock (F_RDLCK) makes the part of the file you specify read-only. No one can write to that part of the file. A write lock prevents any other program from reading or writing to the part of the file you specify. Your program may change that part of the file without being concerned that another process will try to read it before you're finished. If your program stops prematurely, the locks will be released. Example: Get exclusive right to write to the file, waiting until it's possible: -- lock file myLockStruct : lockStruct; result : integer; ... myLockStruct.l_type := F_WRLCK; myLockStruct.l_whence := 0; myLockStruct.l_start := 0; myLockStruct.l_end := 0; fcntl( result, fd, F_SETLKW, myLockStruct ); if result = -1 then put_line( standard_error, "fcntl failed" ); end if; -- file is now locked ... -- unlock file myLockStruct.l_type := F_UNLCK; myLockStruct.l_whence := 0; fcntl( result, fd, F_SETLKW, myLockStruct ); if result = -1 then put_line( standard_error, "fcntl failed" ); end if;
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/16.html (14 of 47) [7/20/2001 11:37:14 AM]
[Double check off_t size for l_start, l_len--KB] function fcntl( fd : file_id; operation => F_GETOWN ) pragma import( C, fcntl ); Get the process (or process group) id of owner of file. The owner is the process that handles SIGIO and SIGURG signals for that file. function fcntl( fd : file_id; operation => F_SETOWN, arg : long_integer ) pragma import( C, fcntl ); Set the process (or process group) id of owner of file. The owner is the process that handles SIGIO and SIGURG signals for that file. This affects async files and sockets. function fcntl( fd : file_id; operation => F_GETSIG ) pragma import( C, fcntl ); Get the signal number of the signal sent when input or output becomes possible on a file (usually SIGIO or zero). (This is a Linux-specific function.) function fcntl( fd : file_id; operation => F_SETSIG, arg : long_integer ) pragma import( C, fcntl ); Set the signal number of the signal sent when input or output becomes possible on a file (zero being the default SIGIO). Use this to set up a signal handler alternative to the kernel calls select and poll. See the man page for more information. (This is a Linux-specifc function.)
O_TRUNC : constant integer := 8#01000#; -- file exists? truncate it O_APPEND : constant integer := 8#02000#; -- file exists? move to end O_NONBLOCK : constant integer := 8#04000#; -- if pipe, don't wait for data O_SYNC : constant integer := 8#010000#; -- don't cache writes O_ASYNC : constant integer := 8#020000#; -- async. IO via SIGIO O_DIRECT : constant integer := 8#040000#; -- direct disk access O_LARGEFILE: constant integer := 8#0100000#; -- not implemented in Linux (yet) O_DIRECTORY: constant integer := 8#0200000#; -- error if file isn't a dir O_NOFOLLOW : constant integer := 8#0400000#; -- if sym link, open link itself Flags may be added together. O_EXCL is somewhat obsolete and has limitations on certain file systems. Use fcntl to lock files instead. O_SYNC only works on the ext2 file system or on block devices. function creat( path : string, mode : mode_t ) return file_id; pragma import( c, creat ); Creat is a short form for open( path, create + writeonly + truncate, mode ) function close( file : file_id ) return integer; pragma import( C, close ); Closes a file. function truncate( path : string; length : size_t ) return integer; pragma import( C, truncate ); function ftruncate( file : file_id; length : size_t ) return integer; pragma import( C, ftruncate); Shorten a file to a specific length. Despite its name, ftruncate is a kernel call, not a standard C library call like fopen. function read( file : file_id; b : in out buffer; length : size_t ) return ssize_t; pragma import( C, read ); Read bytes from the specified file into a buffer. Buffer is any type of destination for the bytes read, with length being the size of the buffer in bytes. The number of bytes read is returned, or -1 on an error. function write( file : file_id; b : in out buffer; length : size_t ) return ssize_t; pragma import( C, write ); Write bytes from a buffer into the specified file. Buffer is any type of destination for the bytes read, with length being the size of the buffer in bytes. The number of bytes written is returned, or -1 on an error. function lseek( file : file_id; offset : off_t; whence : integer ) return integer; pragma import( C, lseek ); Move to a particular position in the specified file. Whence is a code representing where your starting position is. Offset is how many bytes to move. There are three possible "whence" values: SEEK_SET : constant integer := 0; -- from start of file SEEK_CUR : constant integer := 1; -- offset from current position SEEK_END : constant integer := 2; -- from end of file File input/output is naturally suited to generic packages. You can use the generic package to hide the low-level details of the standard C library. In following example, SeqIO is a generic package for reading and writing a sequential file of some type, using the kernel calls mentioned above. -- SeqIO --- A simple sequential IO package using standard C functions
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/16.html (16 of 47) [7/20/2001 11:37:14 AM]
generic type AFileElement is private; package SeqIO is type AFileID is new short_integer; seqio_error : exception; function Open( path : string; read : boolean := true ) return AFileID; -- open a new file for read or write procedure Close( fid : AFileID ); -- close a file procedure Read( fid : AFileID; data : in out AFileElement); -- read one data item from the file. seqio_error is raised -- if the data couldn't be read procedure Write( fid : AFileID; data : AFileElement ); -- write one data item to the file. seqio_error is raised -- if the data couldn't be written end SeqIO; package body SeqIO is pragma optimize( space); -- Import C file handling functions type mode_t is new integer; -- C mode_t type type size_t is new integer; -- C size_t type subtype ssize_t is size_t; -- C ssize_t type -- The C file functions we'll be using -- (denoted with a C_ prefix for clarity ) function C_Open( path : string; flags : integer; mode : mode_t) return AFileID; pragma import( C, C_Open, "open"); function C_Close( file : AFileID ) return integer; pragma import( C, C_Close, "close" ); procedure C_Read( size : out ssize_t; file : AFileID; data : in out AFileElement; count: size_t); pragma import( C, C_Read, "read"); pragma import_valued_procedure( C_Read); -- Using an "in out" parameter is the easiest way to pass -- the address of the data element. Because Ada doesn't -- allow in out parameters in functions, we'll use gnat's -- valued procedure pragma to pretend read is a procedure procedure C_Write( size : out ssize_t;
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/16.html (17 of 47) [7/20/2001 11:37:14 AM]
file : AFileID; data : in out AFileElement; count: size_t); pragma import( C, C_Write, "write"); pragma import_valued_procedure( C_Write); -- Using an "in out" parameter is the easiest way to pass -- the address of the data element. Because Ada doesn't -- allow in out parameters in functions, we'll use gnat's -- valued procedure pragma to pretend write is a procedure -- Our Ada subprograms function Open( path : string; read : boolean := true ) return AFileID -- open a new file for read or write flags : integer; begin -- the flag values are listed in fcntlbits.h and man 2 open if read then flags := 0; -- read only, existing file else flags := 1000 + 100 + 1; -- write only, create or truncate end if; -- octal 640 => usr=read/write, group=read, others=no access return C_Open( path & ASCII.NUL, flags, 8#640# ); end Open; procedure Close( fid : AFileID ) is -- close a file Result : integer; -- we'll ignore it begin Result := C_Close( fid); end Close; procedure Read( fid : AFileID; data : in out AFileElement ) is -- read one data item from the file BytesRead : ssize_t; begin -- 'size returns the size of the type in bits, so we -- divide by 8 for number of bytes to read C_Read( BytesRead, fid, data, AFileElement'size / 8 ); if BytesRead /= AFileElement'size / 8 then raise seqio_error; end if; end Read; procedure Write( fid : AFileID; data : AFileElement ) is -- write one data item to the file BytesWritten : ssize_t; data2write : AFileElement; begin -- can't use data directly because it's an "in" parameter data2write := data; -- 'size returns the size of the type in bits, so we -- divide by 8 for number of bytes to write
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/16.html (18 of 47) [7/20/2001 11:37:14 AM]
is
C_Write( BytesWritten, fid, data2write, AFileElement'size / 8); if BytesWritten /= AFileElement'size / 8 then raise seqio_error; end if; end Write; end SeqIO; You can test SeqIO with the following program: with SeqIO; with Ada.Text_IO; use Ada.Text_IO; procedure SeqIOtest is -- program to test SeqIO package IntIO is new SeqIO( integer); -- IntIO is a SeqIO for integer numbers id : IntIO.AFileID; int2read : integer; begin Put_Line( "Testing SeqIO package..." ); New_Line; -- Part #1: Write numbers to a file Put_Line( "Writing numbers 1 to 10 to a file..."); id := IntIO.Open( "int_list.txt", read => false ); for i in 1..10 loop IntIO.Write( id, i) end loop; IntIO.Close( id); -- Part #2: Read the numbers back from the same file Put_Line( "Reading numbers back..." ); id := IntIO.Open( "int_list.txt", read => true); for i in 1..10 loop IntIO.Read( id, int2read); Put_Line( "Number" & i'img & " =" & int2read'img ); end loop; IntIO.Close( id ); exception when IntIO.seqio_error => Put_Line( "Oh, oh! seqio_error!"); end SeqIOtest; Note: This should be rewritten because a failure to write all the bytes is not necessarily an error--Linux has a buffer limit on how much it writes at one time--KB
Writing numbers 1 to 10 to a file... Reading numbers back... Number 1 = 1 Number 2 = 2 Number 3 = 3 Number 4 = 4 Number 5 = 5 Number 6 = 6 Number 7 = 7 Number 8 = 8 Number 9 = 9 Number 10 = 10 File Multiplexing Operations These kernel calls help programs that have to monitor several file descriptors at once for activity. procedure select( result : out integer; topplusone : integer; readset : in out fdset; writeset : in out fd_set; errorset : in out fd_set; timeout : in out timeval ); pragma import( C, select ); pragma import_valued_procedure( select ); Select checks one or more file descriptors to see if they are ready for reading, writing, or if there is an error. It will wait up to timeout microseconds before timing out (0 wll return immediately). topplusone is the numerically highest file descriptor to wait on, plue one. The result is 0 for a timeout, -1 for failure, or the number of file discriptors that are ready and the file discriptor sets indicate which ones. Unlike most UNIX's, Linux leaves the time remaining in the timeout record so that you can use select in a timing loop--to repeatedly select file descriptors until the timeout counts down to zero. Other UNIX's leave the timeout unchanged.
type pollfd is record fd : integer; events : short_integer; revents : short_integer; end record; Poll Events POLLIN := 16#1#; POLLPRI := 16#2#; POLLOUT := 16#4#; POLLERR := 16#8#; POLLHUP := 16#10#; POLLNVAL := 16#20#; These are defined in asm/poll.h. procedure poll( result : out integer; ufds : in out pollfd; nfds : integer; timeout_milliseconds : integer ); pragma import( C, poll ); pragma import_valued_procedure( poll ); The name of this kernel call is misleading: poll is a form of select(). timeout_milliseconds is a timeout in milliseconds, -1 for no timeout. ufds is an array of pollfd records for files that poll() should monitor. Poll returns the number of pollfd array elements that have something to report, 0 in a timeout, or -1 for an error. Each bit in events, when set, indicates a particular event that the program is waiting for. Revents represents the events which occurred.
16.9 Directories
Directories are "folders" containing collections of files and other directories. In Linux, a directory is a special kind of file. Some of the standard file operations work on directories and some other file operations are specific to directories. The top-most directory is /, or the root directory. All files on a system are located under the root directory. Disk drives do not have separate designations as in MS-DOS. A period (.) represents the current directory, and a double period (..) represents the parent directory of the current directory. All directories have . and .. defined. The root directory, of course, doesn't have a parent: it's .. entry points to itself. Many of the kernel calls and standard C library functions dealing with directories use functions that return C pointers. As mentioned in the bindings section, the only way to convert these kind of functions to Ada is by declaring the C pointers as a System.Address type and changing the C pointers to Ada access types using the Address_To_Access_Conversions package. procedure getcwd( buffer : out string; maxsize : size_t ); pragma import( C, getcwd ); Returns the name of the current working directory as a C string in buffer. Maxsize is the size of the buffer. All symbolic links are dereferenced. function get_current_dir_name return System.Address; pragma import( C, get_current_dir_name ); Like getcwd, returns the current working directory name as a pointer to a C string. Unlike getcwd, symbolic links aren't dereferenced. Use this function to show the current directory to a user. procedure chdir( path : string); pragma import( C, chdir ); Change the current working directory to the specified path. Example: chdir( "/home/bob/stuff" & ASCII.NUL ); function mkdir( path : string; mode : size_t ) return integer; pragma import( C, mkdir ); Create a new directory with permission bits as specified by mode. function rmdir( path : string ) return integer; pragma import( C, rmdir ); Remove a directory. function opendir( path : string ) return System.Address; pragma import( C, opendir); Open a directory in order to read its contents with readdir. function closedir( info : System.Address) return integer; pragma import( C, closedir); Close a directory openned with opendir. Info is the C pointer returned by opendir. function readdir( info : System.Address ) return DirEntCPtr; pragma import( C, readdir); Read the next entry in the directory. A null C pointer is returned if there is no more entries. Info is the C pointer returned by opendir. function rewinddir( info : System.Address ) return integer; pragma import( C, rewinddir); Begin reading from the top of the directory. Info is the C pointer returned by opendir. function telldir( info : System.Address) return integer; pragma import( C, telldir); Mark the current position in the directory, to return to it later using the seekdir function. Info is the C pointer returned by
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/16.html (21 of 47) [7/20/2001 11:37:15 AM]
opendir. function seekdir( info : System.Address; position : integer ) return integer; pragma import( C, seekdir ); Return to a position in the directory marked by telldir. Info is the C pointer returned by opendir. function chroot( newroot : string ) return int; pragma import( C, chroot ); Make Linux think that a different directory is the root directory (for your program). This is used by programs such as FTP servers to prevent uses from trying to access files outside of a designated FTP directory. Example: Result := chroot( "/home/server/stay-in-this-directory" & ASCII.NUL); There is also a scandir function that reads a directory and sorts the entries, but this is difficult to use directly from Ada. The following program demonstrates some of the directory subprograms in Linux. with Ada.Text_IO, Interfaces.C, Ada.Strings.Fixed; use Ada.Text_IO, Interfaces.C, Ada.Strings.Fixed; with System.Address_To_Access_Conversions; procedure direct is -- Working with directories subtype size_t is Interfaces.C.size_t; -- renaming size_t to save some typing package CStringPtrs is new System.Address_To_Access_Conversions( string ); use CStringPtrs; -- Convert between C and Ada pointers to a string subtype DirInfoCPtr is System.Address; subtype DirEntCPtr is System.Address; -- two C pointers (System.Address types), renamed for -- clarity below type DirEnt is record inode : long_integer; -- inode number offset : integer; -- system dependent offset2: unsigned_char; -- system dependent reclen : unsigned_short; -- system dependent name : string( 1..257 ); -- name of file end record; pragma pack( dirent); -- dirent is defined in /usr/src/linux../linux/dirent.h package DirEntPtrs is new System.Address_To_Access_Conversions( DirEnt ); use DirEntPtrs; -- Convert between C and Ada pointers to a directory entry procedure getcwd( buffer : out string; maxsize : size_t ); pragma import( C, getcwd );
function get_current_dir_name return System.Address; pragma import( C, get_current_dir_name); function mkdir( path : string; mode : size_t ) return integer; pragma import( C, mkdir ); function rmdir( path : string ) return integer; pragma import( C, rmdir ); function opendir( path : string ) return DirInfoCPtr; pragma import( C, opendir ); function closedir( info : DirInfoCPtr ) return integer; pragma import( C, closedir ); function readdir( info : DirInfoCPtr ) return DirEntCPtr; pragma import( C, readdir ); function rewinddir( info : DirInfoCPtr ) return integer; pragma import( C, rewinddir ); function telldir( info : DirInfoCPtr ) return integer; pragma import( C, telldir ); function seekdir( info : DirInfoCPtr; position : integer ) return integer; pragma import( C, seekdir ); -- scandir hard to use from Ada s: string(1..80); csop: CStringPtrs.Object_Pointer; Result: integer; DirInfo: DirInfoCPtr; direntop : DirEntPtrs.Object_Pointer; Position : integer; LastPosition : integer; begin Put_Line( "This program demonstrates Linux's directory functions" ); New_Line; -- getcwd example getcwd( s, s'length ); Put( "The current path (simplified) is " ); Put_Line( Head( s, Index( s, ASCII.NUL & "" )-1 )); -- Index for fixed strings takes a string as the second parameter -- We'll make a string containing an ASCII.NUL with & -- get_current_dir_name example csop := To_Pointer( get_current_dir_name );
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/16.html (23 of 47) [7/20/2001 11:37:15 AM]
Put( "The current path is " ); Put_Line( Head( csop.all, Index( csop.all, ASCII.NUL & "" )-1 ) ); -- mkdir example: create a directory named "temp" Result := mkdir( "temp" & ASCII.NUL, 755 ); if Result /= 0 then Put_Line( "mkdir error" ); else Put_Line( "temp directory created" ); end if; -- rmdir example: remove the directory we just made Result := rmdir( "temp" & ASCII.NUL ); if Result /= 0 then Put_Line( "rmdir error" ); else Put_Line( "temp directory removed" ); end if; New_Line; -- directory reading DirInfo := OpenDir( "/home/ken/ada" & ASCII.NUL); Put_Line( "Directory /home/ken/ada contains these files:"); loop direntop := To_Pointer( ReadDir( DirInfo ) ); exit when direntop = null; -- TellDir returns the position in the directory -- LastPosition will hold the position of the last entry read LastPosition := Position; Position := TellDir( DirInfo ); Put_Line( Head( Direntop.name, Index( Direntop.name, ASCII.NUL & "" )-1 ) ); end loop; New_Line; -- SeekDir: move to last position in directory Result := SeekDir( DirInfo, LastPosition ); Put( "The last position is " ); direntop := To_Pointer( ReadDir( DirInfo ) ); Put_Line( Head( Direntop.name, Index( Direntop.name, ASCII.NUL & "" )-1 ) ); New_Line; -- RewindDir: Start reading again Result := RewindDir( DirInfo ); Put( "The first position is " ); direntop := To_Pointer( ReadDir( DirInfo ) ); Put_Line( Head( Direntop.name, Index( Direntop.name, ASCII.NUL & "" )-1 ) ); New_Line; -- close the directory
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/16.html (24 of 47) [7/20/2001 11:37:15 AM]
This program demonstrates Linux's directory functions The current path (simplified) is /home/ken/ada/trials The current path is /home/ken/ada/trial temp directory created temp directory removed Directory /home/ken/ada contains these files: . .. temp all.zip README posix.zip sm posix cgi tia x rcsinfo.txt text_only original lintel texttools smbeta2.zip trials plugins texttools.zip The last position is texttools.zip The first position is .
pragma import( C, fputs, "fputs" ); Writes a C string to a file. function ferror( fid : AStdioFileID ) return integer; pragma import( C, ferror); Return error from last file operation, if any. procedure clearerr( fid : AStdioFileID ); pragma import( C, clearerr); Clear the error and end of file information. function feof( fid : AStdioFileID ) return integer; pragma import( C, feof); Return non-zero if you are at the end of the file. function fileno( fid : AStdioFileID ) return integer; pragma import( C, fileno); Return the file number for use with Linux file kernel calls. function flock( fd, operation : integer ) return integer; pragma import( C, flock ); Locks or unlocks a file (or a portion of a file). This is for compatibility with other UNIXes--use fcntl instead. Operation: LOCK_SH LOCK_EX LOCK_NB LOCK_UN (1) (2) (4) (8) shared lock exclusive lock no block flag (may be added to others) unlock
type AStdioFileID is new System.Address; -- a pointer to a C standard IO (stdio) file id function popen( command, mode : string ) pragma import( C, popen, "popen" ); -- opens a pipe to command return AStdioFileID;
procedure pclose( result : out integer; fid : AStdioFileID ); pragma import( C, pclose, "pclose" ); pragma import_valued_procedure( pclose); -- closes a pipe function fputc( c : integer; fid : AStdioFileID ) return integer; pragma import( C, fputc, "fputc" ); -- part of standard C library.Writes one charctera to a file. function fputs( s : string; fid : AStdioFileID ) return integer; pragma import( C, fputs, "fputs" ); -- part of standard C library.Writes a string to a file. PipeID : AStdioFileID; -- File ID for lpr pipe procedure BeginPrinting is -- open a pipe to lpr begin Put_Line( "Opening pipe to lpr ..." ); PipeID := popen( "lpr" & ASCII.NUL, "w"& ASCII.NUL); end BeginPrinting; procedure EndPrinting is -- close the pipe.Result doesn't matter. -- Linux normally will not eject a page when -- printing is done, so we'll use a form feed. Result : integer; begin Result := fputc( character'pos( ASCII.FF ), PipeID); pclose( Result, PipeID ); end EndPrinting; --> Input/Output Stuff -------------------------------procedure Print( s : string ) is -- print a string to the pipe, with a carriage -- return and line feed. Result : integer; begin Result := fputs( s & ASCII.CR & ASCII.LF & ASCII.NUL, PipeID ); end Print; begin -- Open the pipe to the lpr command Put_Line( "Starting to print..." ); BeginPrinting; Print( "Sales Report" ); Print( "------------" ); Print( "" ); Print( "Sales were good" ); -- Now, close the pipe. EndPrinting; Put_Line( "Program done...check the printer" ); end printer2;
-- imported C functions TIOGWINSIZ : constant integer := 16#5413#; -- get window size ioctl request type WindowSizeInfo is record row, column, unused1, unused2 : short_integer; end record; pragma pack( WindowSizeInfo ); -- the window size information returned by ioctl type AFileID is new integer; -- a file descriptor, a new integer for safety procedure ioctl_winsz( Result : out integer; fid : AFileID; request : integer; info : in out WindowSizeInfo ); pragma import( C, ioctl_winsz, "ioctl");
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/16.html (28 of 47) [7/20/2001 11:37:15 AM]
pragma import_valued_procedure( ioctl_winsz, "ioctl" ); -- get the size of the window function open( path : string; flags : integer ) return AFileID; pragma import( C, open, "open" ); -- open a file (in this case, the tty) procedure close( fid : AFileID ); pragma import( C, close, "close" ); -- close a file -- The Signal Handler protected body SignalHandler is procedure SizeChangeHandler is -- handle a window size change, SIGWINCH fid: AFileID;-- open's file ID Result : integer; -- function result of ioctl Info: WindowSizeInfo; -- window size returned by ioctl begin fid := Open( "/dev/tty" & ASCII.NUL, 0 ); ioctl_winsz( Result, fid, TIOGWINSIZ, Info ); if Result = 0 then Put_Line( "Window is now " & info.column'img & " x " & info.row'img); else Put_Line( "ioctl reports an error" ); end if; Close( fid ); end SizeChangeHandler; end SignalHandler; end sigwinch;
with Ada.Text_IO, sigwinch; useAda.Text_IO, sigwinch; procedure winch is begin Put_Line( "This program catches SIGWINCH signals"); New_Line; Put_Line( "It will stop running is 60 seconds. If you are using" );
Put_Line( "X Windows, move the window to send signals."); New_Line; delay 60.0; -- run for 60 seconds end winch;
16.14.2 PostgreSQL
Not finished--KB
pragma no_run_time; procedure nrt is -- Same as nrt2 but using no run time type file_id is new integer; -- No Ada.Text_IO possible, so we'll write our own -- that talks directly to the Linux kernel procedure write_char( amount_written : out long_integer; id : file_id; buffer : in out character; amount2write : long_integer ); pragma import( C, write_char, "write" ); pragma import_valued_procedure( write_char, "write" ); procedure put( c : character ) is result : long_integer; buf : character := c; begin write_char( result, 1, buf, 1 ); end put; procedure new_line is begin put( character'val( 10 ) ); end new_line; procedure put_line( s : string ) is pragma suppress( index_check, s ); -- s(i) won't throw a range error, but Gnat checks for it -- by default. Exceptions are a part of the run time. begin for i in s'range loop put( s(i) ); end loop; new_line; end put_line; begin put_line( "Hello World" ); end nrt;
Most distributions have OSS in the kernel by default, but there's no reason that OSS must be present--it can always be turned off for computers without a sound card.
---dataLocation : integer; -dataSize: integer; -dataFormat: integer; -samplingRate : integer; -channelCount : integer; -info1, info2, info3, info4 end record;
a unique number denoting a .au file, as used with the magic file, SND_MAGIC Hex 646E732E (bytes 2E, 73, 6E, 64) offset or pointer to the sound data number of bytes of sound data the data format code the sampling rate the number of channels : character;-- name of sound
dataLocation is an offset to the first byte of the sound data. If there's no sound name, it's 28, the size of the header. It can a pointer to the data, depending on the dataFormat code, but that doesn't apply if you're playing a .au file. dataSize is the size of the sound data in bytes, not including the header. dataFormat describes how the sound data is to be interpreted. Here is a table of some common values. Value 0 1 2 3 4 5 6 7 8 10 11 12 13 14 16 18 19 Code SND_FORMAT_UNSPECIFIED SND_FORMAT_MULAW_8 SND_FORMAT_LINEAR_8 SND_FORMAT_LINEAR_16 SND_FORMAT_LINEAR_24 SND_FORMAT_LINEAR_32 SND_FORMAT_FLOAT SND_FORMAT_DOUBLE SND_FORMAT_INDIRECT SND_FORMAT_DSP_CORE SND_FORMAT_DSP_DATA_8 SND_FORMAT_DSP_DATA_16 SND_FORMAT_DSP_DATA_24 SND_FORMAT_DSP_DATA_32 SND_FORMAT_DISPLAY SND_FORMAT_EMPHASIZED SND_FORMAT_COMPRESSED Format unspecified format 8-bit mu-law samples 8-bit linear samples 16-bit linear samples 24-bit linear samples 32-bit linear samples floating-point samples double-precision float samples fragmented sampled data DSP program 8-bit fixed-point samples 16-bit fixed-point samples 24-bit fixed-point samples 32-bit fixed-point samples non-audio display data 16-bit linear with emphasis 16-bit linear with compression
20 21
SND_FORMAT_COMPRESSED_EMPHASIZED SND_FORMAT_DSP_COMMANDS
SamplingRate is the playback rate in hertz.CD quality samples are 44100. channelCount is 1 for mono, 2 for stereo. The info characters are a C null-terminated string giving a name for the sound. It's always at least 4 characters long, even if unused. In order to play a sound, treat /dev/dsp as if it were a device attached to your computer for playing .au sounds.Write a program to open /dev/dsp for writing and write the .au sound to it. Playing sounds is a natural candidate for multithreading because you don't want your entire program to stop while a sound is being played. The following program uses the seqio generic package we developed above to play an .au sound through /dev/dsp. with seqio; with Ada.Text_IO; use Ada.Text_IO; procedure playsnd is -- simple program to play an .au sound file package byteio is new seqio( short_short_integer ); -- sequential files of bytes au_filename : constant string := "beep.au"; -- sound file to play. supply the name of the .au file to play au_file: byteio.AFileID; -- the sound file dev_dsp: byteio.AFileID; -- /dev/dsp device soundbyte : short_short_integer; begin Put_Line( "Playing " & au_filename & "..."); -- open the files au_file := byteio.Open( au_filename, read => true); dev_dsp := byteio.Open( "/dev/dsp", read => false); -- read until we run out of bytes, send all bytes to -- /dev/dsp.The end of file will cause a seqio_error begin -- nested block to catch the exception loop byteio.Read( au_file, soundbyte ); byteio.Write( dev_dsp, soundbyte ); end loop;
exception when byteio.seqio_error => null; -- just leave block end; -- close files byteio.Close( au_file ); byteio.Close( dev_dsp ); Put_Line( "All done" ); exception when others => Put_Line( "Oh, oh!An exception occurred!" ); byteio.Close( au_file ); byteio.Close( dev_dsp ); raise; end playsnd;
Reading or writing values have a special bit set [Ken check using soundcard.h]. Ioctl calls return an integer value. Volume levels can be 0 to 100, but many sound cards do not offer 100 levels of volume. /dev/mixer will set the volume to setting nearest to your requested volume. [Not complete--KB]
Sound_mixer_volume : constant integer := 0; Sound_Mixer_Read : constant integer := ?; Sound_Mixer_Write : constant integer := ?; oldVolume : integer; ioctlResult := Ioctl( mixer_file_id, sound_mixer_read + sound_mixer_volume, oldVolume ); -- master volume now in oldVolume if ioctlResult = -1 then Put_Line( "No mixer installed " ); end if; newVolume := 75; ioctlResult := ioctl( mixer_file_id, sound_mixer_write + sound_mixer_volume, newVolume ); -- master volume is 75%
AFMT_U16_BE AFMT_MPEG
16#00000100# 16#00000200#
sndctl_dsp_setfmt : constant integer := ?; newFormat : integer; newFormat := 16#0000010#; ioctlResult := ioctl( dsp_id, sndctl_dsp_setfmt, newFormat ); -- recording format now 16 bit signed little endian if newFormat /= 16#00000010 then Put_Line( "Sound card doesn't support recording format" ); end if; Selecting mono or stereo recording is a matter of 0 or 1 respectively. sndctl_dsp_stereo : constant integer := ?; stereo : integer; stereo := 1; ... ioctlResult := ioctl( dsp_id, sndctl_dsp_stereo, stereo ); -- recording format now stereo if stereo /= 1 then Put_Line( "Sound card doesn't support stereo" ); end if; Finally, select a sampling rate. sndctl_dsp_speed : constant integer := ?; newSpeed : integer; newSpeed:= 44100; ioctlResult := ioctl( dsp_id, sndctl_dsp_speed, newFormat ); -- recording now CD quality sampling speed if newSpeed /= 44100 then Put_Line( "Sound card doesn't support sampling speed" ); end if; Now read /dev/dsp for the raw sound data. If you want to save the sound as an .au file, you'll have to create the .au header information to attach to the sound data.
IPC_PRIVATE indicates no key is supplied. function shmget( key : key_t; bytes : integer; shmflag : integer ) return integer; pragma import( C, shmget ); Key is an id you supply to identify the memory (or IPC_PRIVATE for no key). bytes is the minimum amount of memory that you need. shmflag indicates options for this call. Returns -1 on error, or an id for the memory. Example: shmid := shmget( mykey, 4096, IPC_CREAT+IPC_EXCL+8#0660#); function shmat( result : out system.address; shmid : integer; shmaddr : system.address; shmflag : integer ) return system.address; pragma import( C, shmat ); Shared memory attach. Makes shared memory accessible by returning a pointer to it. shmid is the id returned by shmget. if shmaddr isn't zero, the kernel will the address you give instead of chosing one itself. shmflags are additional options. Returns the address of the shared memory, or an address of -1 for an error. Example: shmat( ShmCPtr, myID, To_Address( null ), 0 ); ShmPtr := To_Address( ShmCPtr ); SHM_RDONLY - this memory is read-only (that is, as if it was constant ). SHM_RND - allows your shmaddr to be truncated to a virtual memory page boundary. function shmdt( shmaddr : system.address ) return integer; pragma import( C, shmdt ); Shared memory detach. Removes the association of the shared memory to the pointer. Returns 0 if the memory was detached, -1 for failure. Example: Result := shmdt( To_Address( ShmPtr ) ); function shmctl( shmid : integer; cmd : integer; info : system.address) return integer; pragma import( C, shmctl ); Performs miscellaneous shared memory functions, including deallocating shared memory allocated with shmget. Returns 0 if the function was successful, or -1 for a failure. Example: Result := shmctl( myID, IPC_RMID, To_Address( null) ); IPC_RMID - deallocate shared memory
16.23 Sockets
Send (sentto and sendmsg) are supersets of write. When you use write on a socket, it's actually implemented using the send family. Write will not work on UDP because it's connectionless. Use send to specify an address everytime. Protocol Families PF_INET Internet (IPv4) PF_INET6 Internet (IPv6) PF_IPX Novell PF_NETLINK Kernel user interface device PF_X25 ITU-T X.25 / ISO-8208 PF_AX25 Amateur radio AX.25 PF_ATMPVC Access to raw ATM PVCs PF_APPLETALK Appletalk PF_PACKET Low-level packet interface Socket Types SOCK_STREAM Two-way reliable connection, with possible out-of-band transmission (eg. TCP/IP) SOCK_DGRAM (Datagram) Connectionless, unreliable messages (eg. UDP/IP) SOCK_SEQPACKET Sequenced, reliable datagram connection. SOCK_RAQ Raw network protocol access. SOCK_RDM Reliable, unordered datagrams. function socket( domain, soctype, protocol : integer ) return integer; pragma import( C, socket ); Creates a network socket for protocol family domain, connection type soctype, and a protocol (0 uses the default protocol). Returns -1 on an error, or else a kernel file descriptor for the socket. Example: mySocket := socket( PF_INET, SOCK_STREAM, 0 ); -- open a standard Internet socket procedure connect( result : out integer; socket : integer; addr : in out socketaddr; addrlen : integer ); pragma import( C, connect ); pragma import_valued_procedure( connect ); Connects to a server on the network. socket is the socket to use; addr is the machine and service to connect to; addrlen is the length of the addr record. Returns -1 on an error, 0 for success. Example: connect( result, webserver, webserver'size/8 ); -- connect to the web server described by the webserver record function shutdown( socket, how : integer ) return integer; pragma import( C, shutdown ); Shuts down one or both directions of a socket. This is used, for example, by web browsers to let the server know there are no more HTTP requests being sent. Returns 0 on sucess, -1 on failure. Example: result := shutdown( mysocket, 1 ); procedure bind( result : out integer; myaddr : in out sockaddr, addrlen : integer ); pragma import( C, bind ); pragma import_valued_procedure( bind ); Registers your server on a particular port number with the Linux kernel. addrlen is the length of myaddr. Returns 0 on success, -1 on failure. Example: bind( result, myservice, myservice'size/8 ); function listen( socket : integer; backlog : integer ) return integer; pragma import( C, listen ); pragma import_valued_procedure( listen ); Prepares a socket for your server to listen for incoming connections. Backlog is the maximum number of established
connections that can be queued. Returns 0 on success, -1 on failure. Example: result := listen( mysocket, 10 ); procedure accept( result : out integer; socket : integer; clientaddr : in out sockaddr; addrlen : in out addrlen ); pragma import( C, accept ); pragma import_valued_procuedre( accept ); Returns the next connection to your server. If there are no connections, it waits for a new connection (unless you disabled blocking on the socket.) myaddr is the address of the incoming connection, and addrlen is the size of the address is bytes. addrlen should be initialized to the size of your sockaddr record. You must use listen before accept. Returns -1 on failure, or a new socket with the connection on success. You have to close the new socket when you are finished handling the connection. Example: len := clientaddr'size/8; accept( newsocket, listensocket, clientaddr, len ); This section ends with a demonstration of how to get a web page off the Internet. with Ada.Text_IO, Interfaces.C, System.Address_To_Access_Conversions; use Ada.Text_IO, Interfaces.C; procedure websocket is -- A program to fetch a web page from a server
-- Socket related definitions --- These are the kernel calls and types we need to create -- and use a basic Internet socket. type aSocketFD is new int; -- a socket file descriptor is an integer -- man socket -- make this a new integer for strong typing purposes type aProtocolFamily is new unsigned_short; AF_INET : constant aProtocolFamily := 2; -- Internet protocol PF_Net defined as 2 in -- /usr/src/linux/include/linux/socket.h -- Make this a new integer for strong typing purposes type aSocketType is new int; SOCK_STREAM : constant aSocketType := 1; -- this is for a steady connection. Defined as 1 in -- /usr/src/linux/include/linux/socket.h -- Make this a new integer for strong typing purposes type aNetProtocol is new int; IPPROTO_TCP : constant aNetProtocol := 6; ----The number of the TCP/IP protocol TCP protocol defined as 6 in /etc/protocols See man 5 protocols Make this a new integer for strong typing purposes
type aNetDomain is new integer; PF_INET : constant aNetDomain := 2; -- The number of the Internet domain -- Make this a new integer for strong typing purposes type aInAddr is record addr : unsigned := 0; end record; for aInAddr'size use 96; -- A sockaddr_in record is defined as 16 bytes long (or 96 bits) -- Request Ada to use 16 bytes to represent this record type aSocketAddr is record family : aProtocolFamily := AF_INET; -- protocol (AF_INET for TCP/IP) port : unsigned_short := 0; -- the port number (eg 80 for web) ip : aInAddr; -- IP number end record; -- an Internet socket address -- defined in /usr/src/linux/include/linux/socket.h -- and /usr/src/linux/include/linux/in.h function socket( domain : aNetDomain; stype : aSocketType; protocol : aNetProtocol ) return aSocketFD; pragma import( C, socket ); -- initialize a communication socket. -1 if error procedure bind( result : out int; sockfd : aSocketFD; sa : in out aSocketAddr; addrlen : int ); pragma import( C, bind ); pragma import_valued_procedure( bind ); -- give socket a name. 0 if successful procedure Connect( result : out int; socket : aSocketFD; sa : in out aSocketAddr; addrlen : int ); pragma import( C, connect ); pragma import_valued_procedure( connect ); -- connect to a (Internet) server. 0 if successful procedure Close( fd : aSocketFD ); pragma import( C, close ); -- close the socket, discard the integer result procedure Read( result : out integer; from : aSocketFD; buffer : in out string; buffersize : integer ); pragma import( C, read ); pragma import_valued_procedure( read ); -- read from a socket procedure Write( result : out integer; from : aSocketFD; buffer : system.address; buffersize : integer ); pragma import( C, write );
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/16.html (40 of 47) [7/20/2001 11:37:17 AM]
pragma import_valued_procedure( write ); -- write to a socket package addrListPtrs is new System.Address_To_Access_Conversions( System.Address ); -- We need to use C pointers with the address list because this is -- a pointer to a pointer in C. This will allow us to dereference -- the C pointers in Ada. subtype addrListPtr is System.Address; -- easier to read than System.Address type aHostEnt is h_name h_aliases h_addrtype h_length h_addr_list record : System.Address; : System.Address; : int := 0; : int := 0; : addrListPtr;
-------
pointer to offical name of host pointer to alias list host address type (PF_INET) length of address pointer to list IP addresses we only want first one
end record; -- defined in man gethostbyname package HEptrs is new System.Address_To_Access_Conversions( aHostEnt ); -- Again, we need to work with C pointers here subtype aHEptr is System.Address; -- and this is easier to read use HEptrs; -- use makes = (equals) visible function getHostByName( cname : string ) return aHEptr; pragma import( C, getHostByName ); -- look up a host by it's name, returning the IP number function htons( s : unsigned_short ) return unsigned_short; pragma import( C, htons ); -- acronym: host to network short -- on Intel x86 platforms, -- switches the byte order on a short integer to the network -- Most Significant Byte first standard of the Internet procedure memcpy( dest, src : System.Address; numbytes : int ); pragma import( C, memcpy); -- Copies bytes from one C pointer to another. We could probably -- use unchecked_conversion, but the C examples use this. errno : int; pragma import( C, errno ); -- last error number procedure perror( s : string ); pragma import( C, perror ); -- print the last kernel error and a leading C string procedure PutIPNum( ia : aInAddr ) is -- divide an IP number into bytes and display it IP : unsigned := ia.addr; Byte1, Byte2, Byte3, Byte4 : unsigned;
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/16.html (41 of 47) [7/20/2001 11:37:17 AM]
begin Byte4 := IP mod 256; IP := IP / 256; Byte3 := IP mod 256; IP := IP / 256; Byte2 := IP mod 256; Byte1 := IP / 256; Put( Byte4'img ); Put( "." ); Put( Byte3'img ); Put( "." ); Put( Byte2'img ); Put( "." ); Put( Byte1'img ); end PutIPNum; procedure SendHTTPCommand( soc : aSocketFD; cmd : string ) is -- Write a HTTP command string to the socket amountWritten : integer := 0; totalWritten : integer := 0; position : integer := cmd'first; begin loop Write( amountWritten, soc, cmd( position )'address, cmd'length - integer( totalWritten ) ); if amountWritten < 0 then Put_Line( Standard_Error, "Write to socket failed" ); return; end if; Put_Line( "Sent" & amountWritten'img & " bytes" ); totalWritten := totalWritten + amountWritten; position := position + amountWritten; exit when totalWritten = cmd'length; end loop; end SendHTTPCommand; procedure ShowWebPage( soc : aSocketFD ) is -- Read the web server's response and display it to the screen amountRead : integer := 1; buffer : string( 1..80 ); begin -- continue reading until an error or no more data read -- up to 80 bytes at a time while amountRead > 0 loop Read( amountRead, soc, buffer, buffer'length ); if amountRead > 0 then Put( buffer( 1..amountRead ) ); end if; end loop; end ShowWebPage; ServerName: string := "www.adapower.com"; mySocket myAddress : aSocketFD; : aSocketAddr; -- the socket -- where it goes
: : : :
Put_Line( "Socket Demonstration" ); New_Line; Put_Line( "This program opens a socket to a web server" ); Put_Line( "and retrieves the server's home page" ); New_Line; -- initialize a new TCP/IP socket -- 0 for the third param lets the kernel decide Put_Line( "Initializing a TCP/IP socket" ); Put_Line( "Socket( " & PF_INET'img & ',' & SOCK_STREAM'img & ", 0 );" ); mySocket := Socket( PF_INET, SOCK_STREAM, 0 ); if mySocket = -1 then perror( "Error making socket" & ASCII.NUL ); return; end if; New_Line; -- Lookup the IP number for the server Put_Line( "Looking for information on " & ServerName ); Put_Line( "GetHostByName( " & ServerName & ");" ); myServer := GetHostByName( ServerName & ASCII.NUL ); myServerPtr := HEptrs.To_Pointer( myServer ); if myServerPtr = null then Put_Line( Standard_Error, "Error looking up server" ); return; end if; Put_Line( "IP number is" & myServerPtr.h_length'img & " bytes long" ); addrList := addrlistPtrs.To_Pointer( myServerPtr.h_addr_list ); New_Line; -- Create the IP, port and protocol information Put_Line( "Preparing connection destination information" ); myAddress.family := AF_INET; myAddress.port := htons( 80 ); memcpy( myAddress.ip'address, addrlist.all, myServerPtr.h_length ); New_Line; -- Open a connection to the server Put_Line( "Connect( Result, Socket, Family/Address rec, F/A rec size )" );
Connect( Result, mySocket, myAddress, myAddress'size/8 ); Put( "Connect( " & Result'img & "," ); Put( myAddress.family'img & "/" ); PutIPNum( myAddress.ip ); Put( "," & integer'image( myAddress'size / 8 ) & ")" ); if Result /= 0 then perror( "Error connecting to server" & ASCII.NUL ); return; end if; New_Line; -----Write the request "GET" returns a web page from a web server Also send minimal HTTP header using User-Agent Followed with a blank line to indicate end of command
Put_Line( "Transmitting HTTP command..." ); SendHTTPCommand( mySocket, "GET /index.html HTTP/1.0" & ASCII.CR & ASCII.LF & "User-Agent: WebSocket/1.0 (BigBookLinuxAda Example)" & ASCII.CR & ASCII.LF & ASCII.CR & ASCII.LF ); New_Line; -- read web page back Put_Line( "---Web Page / Server Results-------------------------" ); ShowWebPage( mySocket ); Put_Line( "-----------------------------------------------------" ); -- close the connection Put_Line( "Closing the connection" ); close( mySocket ); Put_Line( "Demonstration finished - have a nice day" ); end websocket;
Socket Demonstration This program opens a socket to a web server and retrieves the server's home page Initializing a TCP/IP socket Socket( 2, 1, 0 ); Looking for information on www.adapower.com GetHostByName( www.adapower.com); IP number is 4 bytes long Preparing connection destination information
Connect( Result, Socket, Family/Address rec, F/A rec size ) Connect( 0, 2/ 216. 92. 66. 46, 16) Transmitting HTTP command... Sent 81 bytes ---Web Page / Server Results------------------------HTTP/1.0 200 OK Date: Wed, 29 Mar 2000 02:32:56 GMT Server: Apache/1.3.3 Last-Modified: Thu, 11 Nov 1999 02:03:14 GMT Etag: "1f31-406-382a23e2" Accept-Ranges: Bytes Content-Length: 1030 Content-Type: text/html Age: 39 Via: HTTP/1.0 csmc2 (Traffic-Server/3.0.3 [uScHs f p eN:t cCHi p s ]) <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> <HTML> <HEAD> <META NAME="author" CONTENT="David Botton">> <META NAME="keywords" CONTENT="Ada AdaPower power source code free treasury repository"> <META NAME="description" CONTENT="The Ada Source Code Treasurey contains components, procedures, algorithms and articles for Ada developers."> <META http-equiv="Page-Enter" CONTENT="revealtrans(duration=2.0, transition=3)"> <TITLE>AdaPower.com</TITLE> <LINK href="mailto:[email protected]" rev="made"> </HEAD> <FRAMESET COLS="120,*" FRAMEBORDER=0 FRAMESPACING=0 BORDER=0> <FRAME SRC="buttons.html" name="menu" frameborder=0 marginheight=0 marginwidth=0 noresize scrolling=auto border=0> <FRAME SRC="http://www.adapower.com/body.html" name="body" frameborder=0 marginheight=5 marginwidth=0 noresize scrolling=auto border=0> <NOFRAMES> <meta HTTP-EQUIV="REFRESH" CONTENT="0; URL="body.html"> <body bgcolor="#ffffff" text="#000000"> <a href="body.html">Click here</a> </BODY> </BODY> </NOFRAMES> </FRAMESET> </HTML> ----------------------------------------------------Closing the connection Demonstration finished - have a nice day
Return the size of a Linux memory page (that is, the size of the memory blocks that your program and data are broken up into when loaded into memory). function mmap( start : system.address; size : long_integer; prot : aProtection; flags : aMapFlag; fd : integer; offset : long_integer ) return system.address; pragma import( C, mmap ); Allocates size bytes of memory and returns a C pointer. If it failed, -1 is returned. If MAP_FIXED and start are used, the memory will be at the specified address. The protection flags indicate how the memory will be accessed: a signal will be raised on an illegal access. If MAP_ANON is used, fd should be -1 and no file will be associated with the memory, otherwise fd is a file that will be copied into the block of memory and offset indicates how many bytes into the file the copying should take place. function munmap( start : system.address; size : long_integer ) return integer; pramga import( C, munmap ); Deallocate memory allocated by mmap. Returns -1 on an error. function mremap( old_start : system.address; old_size : long_integer; new_size : long_integer; flags : aMapFlag) return system.address; pragma import( C, mremap ); Changes the size of a block of memory allocated by mmap, possibly moving it. function mprotect( start : system.address; size : long_integer; new_prot : aProtection ) return integer; pragma import( C, mprotect ); Change the protection settings on a block of memory allocated by mmap. Returns -1 on an error. Other mmap flags: PROT_NONE PROT_READ PROT_WRITE PROT_EXEC MAP_SHARED processes any) MAP_PRIVATE : : : : constant constant constant constant aProtection aProtection aProtection aProtection := := := := 0; 1; 2; 4; ----shorthand for no access read access allowed write access allowed execute access allowed
: constant aMapFlag := 16#02#; constant aMapFlag := 16#10#; constant aMapFlag := 16#20#; constant constant constant constant constant constant aMapFlag aMapFlag aMapFlag aMapFlag aMapFlag aMapFlag := := := := := := MAP_ANON; 16#0100#; 16#0800#; 16#1000#; 16#2000#; 16#4000#;
-----------
separate copy for child processes (changes kept in memory, if any) use specified address just alloc memory, no related another name for MAP_ANON stack-line usage write lock the file mark as executable don't swap out memory don't check for reservations
function msync( start : system.address; size : length; flags : aMSyncFlag ) return integer; pragma import( C, msync ); Updates the file associated with the memory allocated by mmap. Returns -1 on an error.
-- request memory to be saved soon -- mark cache as needing updating -- save memory to file immediately
function mlock( start : system.address; size : long_integer ) return integer; pragma import( C, mlock ); Deny page swapping on this block of memory allocated by mmap. Only a superuser process may lock pages. Returns -1 on an error. function munlock( start : system.address; size : long_integer) return integer; pragma import( C, munlock ); Allow page swapping on this block of memory allocated by mmap. Returns -1 on an error. function mlockall( flags : aLockFlag ) return integer; pragma import( C, mlockall ); Deny swapping on all memory for this process. Only a superuser process can lock memory. Returns -1 on an error. function munlockall return integer; pragma import( C, mlockall ); Allow swapping on all memory for this process. Returns -1 on an error. MCL_CURRENT : constant aLockFlag := 1; -- lock current blocks MCL_FUTURE : constant aLockFlag := 2; -- lock subsequent blocks function brk( end_data_segment : system.address ) return integer; pragma import( C, brk ); Resize the (Intel) data segment to the specified ending address. Returns -1 on an error. procedure sbrk( increment : long_integer ); pragma import( C, sbrk ); Increase the (Intel) data segment by the specified number of bytes.
<--Last Chapter
Table of Contents
Next Chapter-->
17.1 c2ada: Translating Your Programs c2ada is available from http://www.skinner.demon.co.uk/aidan/programming/ 17.2 Interfaces.C package
C Equivalent
C integer int C unsigned integer unsigned char [n] long long C long long
The Interfaces.C and Interfaces.C.Extensions packages provide basic type definitions and conversions functions for C programs. Gnat 3.12 introduces a new boolean type, C_bool, which behaves as a proper C boolean value: 0 is false and any other value is true. One thing to remember about this package is that C strings are defined as an array of characters, and Ada will raise a CONSTRANT_ERROR exception if two arrays of characters are not exactly equal length, even if a smaller array is being assigned to a larger one. For example, s : char_array( 1..80 ) := To_C( "Fred Smith" ); -- bad This example will raise the exception because the string is 11 characters long (10 characters plus a null character), but the array being assigned to is 80 characters. You can get around these kind of errors with dynamic allocation. The following program demonstrates some of the types and functions in the Interfaces.C packages. with text_io, unchecked_deallocation, Interfaces.C.Extensions;
use text_io, Interfaces.C, interfaces.C.Extensions; procedure ctest is -- my types --- pointer to C string and deallocation procedure for same
type stringptr is access all char_array; procedure free is new unchecked_deallocation( char_array, stringptr ); -- types from standard Ada package Interfaces.C i u l ul c sp f d wc : : : : : : : : : int; unsigned; long; unsigned_long; char; stringptr; C_float; double; wchar_t; ---------integer unsigned integer long unsigned long a character ptr to a string (array of characters) a float a double 16-bit wide character
-- additional types from Gnat package Interfaces.C.Extensions ll : long_long; -- long long ull: unsigned_long_long; -- unsigned long long vp : void_ptr; -- void pointer begin Put_Line( "This is an example of Interfaces.C" ); New_Line; sp := new char_array'( To_C( "This is a string" ) ); Put_Line( "The C string s is '" & To_Ada( sp.all ) & "'." ); Free( sp ); end ctest;
package StringPtrs is new Interfaces.C.Pointers( Index => size_t, -- the index range is size_t Element => char, -- the array contains chars Element_Array => char_array, -- the unbounded type Default_Terminator => char'val( 0 ) -- the terminator value, ASCII.NUL ); use StringPtrs; -- need this to make + and - visible strptr : StringPtrs.Pointer; The use clause is very important. Without it, the arithmetic operators would not be directly available because they would be hidden inside the StringPtrs package. Pointers created using Interfaces.C.Pointers are access types, and can be used like any other access type. Put_Line( "strptr is pointing to the character " & strptr.all ); However, unlike other access types, they have new pointer arithmetic features. Addition and subtraction is performed the same way as in C by specifying how many positions in the array to move. To move strptr ahead 2 index positions in a string, add 2 to it: strptr := strptr + 2; Since Ada has no increment or decrement operators, two procedures are provided to move a pointer forward or backward by one array position: Increment( strptr ); -- forward one position Decrement( strptr ); -- back one position The package also makes four additional subprograms available: The Virtual_Length function returns the length of an array, up to end of the array or until a terminator is found. [If no terminator, do you get a storage error?--KB] The Value function returns a slice from the array, from the position of the pointer to the end of the array or until a terminator is found. It can also slice a specific number of elements from an array. Copy_Array copies a slice of a specific number of elements from one pointer to another. Copy_Terminated Array copies a slice from the pointer position until a terminator is found. The following program demonstrates C pointers to integer arrays. with Ada.Text_IO, Interfaces.C.Pointers; use Ada.Text_IO; procedure point is -------To use Interfaces.C.Pointers, you need to define an unbounded array type. In this case, we'll create an unbounded array called IntegerArrays with a maximum index range of 1 to 9. BiggestArray is the largest IntegerArrays array possible, with an index range of 1 to 9. IntegerArrays must have aliased elements because we will be accessing them with an access type.
subtype PointerRange is integer range 1..9; type IntegerArrays is array PointerRange range <> ) of aliased integer;
type BiggestArray is new IntegerArrays( PointerRange ); package IntPtrs is new Interfaces.C.Pointers( Index => PointerRange, -- the index range Element => Integer, -- what the array contains Element_Array => IntegerArrays, -- the unbounded type Default_Terminator => 0 ); -- the terminator value use IntPtrs; -- need this to make + and - visible procedure ShowArray( ia : IntegerArrays ) is -- show the contents of any IntegerArrays array begin for i in ia'first..ia'last-1 loop Put( i'img ); Put( " =>" ); Put( ia( i )'img ); Put( "," ); end loop; Put( ia'last'img ); Put( " => " ); Put_Line( ia( ia'last )'img ); end ShowArray; ia, ia2 : BiggestArray; -- two integer arrays ip, ip2 : IntPtrs.Pointer; -- two pointers to integer arrays begin Put_Line( "This program demonstrates C-style pointers provided" ); Put_Line( "by Interfaces.C.Pointers" ); New_Line; -- initialize and display the contents of the array for i in PointerRange'first..PointerRange'last-1 loop ia( i ) := i*2; end loop; ia( PointerRange'last ) := 0; Put_Line( "The array is: " ); ShowArray( IntegerArrays( ia ) ); -- must typecast ia because ShowArray is expecting an IntegerArrays Put_Line( "Zero is our terminator in this example" ); New_Line; -- set the pointers to the first elements in the arrays
ip := ia( ia'first )'access; ip2 := ia2( ia'first )'access; -- ip works like a normal access type Put_Line( "Our pointer is set to first position in the array"); Put_Line( "The element is " & ip.all'img ); New_Line; -- increment example Increment( ip ); Put_Line( "Incrementing the pointer, it now points at " & ip.all'img ); New_Line; -- decrement example Decrement( ip ); Put_Line( "Decrementing the pointer, it now points at" & ip.all'img ); New_Line; -- addition example ip := ip + 3; Put_Line( "Addition moves the pointer forward." ); Put_Line( "Moving forward three elements, it now points at" & ip.all'img ); New_Line; -- subtraction example ip := ip - 2; Put_Line( "Subtraction moves the pointer backwards." ); Put_Line( "Moving backwards two elements, it now points at" & ip.all'img ); New_Line; -- Virtual_Length examples Put_Line( "Virtual_Length gives the length from the pointer to the" ); Put_Line( "default terminator. The length from this position is" & Virtual_Length( ip )'img & " positions" ); Put_Line( "Virtual_Length can also use an arbitrary terminator." ); Put_Line( "The length from the pointer to the first 14 is" & Virtual_Length( ip, 14 )'img & " positions" ); New_Line; -- Value examples Put_Line( "Value returns the array slice from the pointer position to" ); Put_Line( "the terminator. The array value from this position is" ); ShowArray( Value( ip ) );
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/17.html (5 of 11) [7/20/2001 11:37:39 AM]
Put_Line( "Value can also return a slice of a specific length." ); Put_Line( "The next four elements are" ); ShowArray( Value( ip, Length => 4 ) ); New_Line; -- Copy_Terminated_Array example Put_Line( "Our second array contains" ); ShowArray( IntegerArrays( ia2 ) ); -- must typecast here New_Line; Put_Line( "Copy_Terminated_Array copies elements from one pointer to" ); Put_Line( "another, up to and including the terminator. Copying to" ); Put_Line( "the second array " ); Copy_Terminated_Array( ip, ip2 ); ShowArray( IntegerArrays ( ia2 ) ); -- must typecast here New_Line; -- Copy_Array example Put_Line( "Copy_Array copies a specific number of elements."); Put_Line( "Copying 4 elements from 3 positions ahead, the new"); Put_Line( "array contains" ); Copy_Array( ip+3, ip2, 4 ); ShowArray( IntegerArrays( ia2 ) ); -- must typecast here New_Line; end point;
This program demonstrates C-style pointers provided by Interfaces.C.Pointers The array is: 1 => 2, 2 => 4, 3 => 6, 4 => 8, 5 => 10, 6 => 12, 7 => 14, 8 => 16, 9 => 0 Zero is our terminator in this example Our pointer is set to first position in the array The element is 2 Incrementing the pointer, it now points at 4 Decrementing the pointer, it now points at 2 Addition moves the pointer forward. Moving forward three elements, it now points at 8 Subtraction moves the pointer backwards. Moving backwards two elements, it now points at 4
Virtual_Length gives the length from the pointer to the default terminator. The length from this position is 7 positions Virtual_Length can also use an arbitrary terminator. The length from the pointer to the first 14 is 5 positions Value returns the array slice from the pointer position to the terminator. The array value from this position is 1 => 4, 2 => 6, 3 => 8, 4 => 10, 5 => 12, 6 => 14, 7 => 16, 8 => 0 Value can also return a slice of a specific length. The next four elements are 1 => 4, 2 => 6, 3 => 8, 4 => 10 Our second array contains 1 => 12, 2 => 134531961, 3 => 12, 4 => 1, 5 => 134560412, 6 => 134891560, 7 => 134575980, 8 => 0, 9 => 134560432 Copy_Terminated_Array copies elements from one pointer to another, up to and including the terminator. Copying to the second array 1 => 4, 2 => 6, 3 => 8, 4 => 10, 5 => 12, 6 => 14, 7 => 16, 8 => 0, 9 => 134560432 Copy_Array copies a specific number of elements. Copying 4 elements from 3 positions ahead, the new array contains 1 => 10, 2 => 12, 3 => 14, 4 => 16, 5 => 12, 6 => 14, 7 => 16, 8 => 0, 9 => 134560432
Although the basic Ada types are identical to their C counterparts, the IO libraries are not guaranteed to write data in a format that is readable from other languages. Text files are fine, but to write binary files that can be accessed by C, you'll need to read and write the files using C file handing libraries. The Interfaces.C_Streams package provides a thin binding to the C stdio library. This is comparable to the gnat.os_lib library, but the binding is "thinner" and covers all C stream operations. Some stdio library functions aren't covered because they can't be represented by Ada. Gnat guarantees these functions will be available, no matter what platform gnat is running under, even if it isn't UNIX-based. It is also possible to call stdio directly. See the discussion above. c_streams uses "stream" to refer to a Linux text file. procedure clearerr(stream : FILEs); Clear any error associated with the stream.
function fclose(stream : FILEs) return int; Close a stream. function fdopen(handle : int; mode : chars) return FILEs; Open a stream by a handle (UNIX file descriptor). function feof(stream : FILEs) return int; Check for the end of stream. function ferror(stream : FILEs) return int; Return any error associated with the last stream operation. functionfflush(stream : FILEs) return int; Finish writing any outstanding data to the stream. function fgetc(stream : FILEs) return int; Read one character from the stream. Characters will be ASCII values between 0 and 255, and can be converted to a character with character'val. function fgets(strng : chars; n : int; stream : FILEs) return chars; Read a string from the stream. Note this is not an Ada string. function fileno(stream : FILEs) return int; Return the fine number associated with a stream for use with standard Linux file operations. function fopen( filename : chars; Mode : chars) return FILEs; Open a stream. function fputc(C : int; stream : FILEs) return int; Write one character to a stream. Convert the character to an integer using character'val. function fputs(Strng : chars; Stream : FILEs) return int; Write a string of characters to the stream. function fread( buffer : voids; size : size_t; count : size_t; stream : FILEs) return size_t; Read count bytes into a buffer of length size and return number of bytes actually read. function freopen( filename : chars; mode : chars; stream : FILEs) return FILEs; Reopen the stream with a new mode. function fseek( stream : FILEs; offset : long; origin : int) return int; Move offset bytes from the specified origin point. function ftell(stream : FILEs) return long; Get stream offset for fseek. function fwrite( buffer : voids; size : size_t; count : size_t; stream : FILEs) return size_t; Write count bytes from a buffer of count length and return the number of bytes actually written. function isatty(handle : int) return int; [NQS--determine if stream is a TTY device?--KB] procedure mktemp(template : chars); Create a random name for a temporary file. procedure rewind(stream : FILEs); Move to the start of the stream. function setvbuf(stream : FILEs; buffer : chars; mode : int; size : size_t) return int;
[NQS--used to know what this did--KB] procedure tmpnam(string : chars); [Difference with tmpnam?--KB] The parameter must be a pointer to a string buffer of at least L_tmpnam bytes (the call with a null parameter is not supported). function tmpfile return FILEs; [NQS--KB] function ungetc(c : int; stream : FILEs) return int; Back up one character in the stream. function unlink(filename : chars) return int; Delete a stream file. The following are related utility functions added by ACT. They are not standard UNIX functions like the above. function file_exists(name : chars) return int; Returns 0 if a file doesn't exist, 1 if it does. function is_regular_file(handle : int) return int; Return 1 if given handle is for a regular file, or 0 for some other kind of file. procedure set_binary_mode( handle : int); Read text without translation. Only works if compiled with text_translation_required. procedure set_text_mode( handle : int); Translate text. Only works if compiled with text_translation_required. procedure full_name(nam : chars; buffer : chars); Return the full path of a file as a C string. The following program demonstrates some of the c_stream functions. with text_io, unchecked_deallocation, Interfaces.C_Streams; use text_io, Interfaces.C_Streams; procedure cstreamtest is fd : FILEs; line2write : constant string := "This is a test"; cline2write: constant string := "This is a test" & ASCII.NUL; path : constant string := "testfile.xxx"; cpath : constant string := path & ASCII.NUL; fileMode : constant string := "w"; amountWritten : size_t; result : int; begin Put_Line( "This is an example of Interfaces.C_Streams" ); New_Line; fd := fopen( cpath'address, fileMode'address ); if ferror( fd ) = 0 then Put_Line( "Opened " & path & " with fopen"); Put_Line( "Writing '" & line2write & "' with fwrite" );
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/17.html (9 of 11) [7/20/2001 11:37:39 AM]
amountWritten := fwrite( line2write'address, -- what to write 1, -- size of elements line2write'length, -- how many to write fd ); -- to which file Put_Line( New_Line; Put_Line( result := Put_Line( New_Line; "Wrote" & amountWritten'img & " characters" ); "Writing with fputs" ); fputs( cline2write'address, fd ); "Result was" & result'img );
result := fclose( fd ); Put_Line( "Closed " & path & " with fclose"); Put_Line( "Result was" & result'img ); New_Line; result := unlink( cpath'address ); Put_Line( "Deleted " & path & " with unlink"); Put_Line( "Result was" & result'img ); end if; end cstreamtest;
This is an example of Interfaces.C_Streams Opened testfile.xxx with fopen Writing 'This is a test' with fwrite Wrote 14 characters Writing with fputs Result was 1 Closed testfile.xxx with fclose Result was 0 Deleted testfile.xxx with unlink Result was 0
Gnat 3.12 introduces a proper Fortran logical type that behaves according to Fortran semantics. Fortran subprograms may be imported into Ada using pragma import: procedure MyFortranSubroutine; pragma import( Fortran, MyFortranSubroutine ); Variables may be likewise imported. RealVar : float; pragma import( Fortran, RealVar ); g77 adds an undescore to subroutine names, so ifyou are importing from g77 you'll need to include the name of the subroutine with a trailing underscore in pragma import. pragma import (Fortran, MyFortranSubroutine, "MyFortranSubroutine_");
<--Last Chapter
Table of Contents
Next Chapter-->
18 Data Structures
<--Last Chapter Table of Contents Next Chapter-->
Good programmers write good programs. Great programmers write good programs and good data structures. Organizing your data is as important as the program that crunches the data and produces a result. Unfortunately, my experiences in the corporate world have taught me that that the only data structure used is the single dimensional array. When results are the only goal and more processing power is the cure for bad software design, arrays are easy to implement (they are built into Ada). Even the worst programmer knows how to use an array. And arrays are easy to understand. Try to use a linked list, and a programmer can get into trouble with his boss for using risky, "advanced" technology. Alternatively, programmers will sometimes rely on the complexity and overhead of databases when a simplier solution using the correct data structure would be faster and easier to implement. If you are lucky enough to work for a company that uses more than arrays, this chapter will discuss how to use other kinds of data structures in Ada.
Preallocate - Increase the size of the collection immediately Chunk_Size - Returns the current chunk size
The Booch components are organzied in a hierarchy of packages. The BC package is the top-most package. BC defines the basic execptions that can be raised by the various components: Container_Error : exception; Duplicate : exception; Illegal_Pattern : exception; Is_Null : exception; Lexical_Error : exception; Math_Error : exception; Not_Found : exception; Not_Null : exception; Not_Root : exception; Overflow : exception; Range_Error : exception; Storage_Error : exception; Synchronization_Error : exception; Underflow : exception; Should_Have_Been_Overridden : exception; Not_Yet_Implemented : exception; The data structure components are: Data Structure Booch Packages Description Unordered collection of items. Duplicates are counted but not actually stored. Ordered collection of items. Duplicates are allowed and stored. Double-ended queues A sequence of 0 or more items with a head and a pointer to each successive item. A sequence of 0 or more items with a head and a pointer to both successive and previous items.
Bags
Collections
Deques
bc-containers-lists-double
Maps
bc-containers-maps-bounded bc-containers-maps-dynamic bc-containers-maps-unbounded bc-containers-queues-bounded bc-containers-queues-dynamic bc-containers-queues-unbounded bc-containers-queues-ordered-bounded bc-containers-queues-ordered-dynamic bc-containers-queues-ordered-unbounded bc-containers-rings-bounded bc-containers-rings-dynamic bc-containers-rings-unbounded bc-containers-sets-bounded bc-containers-sets-dynamic bc-containers-sets-unbounded bc-containers-stacks-bounded bc-containers-stacks-dynamic bc-containers-stacks-unbounded bc-containers-trees-avl
A set with relationships between pairs of items. First in, first out list. A sorted list, items removed from the front. A deque with only one endpoint. Unordered collection of items. Duplicates are not allowed. Last in, first out list. Balanced binary trees A list with two successors per item. Tree with an arbitrary number of children. Groups of items with one-way relationships Groups of items with two-way relationships Access types that automatically deallocate themselves
Sets
bc-containers-trees-binary-in_order Binary Trees bc-containers-trees-binary-post_order bc-containers-trees-binary-pre_order Multiway Trees Directed Graphs Undirected Graphs bc-containers-trees-multiway-post_order bc-containers-trees-multiway-pre_order bc-graphs-directed
bc-graphs-undirected
Smart Pointers
bc-smart
A definition of common data structures can be found at the National Institute of Standards and Technology. The components are generic packages and must be instantiated for a particular type. They are arranged in hierarchies of generic packages. Each parent package must be instantiated before its child. For example, to use single linked lists (bc.containers.lists.single), bc.containers, bc.containers.lists, and bc.containers.lists.single must all be be created for the item type. As with many component libraries, the Booch components represent all structures in memory, not in long-term storage. They cannot be used to create disk files, although the data could be saved to disk and reloaded later. 18.1.1 Containers
Containers form the cornerstone of the Booch components. Containers are a controlled tagged record that encloses an item. The Booch components are composed of items stored in containers that are arranged in different ways. To use any of the Booch components, a container must be instantiated to hold your item. For example, to create a new package to manage character in containers, use package charContainers is new BC.Containers (Item => Character); 18.1.2 Iterators The Container package also manages the iterators used by the Booch components. An iterator is a variable that keeps track of the position in a data structure during a traversal. Iterators are created by New_Iterator in a data structure's package, but the subprograms that work with the iterator are defined in the Container package. q Reset - start a new traversal at the first item q Next - continue to another item in the component q Is_Done - True if there are no more items q Current_Item - return the current item The Is_Done function indicates when all items have been traversed. When Is_Done is true, Current_Item is undefined. In other words, the program must loop through all items in the list, plus 1, before Is_Done is true. Because an Iterator is a class-wide type, it must be assigned a new value when it is declared to avoid a compiler error. i : charContainers.Iterator'class := charList.New_Iterator( customers ); 18.1.3 Single linked Lists Creating a single linked list requires the instantiation of 3 separate generic packages: BC.Containers, BC.Containers.Lists, and BC.Containers.Lists.Single. To avoid problems with access types, these should be declared globally (that is, in a package spec). First, a container must be defined to hold the item you want to store in your linked list. package Containers is new BC.Containers (Item => Character); Second, basic operations on lists must be instantiated. package Lists is new Containers.Lists; Finally, the single linked list package must be instantiated. For an unbounded package, you chose a storage pool to use. Single linked lists are always unbounded. Use Global_Heap if you have no preference. package LS is new Lists.Single (Storage_Manager => Global_Heap.Pool, Storage => Global_Heap.Storage); The single linked list package provides the following subprograms: q Clear - destroy the list q Insert - add an item to the list q Append - add an item to the end of the list q Remove - remove an item from the list q Purge - remove consecutive items
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/18.html (4 of 26) [7/20/2001 11:38:16 AM]
Preserve - inverse of Purge, keep consecutive items, removing the rest Share/_Head/_Foot - make the list an alias for a sublist in another list Tail - discard everything but the last item in the list Length - return the number of items in the list Is_Null - true if the list has no items Is_Shared - true if there an alias to a sublist in the list Head - return the top-most item Foot - return the last item in the list Item_at - return an item at a given position New_Iterator - return an iterator for traversing the list Process_Head/Tail - generic procedure to return an item with processing Swap_Tail - NQS-KB
Notice that the term "Foot" refers to the last item in the list. The Ada string packages uses the term "Tail". Here's an example: with BC.Containers.Lists.Single; with Global_Heap; package customers is type aCustomer is record customerID : integer; accountBalance : float; end record; -- this is the item to put in the list package customerContainers is new BC.Containers (Item => aCustomer); -- create a new controlled tagged record container for customers package customerLists is new customerContainers.Lists; -- create a new list support package for our using container type package customerList is new customerLists.Single (Storage_Manager => Global_Heap.Pool, Storage => Global_Heap.Storage); -- create a single linked list package using the lists support -- customized for our container type end customers;
with ada.text_io, BC, customers; use ada.text_io, BC, customers; procedure list_demo is customers : customerList.Single_List; c : aCustomer; i : customerContainers.Iterator'class := customerList.New_Iterator( customers ); begin Put_Line( "This is a demo of the Booch components: single-linked lists" );
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/18.html (5 of 26) [7/20/2001 11:38:17 AM]
New_Line; -- The Newly Declared List Put_Line( Put_Line( Put_Line( Put_Line( New_Line; "The "The "The "The list list list list is newly declared." ); is empty? " & customerList.Is_Null( customers )'img ); is shared? " & customerList.Is_Shared( customers )'img ); length is" & customerList.Length( customers )'img );
-- Inserting a customer c.customerID := 7456; c.accountBalance := 56.74; customerList.Insert( customers, c ); Put_Line( Put_Line( Put_Line( Put_Line( "Added customer" & c.customerID'img ); "The list is empty? " & customerList.Is_Null( customers )'img ); "The list is shared? " & customerList.Is_Shared( customers )'img ); "The list length is" & customerList.Length( customers )'img );
c := customerList.Head( customers ); Put_Line( "The head item is customer id" & c.customerID'img ); c := customerList.Foot( customers ); Put_Line( "The foot item is customer id" & c.customerID'img ); New_Line; -- Apending a customer c.customerID := 9362; c.accountBalance := 88.92; customerList.Append( customers, c ); Put_Line( "Appended customer" & c.customerID'img ); Put_Line( "The list length is" & customerList.Length( customers )'img ); c := customerList.Head( customers ); Put_Line( "The head item is customer id" & c.customerID'img ); c := customerList.Foot( customers ); Put_Line( "The foot item is customer id" & c.customerID'img ); New_Line; -- Iterator example Put_Line( "Resetting the iterator.." ); customerContainers.Reset( i ); c := customerContainers.Current_item ( i ); Put_Line( "The current item is customer id" & c.customerID'img ); Put_Line( "Are we done? " & customerContainers.Is_Done( i )'img ); Put_Line( "Advancing to the next item..." ); customerContainers.Next( i ); c := customerContainers.Current_item ( i ); Put_Line( "The current item is customer id" & c.customerID'img ); Put_Line( "Are we done? " & customerContainers.Is_Done( i )'img );
Put_Line( "Advancing to the next item..." ); customerContainers.Next( i ); Put_Line( "Are we done? " & customerContainers.Is_Done( i )'img ); begin c := customerContainers.Current_item ( i ); exception when BC.NOT_FOUND => put_line( "BC.NOT_FOUND exception: no item at this position in the list" ); end; end list_demo;
This is a demo of the Booch components: single-linked lists The The The The list list list list is newly declared. is empty? TRUE is shared? FALSE length is 0
Added customer 7456 The list is empty? FALSE The list is shared? FALSE The list length is 1 The head item is customer id 7456 The foot item is customer id 7456 Appended The list The head The foot customer 9362 length is 2 item is customer id 7456 item is customer id 9362
Resetting the iterator.. The current item is customer id 7456 Are we done? FALSE Advancing to the next item... The current item is customer id 9362 Are we done? FALSE Advancing to the next item... Are we done? TRUE BC.NOT_FOUND exception: no item at this position in the list Single linked lists should not be Guarded. 18.1.4 Double linked Lists Double linked lists are implemented exactly the same as single-linked lists except that the word "Double" is substituted for the word "Single". Double linked lists are useful for lists that must be browsed backwards and forwards continuously. Double linked lists should not be Guarded. 18.1.5 Bags Bags, like linked lists, are collections of items. However, there is no attempt to order the items. Duplicate items can be stored, but the bag keeps a count of duplications to save memory instead of storing copies of the duplicates.
The bags package provides the following subprograms: q Are_Equal - True if two bags have the same contents q Clear - Removes all items from the bag q Add - Adds an item to the bag q Remove - Removes an item from the bag q Union - Add one bag to another q Intersection - Remove all items not common between two bags. Where there are duplicates, keep the lower duplicate count q Difference - Remove all items not common between two bags. Where there are duplicates, subtract from the original and discard the item if the total is >= 0 q Extent - Return the number of distinct items q Total_Size - Return the total number of items (including duplicates) q Count - Return the number of occurences of an item in the bag q Is_Empty - True if the bag is empty q Is_Member - True if one or more copies of an item is in the bag q Is_Subset - True if the contents of one bag is completely contained in another q Is_Proper_Subset - Same as Is_Subset, but the bags must not be equal Bags can be bounded, dynamic or unbounded. Bags are implemented using a hash table. To declare a bag, a program must provide a hash function for storing items in the bag, and must indicate the size of the hash table. Here's an example. Notice that some of the subprograms are in the Bags instantiation, and some in the Bags.Unbounded instantiation. Also notice the iterator moves over the items, but not the duplications:
with BC.Containers.Bags.Unbounded; with Global_Heap; package customers is type aCustomerID is new integer range 1_000..9_999; function IDHash( id : aCustomerID ) return Positive; -- our hash function package customerContainers is new BC.Containers (Item => aCustomerID); -- create a new controlled tagged record container for customers package customerBags is new customerContainers.Bags; -- create a new bag support for our using container type package customerBag is new customerBags.Unbounded( Hash => IDHash, Buckets => 99, Storage_Manager => Global_Heap.Pool, Storage => Global_Heap.Storage); -- create an unbounded bag package holding customer numbers end customers;
package body customers is function IDHash( id : aCustomerID ) return Positive is -- our hash function begin return Positive( id ); -- in this case, using the id is good enough end IDHash;
end customers;
with ada.text_io, BC, customers; use ada.text_io, BC, customers; procedure bag_demo is customers : customerBag.Unbounded_Bag; c : aCustomerID; i : customerContainers.Iterator'class := customerBag.New_Iterator( customers ); begin Put_Line( "This is a demo of the Booch components: bags" ); New_Line; -- The Newly Declared Bag Put_Line( Put_Line( Put_Line( Put_Line( New_Line; "The "The "The "The bag bag bag bag is newly declared." ); is empty? " & customerBag.Is_Empty( customers )'img ); extent is" & customerBag.Extent( customers )'img ); total size is" & customerBags.Total_Size( customers )'img );
-- Inserting a customer c := 7456; customerBags.Add( customers, c ); Put_Line( "Added customer" & c'img ); Put_Line( "The bag is empty? " & customerBag.Is_Empty( customers )'img ); Put_Line( "The bag extent is" & customerBag.Extent( customers )'img ); New_Line; -- Inserting another customer c := 9362; customerBags.Add( customers, c ); Put_Line( Put_Line( Put_Line( Put_Line( New_Line; "Added customer" & c'img ); "The bag is empty? " & customerBag.Is_Empty( customers )'img ); "The bag extent is" & customerBag.Extent( customers )'img ); "The bag total size is" & customerBags.Total_Size( customers )'img );
c := 9362; customerBags.Add( customers, c ); Put_Line( Put_Line( Put_Line( Put_Line( New_Line; "Added customer" & c'img ); "The bag is empty? " & customerBag.Is_Empty( customers )'img ); "The bag extent is" & customerBag.Extent( customers )'img ); "The bag total size is" & customerBags.Total_Size( customers )'img );
-- Iterator example Put_Line( "Resetting the iterator.." ); customerContainers.Reset( i ); c := customerContainers.Current_item ( i ); Put_Line( "The current item is customer id" & c'img ); Put_Line( "Are we done? " & customerContainers.Is_Done( i )'img ); Put_Line( "Advancing to the next item..." ); customerContainers.Next( i ); c := customerContainers.Current_item ( i ); Put_Line( "The current item is customer id" & c'img ); Put_Line( "Are we done? " & customerContainers.Is_Done( i )'img ); Put_Line( "Advancing to the next item..." ); customerContainers.Next( i ); Put_Line( "Are we done? " & customerContainers.Is_Done( i )'img ); begin c := customerContainers.Current_item ( i ); exception when BC.NOT_FOUND => put_line( "BC.NOT_FOUND exception: no item at this position in the bag" ); end; end bag_demo;
This is a demo of the Booch components: bags The The The The bag bag bag bag is newly declared. is empty? TRUE extent is 0 total size is 0
Added customer 7456 The bag is empty? FALSE The bag extent is 1 Added customer 9362 The bag is empty? FALSE The bag extent is 2 The bag total size is 2 Added customer 9362 The bag is empty? FALSE The bag extent is 2
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/18.html (10 of 26) [7/20/2001 11:38:17 AM]
The bag total size is 3 Resetting the iterator.. The current item is customer id 7456 Are we done? FALSE Advancing to the next item... The current item is customer id 9362 Are we done? FALSE Advancing to the next item... Are we done? TRUE BC.NOT_FOUND exception: no item at this position in the bag Bags are useful for counting the occurrences of an item in a large amount of data. 18.1.6 Sets Sets are essentially the same as bags but may not contain duplicates. The are useful for detecting the presence/absence of an item, or representing flags or conditions. with BC.Containers.Sets.Bounded; with Global_Heap; package fruit_sets is -- my grandfather owned one of the largest fruit companies in the world type aFruit is ( Apples, Grapes, Peaches, Cherries, Pears, Plums, Other );
function FruitHash( f : aFruit ) return Positive; -- our hash function for the set package fruitContainers is new BC.Containers( item=> aFruit ); -- basic fruit container package fruitSets is new fruitContainers.Sets; -- basic set support package fruitBoundedSets is new fruitSets.Bounded( fruitHash, Buckets => 10, Size => 20 ); -- our actual set is an unbounded set end fruit_sets;
package body fruit_sets is function FruitHash( f : aFruit ) return Positive is begin return aFruit'pos( f )+1; -- good enough for this example end FruitHash; end fruit_sets;
with ada.text_io, kb_sets; use ada.text_io, kb_sets; procedure set_demo is use fruitSets; use fruitBoundedSets; s1 : Bounded_Set; s2 : Bounded_Set; s3 : Bounded_Set; begin Put_Line( "This is a demo of the Booch components: sets" ); New_Line; Add( Add( Add( Add( Add( s1, s1, s2, s2, s2, apples ); peaches ); apples ); peaches ); pears ); "Set 1 has apples and peaches." ); "Set 2 has apples, peaches and pears." ); "Extent of set 1? " & Extent( s1 )'img ); "Extent of set 2? " & Extent( s2 )'img ); "Peaches in set 1? " & Is_Member( s1, peaches )'img ); "Pears in set 1? " & Is_Member( s1, pears )'img ); "Set 1 a subset of set 2? " & Is_Subset( s1, s2 )'img ); "Set 2 a subset of set 1? " & Is_Subset( s2, s1 )'img ); "Set 1 a subset of set 1? " & Is_Subset( s1, s1 )'img ); "Set 1 a proper subset of set 1? " & Is_Proper_Subset( s1, s1 )'img );
Put_Line( Put_Line( New_Line; Put_Line( Put_Line( Put_Line( Put_Line( Put_Line( Put_Line( Put_Line( Put_Line( New_Line;
s3 := s1; Union( s3, s2 ); Put_Line( "Set 3 is the union of set 1 and set 2" ); Put_Line( "Extent of set 3? " & Extent( s3 )'img ); end set_demo;
This is a demo of the Booch components: sets Set 1 has apples and peaches. Set 2 has apples, peaches and pears. Extent of set 1? 2 Extent of set 2? 3 Peaches in set 1? TRUE Pears in set 1? FALSE Set 1 a subset of set 2? Set 2 a subset of set 1? Set 1 a subset of set 1? Set 1 a proper subset of
18.1.7 Collections Collections are a (conceptually) combination of lists and bags. Duplicates actually exist as copies in the collection, not simply counted. Collections are also indexed, like a list, so that items can be referenced in the collection. The Collections package provides the following subprograms: q Create - Create a new collection and its initial chunk q Clear - Remove all items from a collection q Insert - Add an item in front of another q Append - Add an item to the end of the collection q Remove - Remove an item at an index q Replace - Replace an item at an index q Length - Return the number of items in the collection q Is_Empty - True if there are no items in the collection q First - Return the item at the front of the collection q Last - Return the item at the end of the collection q Item_At - Return the item at a particular index q Location - Return the first index where an item is found (0 if Collections are implemented as dynamically allocated arrays. with BC.Containers.Collections.Dynamic; with Global_Heap; package products is type aProduct is record id : integer; weight : float; end record; package productContainers is new BC.Containers (Item => aProduct); -- this is the basic container package productCollections is new productContainers.Collections; -- create a new collection support for our using container type package productCollection is new productCollections.dynamic( Storage_Manager => Global_Heap.Pool, Storage => Global_Heap.Storage); -- create a dynamic collection holding products end products;
with ada.text_io, BC, products; use ada.text_io, BC, products; procedure collection_demo is products : productCollection.Dynamic_Collection; p : aProduct; i : productContainers.Iterator'class := productCollection.New_Iterator(
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/18.html (13 of 26) [7/20/2001 11:38:17 AM]
products ); begin Put_Line( "This is a demo of the Booch components: collections" ); New_Line; products := productCollection.Create( 100 ); -- The Newly Declared Collection Put_Line( "The collection is newly declared with a chunk size of 100..." ); Put_Line( "The collection is empty? " & productCollection.Is_Empty( products )'img ); Put_Line( "The collection length is" & productCollection.Length( products )'img ); Put_Line( "The collection chunk size is" & productCollection.Chunk_Size( products )'img ); New_Line; -- Adding an Item p.id := 8301; p.weight := 17.0; productCollection.Append( products, p ); Put_Line( "Product id" & p.id'img & " was added..." ); Put_Line( "The collection is empty? " & productCollection.Is_Empty( products )'img ); Put_Line( "The collection length is" & productCollection.Length( products )'img ); Put_Line( "The collection chunk size is" & productCollection.Chunk_Size( products )'img ); p := productCollection.First( products ); Put_Line( "The first item is" & p.id'img ); p := productCollection.Last( products ); Put_Line( "The last item is" & p.id'img ); New_Line; -- Adding another Item p.id := 1732; p.weight := 27.0; productCollection.Append( products, p ); Put_Line( "Product id" & p.id'img & " was added..." ); Put_Line( "The collection is empty? " & productCollection.Is_Empty( products )'img ); Put_Line( "The collection length is" & productCollection.Length( products )'img ); Put_Line( "The collection chunk size is" & productCollection.Chunk_Size( products )'img ); p := productCollection.First( products ); Put_Line( "The first item is" & p.id'img ); p := productCollection.Last( products ); Put_Line( "The last item is" & p.id'img ); New_Line; -- Changing the Chunk Size productCollection.Set_Chunk_Size( products, Size => 1 );
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/18.html (14 of 26) [7/20/2001 11:38:17 AM]
Put_Line( "The chunk size was reduced to only 1..." ); Put_Line( "The collection is empty? " & productCollection.Is_Empty( products )'img ); Put_Line( "The collection length is" & productCollection.Length( products )'img ); Put_Line( "The collection chunk size is" & productCollection.Chunk_Size( products )'img ); p := productCollection.First( products ); Put_Line( "The first item is" & p.id'img ); p := productCollection.Last( products ); Put_Line( "The last item is" & p.id'img ); New_Line; -- Iterator example Put_Line( "Resetting the iterator.." ); productContainers.Reset( i ); p := productContainers.Current_item ( i ); Put_Line( "The current item is customer id" & p.id'img ); Put_Line( "Are we done? " & productContainers.Is_Done( i )'img ); Put_Line( "Advancing to the next item..." ); productContainers.Next( i ); p := productContainers.Current_item ( i ); Put_Line( "The current item is customer id" & p.id'img ); Put_Line( "Are we done? " & productContainers.Is_Done( i )'img ); Put_Line( "Advancing to the next item..." ); productContainers.Next( i ); Put_Line( "Are we done? " & productContainers.Is_Done( i )'img ); begin p := productContainers.Current_item ( i ); exception when BC.NOT_FOUND => put_line( "BC.NOT_FOUND exception: no item at this position in the collection" ); end; Collections are suitable for small lists or lists where the upper bound is known or rarely exceeded.
This is a demo of the Booch components: collections The The The The collection collection collection collection is newly declared with a chunk size of 100... is empty? TRUE length is 0 chunk size is 100
Product id 8301 was added... The collection is empty? FALSE The collection length is 1 The collection chunk size is 100 The first item is 8301 The last item is 8301 Product id 1732 was added... The collection is empty? FALSE
The The The The The The The The The The
collection length is 2 collection chunk size is 100 first item is 8301 last item is 1732 chunk size was reduced to only 1... collection is empty? FALSE collection length is 2 collection chunk size is 1 first item is 8301 last item is 1732
Resetting the iterator.. The current item is customer id 8301 Are we done? FALSE Advancing to the next item... The current item is customer id 1732 Are we done? FALSE Advancing to the next item... Are we done? TRUE BC.NOT_FOUND exception: no item at this position in the collection 18.1.8 Queues Queues are a list in which items are removed in the same order they are added. Items are added at the end of the queue and removed at the front. An ordered (or "priority") queue is a queue in which added items are sorted. The queues package provides the following subprograms: q Clear - Remove all items from the queue q Append - Add an item to the back of the queue q Pop - Remove an item from the front of the queue and return it q Remove - Remove an item at a particular index q Length - Return the number of items in the queue q Is_Empty - True if there are no items in the queue q Front - Return the item at the front of the queue without removing it q Process - generic procedure to return an item with processing q Location - Return the first index where an item appears else 0 q Are_Equal - True if two queues have the same items and length q Copy - Copy one queue to another An ordered queue is identical except that append adds an item in sorted order. Queues can be bounded, dynamic or unbounded. Queues provide "fair" processing and reduce starvation. 18.1.9 Stacks Stacks are lists in which the last item placed in the list is the first item removed. The Stacks package provides the following subprograms: q Clear - Remove all items from the stack
Push - Add an item to the top of the queue Pop - Remove an item from the top of the stack and return it Depth - Return the number of items in the stack Is_Empty - True if there are no items in the stack Top - Return the item at the top of the stack without removing it Process_Top - generic procedure to return an item with processing Are_Equal - True if two stacks have the same items and length Copy - Copy one stack to another
Stacks can be bounded, dynamic or unbounded. Stacks are used for temporary storage, compact representation and fast data access. 18.1.10 Deques Deques (double-ended queues, pronounced "deck") are a combination of a stack and queue where items can be placed at the front or the back and removed from either the front or the back. The Deques package provides the following subprograms: q Clear - Remove all items from the deque q Append - Add an item to the deque q Pop - Remove an item from the deque and return it q Remove - Remove an item at a particular index q Length - Return the number of items in the deque q Is_Empty - True if there are no items in the deque q Front - Return the item at the front of the deque without removing q Back - Return the item at the back of the deque without removing it q Process_Front/_Back - generic procedure to return an item with processing q Location - Return the first index where an item appears else 0 q Are_Equal - True if two deques have the same items and length q Copy - Copy one deque to another Deques can be bounded, dynamic or unbounded. 18.1.11 Rings Rings are similar to deques, but rings have no "front" or "back", only a moving point of reference called "top". In addition to the deque subprograms, rings include "Mark" to mark a point in the ring, "Rotate_To_Mark" to move the ring to the marked position, and "At_Mark" to test to see if the top of the ring is at the mark. Rings can be bounded or dynamic. 18.1.12 Maps Maps are ordered pairs of related items. Each item is related to a "value" which may or may not be the same type. Maps relate items to values by "binding" them. The Maps package provides the following subprograms: q Clear - destroy a map q Bind - relate an item to a value q Rebind - relate an item to a different value q Unbind - remove the relationship between an item and its value
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/18.html (17 of 26) [7/20/2001 11:38:18 AM]
Extent - return the number of relationships Is_Empty - true if there are no relationships Is_Bound - true if the item is related to a value Value_Of - the value an item is related to Visit - a "read-only" procedure to traverse the map Modify - a procedure that traverses the map making changes
Maps are implemented with a hash table and caching. Maps can be bounded, dynamic, unbounded or synchronized. Maps are useful as translation tables. 18.1.13 Binary Trees Binary trees are lists with two successors instead of 1, named "left" and "right". The items in the tree are not sorted by the Booch component. The program has full control on how items are added to the tree. Programs "walk" the tree by moving the root of the tree up and down the links to the items. Left_Child follows the left child link. Right_Child follows the right child link. Parent follows the parent link. Each of these subprograms can be used as a procedure (to move the root of the tree) or as a function (to examine the item the link connects to). item := Item_At( Put( "Left child item := Item_At( Put_Line( " is " tree ); of " & item ) ; Left_Child( tree ) ); & item ) ;
When the root of the tree is moved, any items above the new root that aren't referenced anymore are destroyed. To move around the tree without destroying nodes (which is typically what you want to do), create an "alias" to the root of the tree with Create prior to moving. root := Create( tree ); -- create a reference to the root Left_Child( tree ); -- safe: old root is not destroyed Moving into an empty (null) position in the tree is allowed, but any attempt to look at the item there will raise an exception. The leaves and the parent of the root are empty. The Trees.Binary package provides the following subprograms: q Clear - Destroy the tree q Insert - Insert an item at the tree's root q Append - Add an item in the place of a particular item, moving the old item to a new position q Remove - Remove an item from the tree q Share - Create an alias to a subtree of the tree q Child/Left_Child/Right_Child - Move to a child item q Parent - Move towards the root q Set_Item - Make an item the root of the tree q Has_Children - True if the tree has any children items q Is_Null - True if the tree has no items q Is_Shared - True if any subtree has an alias to it q Is_Root - True if the tree is at the root of tree q Item_At - Return the item at the root of the tree In addition, the tree may have an in_order, pre_order or post_order generic procedure. This procedure traverses the tree and
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/18.html (18 of 26) [7/20/2001 11:38:18 AM]
executes processes each item. Pre_order processes an item before its children. Post_order processes an item after its children. In_order processes a node in the sort order of the tree--after all the left children but before all the right. with with with with BC.Containers.Trees.Binary.In_Order; BC.Containers.Trees.Binary.Pre_Order; BC.Containers.Trees.Binary.Post_Order; Global_Heap;
package shipment_binary is -- grandfather would be proud type aQuantity is ( Unknown, Basket_6Quart, Basket_11Quart, Bushel, Skid, Boxcar ); type aFruit is ( Apples, Grapes, Peaches, Cherries, Pears, Plums, Other ); type aShipment is record number : Positive; quantity : aQuantity; contents : aFruit; end record;
procedure visitShipment( s : aShipment; OK : out boolean ); -- our tree traversal function package shipmentContainers is new BC.Containers( item=> aShipment ); -- basic fruit container package shipmentTrees is new shipmentContainers.Trees; -- basic tree support package shipmentBinaryTrees is new shipmentTrees.Binary( Storage_Manager => Global_Heap.Pool, Storage => Global_Heap.Storage ); -- our binary tree support procedure inOrdershipmentTraversal is new shipmentBinaryTrees.In_Order( visitShipment ); -- an in-order traversal procedure preOrdershipmentTraversal is new shipmentBinaryTrees.Pre_Order( visitShipment ); -- a pre-order traversal procedure postOrdershipmentTraversal is new shipmentBinaryTrees.Post_Order( visitShipment ); -- a post-order traversal end shipment_binary;
procedure visitShipment( s : aShipment; OK : out boolean ) is -- our tree traversal function begin Put( "Shipment of" ); Put( s.number'img ); Put( " " ); Put( s.quantity'img ); Put( "(S) of " ); Put_Line( s.contents'img ); OK := true; end visitShipment; end shipment_binary;
with ada.text_io, shipment_binary; use ada.text_io, shipment_binary; procedure bintree_demo is use shipmentBinaryTrees; root : Binary_Tree; t : Binary_Tree; s : aShipment; OK : boolean; begin Put_Line( "This is a demo of the Booch components: binary trees" ); New_Line; -- this is the root item s.number := 5; s.quantity := basket_6quart; s.contents := cherries; Insert( t, s, Child => Left ); -- child doesn't really matter because there's no prior item at the root root := Create( t ); -- remember where the root is -- add to left of root s.number := 7; s.quantity := basket_11quart; s.contents := pears; Append( t, s, Child => Left, After => Left ); -- child doesn't really matter here -- add to right of root s.number := 12; s.quantity := bushel; s.contents := apples; Append( t, s, Child => Left, After => Right ); -- child doesn't really matter here
Left_Child( t );
s.number := 3; s.quantity := skid; s.contents := peaches; Append( t, s, Child => Left, After => Right ); -- child doesn't really matter here Put_Line( Put_Line( Put_Line( Put_Line( Put_Line( Put_Line( ); Put_Line( " Put_Line( " Put_Line( " New_Line; |" ); +-------------------------------|" ); 3 skids of peaches" ); "Our tree is: "); " 5 6 qt baskets of cherries" ); " |" ); " +----------------------------------------------------+" ); " | |" ); "7 11 qt baskets of pears 12 bushels of apples"
Put_Line( "In-order traversal:" ); inOrderShipmentTraversal( root, OK ); if not OK then Put_Line( "The traversal was interrupted" ); end if; New_Line; Put_Line( "Pre-order traversal:" ); preOrderShipmentTraversal( root, OK ); if not OK then Put_Line( "The traversal was interrupted" ); end if; New_Line; Put_Line( "Post-order traversal:" ); postOrderShipmentTraversal( root, OK ); if not OK then Put_Line( "The traversal was interrupted" ); end if; end bintree_demo;
This is a demo of the Booch components: binary trees Our tree is: 5 6 qt baskets of cherries | +----------------------------------------------------+ | | 7 11 qt baskets of pears 12 bushels of apples | +-------------------------------| 3 skids of peaches
Pre-order traversal: Shipment of 5 BASKET_6QUART(S) of CHERRIES Shipment of 7 BASKET_11QUART(S) of PEARS Shipment of 3 SKID(S) of PEACHES Shipment of 12 BUSHEL(S) of APPLES Post-order traversal: Shipment of 3 SKID(S) of PEACHES Shipment of 7 BASKET_11QUART(S) of PEARS Shipment of 12 BUSHEL(S) of APPLES Shipment of 5 BASKET_6QUART(S) of CHERRIES Binary trees should not be Guarded. 18.1.14 AVL Trees AVL trees are binary trees that are balanced. On every insert or delete, the tree is restructured to keep its symmetry. As a result, the trees must be sorted by the Booch component and the program using the AVL tree must provide a "<" function to sort the tree by. The AVL package provides fewer subprograms than the binary tree package: q Clear - destory the AVL tree q Insert - add an item into the AVL tree q Delete - remove an item form the AVL tree q Extent - return the number of items in the AVL tree q Is_Null - true if there are no items in the AVL tree q Is_Member - true if an item is in the AVL tree q Visit - traverse the tree in-order executing a "read only" procedure q Modify - traverse the tree in-order executing a procedure that can alter the items. There are no subprograms for walking the tree. Here is a sample declaration: with BC.Containers.Trees.AVL; with Global_Heap; package fruit_avl is -- more fun with fruit type aQuantity is ( Unknown, Basket_6Quart, Basket_11Quart, Bushel, Skid, Boxcar ); type aFruit is ( Apples, Grapes, Peaches, Cherries, Pears, Plums, Other ); type aShipment is record number : Positive; quantity : aQuantity; contents : aFruit;
end record;
function sortCriteria( left, right : aShipment ) return boolean; -- for sorting the AVL tree package shipmentContainers is new BC.Containers( item=> aShipment ); -- basic fruit container package shipmentTrees is new shipmentContainers.Trees; -- basic tree support package shipmentAVLTrees is new shipmentTrees.AVL( sortCriteria, Storage_Manager => Global_Heap.Pool, Storage => Global_Heap.Storage ); -- our AVL tree support end fruit_avl;
package body fruit_avl is function sortCriteria( left, right : aShipment ) return boolean is begin return left.number < right.number; end sortCriteria; end fruit_avl; AVL trees have slower inserts and deletes than binary trees but are faster than a normal binary tree for searching. 18.1.15 Multiway Trees A multiway tree is a tree with any number of unsorted children (as opposed to a binary tree which always has no more than two chidren). The subprograms are similar to a binary tree. The append procedures add child items to an item. A new function called "Arity" returns the number children an item has. Multiway trees should not be Guarded. 18.1.16 Graphs Essentially, graphs are a generalization of maps where any number of items can be related to each other (as opposed to only two). A directed graph is a set of items (vertices) that are connected by relationships (edges or "arcs"). Like a single linked list, a program can only move forward along an arc. Items can also be linked to themselves. The graphs-directed package provides the following subprograms: q Create_Arc - add a relationship between two items q Number_Of_Incoming_Arcs - return the number of incoming arcs to an item q Number_Of_Outgoing_Arcs - return the number of outgoing arcs to an item q Set_From_Vertex - move an arch's source to a new item
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/18.html (23 of 26) [7/20/2001 11:38:18 AM]
Set_To_Vertex - move an arc's destination to a new item From_Vertex - return the source item for an arc To_Vertex - return the destination item for an arc
There are four iterators: a graph iterator, and three iterators for visiting items (incoming, outgoing and both). An undirected graph is a directed graph with pointers to both the previous and next item along an arc. Like a double linked list, a program can move forwards or backwards along an arc. The graphs-undirected package provides the following subprograms: q Create_Arc - add a relationship between two items q Arity - return the number of arcs. Self-arcs are counted only once q Set_First_Vertex - move an arch's first item to a new item q Set_Second_Vertex - move an arc's second item to a new item q First_Vertex - return the first item for an arc q Second_Vertex - return the second item for an arc There are two iterators: a graph iterator and an item iterator. Graphs should not be Guarded. 18.1.17 Smart Pointers Smart pointers are an access type that counts the number of references to the item being pointed to. Your program allocates the item. The item is deallocated when no more pointers point to it. Smart pointers are a simplified form of garbage collection. The smart package provides the following subprograms: q Create - create a new smart pointer from an access variable q Value - return the item pointed to by the smart pointer with BC.smart; package depts is type departments is ( accounting, information_technology, shipping, human_resources ); type deptAccess is access all departments; package deptPtrs is new BC.smart( departments, deptAccess ); end depts;
with ada.text_io, depts; use ada.text_io, depts; procedure sp_demo is accountingPtr : deptPtrs.Pointer; accounting2Ptr : deptPtrs.Pointer; department : deptAccess; begin Put_Line( "This is a demo of the Booch components: smart pointers" ); New_Line; department := new departments'( accounting );
Put_Line( "Assigning dynamically allocate value to a smart pointer" ); accountingPtr := deptPtrs.Create( department ); Put_Line( "The accounting pointer points at " & deptPtrs.Value( accountingPtr ).all'img ); New_Line; Put_Line( "Assigning a smart pointer to a smart pointer" ); accounting2Ptr := accountingPtr; Put_Line( "The accounting pointer 2 points at " & deptPtrs.Value( accounting2Ptr ).all'img ); New_Line; Put_Line( "The memory is released when the program ends or no more pointers" ); Put_Line( "access the memory." ); end sp_demo;
This is a demo of the Booch components: smart pointers Assigning dynamically allocate value to a smart pointer The accounting pointer points at ACCOUNTING Assigning a smart pointer to a smart pointer The accounting pointer 2 points at ACCOUNTING The memory is released when the program ends or no more pointers access the memory. 18.1.18 Booch Multithreading Booch components can be guarded (manually "locking" the structure for exclusive access) or synchronized (implicit blocking) for multithreading purposes. Guarding is implemented by creating extending a container type to a Guarded_Container using the GC.Containers.Guarded package. Guarded containers contain two new subprograms, "Seize" and "Release", to lock and unlock a container. (This is implemented using a semaphore.) Any Booch data structure can be made guarded using guarded containers, but in some cases guarding will not work as expected and should not be used (for example, with lists). The basic semaphore locks individual objects (although it many not work as expected on certain structures such as lists, according to AdaPower.Net). The basic semaphore can be extended and customized by a programmer. Rewriting the Bags example with guards: with with with with BC.Containers.Bags.Unbounded; BC.Containers.Guarded; BC.Support.Synchronization; Global_Heap;
package guarded_customers is type aCustomerID is new integer range 1_000..9_999; function IDHash( id : aCustomerID ) return Positive; -- our hash function
package customerContainers is new BC.Containers (Item => aCustomerID); -- this is the basic container package customerBags is new customerContainers.Bags; -- create a new bag support for our using container type package customerBag is new customerBags.Unbounded( Hash => IDHash, Buckets => 99, Storage_Manager => Global_Heap.Pool, Storage => Global_Heap.Storage); -- create an unbounded bag holding customer numbers package customerGuardedBag is new customerContainers.Guarded ( Base_Container => customerBag.Unbounded_Bag, Semaphore => BC.Support.Synchronization.Semaphore ); -- create a new controlled tagged record container for customers end guarded_customers; A new guarded bag can now be declared: customers : customerGuardedBag.Guarded_Container; and the bag can be locked using customerGuardedBag.Seize( customers ); Synchronized access by threads is implemented in special versions of the data structure packages (for example, maps.synchronized). With synchronized packages, the implementation details are hidden from the user.
<--Last Chapter
Table of Contents
Next Chapter-->
19 Specialized Topics
<--Last Chapter Table of Contents Next Chapter-->
-aOdir -aIdir -Idir -I-Ldir -nostdinc -nostdlib -cargs opts -bargs opts -largs opts -g -Idir -I-O[0123] -gnata -gnatA -gnatb -gnatc -gnatd? -gnatD -gnate -gnatE -gnatf -gnatF -gnatg -gnatG -gnath -gnati? -gnatk -gnatl -gnatL -gnatmnnn -gnatn -gnato -gnatO nm
Specify library/object files search path Specify source files search path Like -aIdir -aOdir Don't look for sources & library files in the default directory Look for program libraries also in dir Don't look for sources in the system default directory Don't look for library files in the system default directory opts are passed to the compiler opts are passed to the binder opts are passed to the linker Generate debugging information Specify source files search path Do not look for sources in current directory Control the optimization level Assertions enabled. Pragma Assert/Debug to be activated Avoid processing gnat.adc, if present file will be ignored Generate brief messages to stderr even if verbose mode set Check syntax and semantics only (no code generation) Compiler debug option ? (a-z,A-Z,0-9), see debug.adb Debug expanded generated code rather than source code Error messages generated immediately, not saved up till end Dynamic elaboration checking mode enabled Full errors. Verbose details, all undefined references Force all import/export external names to all uppercase GNAT implementation mode (used for compiling GNAT units) Output generated expanded code in source form Output this usage (help) information Identifier char set (?=1/2/3/4/8/p/f/n/w) Limit file names to nnn characters (k = krunch) Output full source listing with embedded error messages Use longjmp/setjmp for exception handling Limit number of detected errors to nnn (1-999) Inlining of subprograms (apply pragma Inline across units) Enable overflow checking (off by default) Set name of output ali file (internal switch)
-gnatp -gnatP -gnatq -gnatR -gnats -gnatt -gnatTnnn -gnatu -gnatU -gnatv -gnatw? -gnatW -gnatx -gnatX -gnaty
Suppress all checks Generate periodic calls to System.Polling.Poll Don't quit, try semantics, even if parse errors List representation information Syntax check only Tree output file to be generated All compiler tables start at nnn times usual starting size List units for this compilation Enable unique tag for error messages Verbose mode. Full error output with source lines to stdout Warning mode. (?=s/e/l/u for suppress/error/elab/undefined) Wide character encoding method (h/u/s/e/8/b) Suppress output of cross-reference information Language extensions permitted Enable all style checks Enable selected style checks xxx = list of parameters: q 1-9 check indentation q b check no blanks at end of lines q c check comment format q e check end labels present q f check no form feeds/vertical tabs in source q h check no horizontal tabs in source q i check if-then layout q k check casing rules for keywords, identifiers q m check line length <= 79 characters q Mnnn check line length <= nnn characters q r check RM column layout q s check separate subprogram specs present q t check token separation rules Distribution stub generation (r/s for receiver/sender stubs) Use zero cost exception handling Enforce Ada 83 restrictions
-gnatyxxx
jgnatmake will create two files: hello.class and ada_hello.class. To run the program under the Java interpreter, type
java hello Table: java switches Java Interpreter Switch -help -version -ss size -mx size -ms size -as size -classpath path -verify -verifyremote -noverify -Dproperty=value -verbosegc -noclassgc -v, -verbose -verbosejit -verbosemem -debug -noasyncgc -cs, -checksource -oss size -jar Description Print usage info Print version number Maximum native stack size Maximum heap size Initial heap size Heap increment Set classpath Verify all bytecode Verify bytecode loaded from network Do not verify any bytecode Set a property Print message during garbage collection Disable class garbage collection Be verbose Print message during JIT code generation Print detailed memory allocation statistics Trace method calls Do not garbage collect asynchronously Check source against class files Maximum java stack size Executable is a JAR
Limitations: Ada streams don't work with Jgnat. 19.2 ASIS Information on ASIS is available at http://info.acm.org/sigada/WG/asiswg/asiswg.html.
<--Last Chapter
Table of Contents
Next Chapter-->
20.1 The Project Proposal Before you begin any project that will be released to the public, it's a good idea to draw up a proposal. The proposal should be about one page document describing the purpose of the project, who it's being made for, and how long it will take and what kind of investments (time, money or otherwise) you expect. This is especially important if there is anybody working with you. Don't assume your teammates see the project in exactly the same was as you do: write a proposal to avoid misunderstandings. For example, calling a project "a database" doesn't say much. Calling it a "fast, distributed database for businesses" tells your teammates where the database will be used, gives them an idea about the features required, and tells that the design emphasis is on execution speed. Once your proposal is finished, bounce the ideas of a few people you respect and trust, especially if they are potential users of your program. If none of them think the project is practical, you may want to change the target audience or features of your project, or chose another project altogether. You can later use your proposal as the basic text for an announcement of the release of your program. 20.2 The Design Phase When it comes time to begin designing the basic layout of a project, remember that Ada has features designed just for this task. Break up your project into a series of packages, and include basic type definitions and subprograms (using pragma stubbed). Remember that the design doesn't have to be perfect, but you need a starting place for you and your teammates to discuss the work. Use lots of comments to avoid continually explaining the purpose of each package and it's contents. When you have a basic layout, compile each of the specs to make sure the design is sound. 20.3 The Development Phase Check list: Did you use pragma pure, preelaborate or no_elaboration_code whenever possible? Did you use pragma Normalize_Scalars whenever possible? 20.4 The Alpha/Beta Release Check list for first alpha or beta release: Check your integers: did you use integer when short_integer or long_integer would have been better?
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/20.html (1 of 7) [7/20/2001 11:38:49 AM]
Do you have pragma Optimize set in all of your packages? Did you use pragma Pack all arrays and records that need packing? Do some need packing turned off? Did you assign your access types to a debug pool in order to check for run-time errors? 20.5 Releasing Your Software Check list: Did you remove all pragma Normalize_Scalars? Did you remove all access type references to debug pools? 20.5.1 A Third Party Library If you want to release a package as a third party library: Change your .ads files to read-only with chmod -w. Collect your executables into an archive with the ar command (see the section on libraries above). Include instructions for installing the archive and make sure you mention that those who use your library must use the -f option for gnatmake. This option treats all read-only files as third party libraries that cannot be recompiled because the package bodies were not included. 20.6 Distribution Formats 20.6.1 RPM: Red Hat Package Manager RPM (Red Hat Package Manager) is the most popular installation tool. It installs, uninstalls, and checksums packages. S.u.S.E.'s YaST (Yet Another Setup Tool) works using RPM. RPM files end in ".rpm". Full details on the RPM format are available from Red Hat's RPM site at http://www.rpm.org. The -q command checks for a package. -a shows all installed packages. [root@redbase /root]# rpm -q uucp uucp-1.06.1-14 [root@redbase /root]# rpm -q kernel kernel-2.0.32-2 [root@redbase /root]# rpm -q -a setup-1.9.1-1 filesystem-1.3.1-2 basesystem-4.9-1 AnotherLevel-0.5-2 ldconfig-1.9.5-2
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/20.html (2 of 7) [7/20/2001 11:38:49 AM]
XFree86-Mach64-3.3.1-14 Creating a new RPM archive is a cumbersome, multistep process. 20.6.2 TGZ Packages TGZ (tar-ed gzip packages) are created by collecting all the files into one file using tar (tape archiver) and compressing the file gzip (GNU zip) or tar's z (compress) option. The files are usually named with ".tgz" ending but sometimes have the ".tar.gz" longform ending. To create a new .tar archive, use the "cfv" options. tar cfv archivename file To add additional files to the archive, use "rfv". tar rfv archivename file When the tar file is finished, compress it with gzip gzip 9 archivename.tar And rename it to .tgz mv archivename.tar.gz archivename.tar.gz 20.6.3 TAR.BZ2 Packages TAR.BZ2 (tar-ed bzip packages) are another option. Like TGZ, these are tar files that are compressed, but instead use the new bzip2 command that compresses better than gzip. Other Formats TZ is an older format, these are tar files that are compressed with the old compression command, compress. ZIP packages are collected and compressed in the popular PC zip format using zip. ZOO packages use an older compression program, zoo. CPIO (Copy In-Out) is another archiving program similar to tar. It collects files but doesn't compress them. DEB is a package for the Debian distribution. There are a host of other tools and formats, including ones to create archives for other platforms. 20.7 Man Pages Linux man pages are special text files formatted for the groff program (GNU run off) is based on the older UNIX programs troff (for printers) and nroff (for terminals). Troff was originally created in 1973 by Joseph F. Ossanna.
man pages are text files containing groff markup codes embedded in the text. These codes, much like HTML tags in a web page, control the fonts, layout and graphics used in the pages. You can also define your own groff codes (using groff macros). Here's an example of a man page with groff markup codes: .\"This is a comment .TH MAN 7 "25 July 1993" "Linux" "Linux Programmer's Manual" .SH NAME man \- macros to format man pages .SH SYNOPSIS .B groff \-Tascii \-man .I file Here, the ".B" groff code indicates that the text that follows should be bold (similar to <b>), and the ".SH" groff code incidcates the text that follows is a subheading (similar to <hn>). The groff predefined macros pretaining to manual pages are documented in the section 7 manual page on man ("man 7 man"). All the man pages are stored in subdirectories in /usr/man. The subdirectories are numbered, each number representing a different section number of the Linux manual. The manuals sections include: 1. Linux introduction 2. System Calls 3. C Library Calls 4. Summaries and Data Structures For example, the C library call manual pages are located in /usr/man/man3. The easiest way to create a simple man page for your program is to find a similar man page and make a copy. Use this copy as a basis for your new man page. You can perform a simple test on your new man page by groff mypage | less To convert your page to another format, use groff mypage > mypage.ps to create a PostScript version of your man page (or use the -Tdvi switch to create a TeX .dvi file). Use one of the free conversion programs available on the Internet to translate the PostScript file to another format.
Entered-date: Wednesday, May 26, 1999 Description: Linux configuration and administration utility using AI techniques.
Author:
Maintained-by: [email protected] (PegaSoft Canada) Primary-site: metalab.unc.edu /pub/Linux/system/admin 700kB smiab-0.9.1.tgz Alternate-site: Original-site: Platforms: Copying-policy: freeware End [KB-platform or platforms?] Details about the format are available from the LSM web site. To register a program with the Linux Software Map, email your LSM entry to '[email protected]' with the subject 'add'. 20.9 Software Licensing Options The following is a very simplistic overview of the basic licensing options for Linux: Commercial sold for money, with warranty. Windows 95 is commercial, as are most programs that run on it. Free/Freeware free for all use, usually has no warranty. GPL (GNU Public License) free for use and no warranty. If it's a programming tool, you can only incorporate it into your programs to create more GPL software. In other words, GPL is free public software that can only be used to make more free public software. Imagine a free engine for cars. If any car is built to take that free engine, it must be sold for free as well. LGPL (Library GPL) same as GPL. Commercial programs may only use it if it's shared, not statically linked. A car can be sold with no engine in it, and the engine can be added separately by the dealer, but you can't sell the car with the free engine factory-installed. Shareware commercial software that's sold on the honor system: people who like the software and who use it are expected to send in a cheque to the author. There's a lot of shareware for Windows. Xfree86 uses a different licence that's compatible with GPL/LGPL. Virtually all the standard C libraries are LGPL, including libc, but you should check to documentation or C header files to make sure. Details on these and other licensing options, and how they interact, are described in the book Linux Application Development from Addision-Wesley-Longman.
<--Last Chapter
Table of Contents
Appendices-->
<--Chapter 20
Table of Contents
Appendix B-->
[root@armitage temp]# grep "procedure" /home/ken/ada/basicio2.adb procedure basicio2 is find search for a file [root@armitage temp]# find /home/ken -type f -name basicio3.adb /home/ken/ada/basicio3.adb lpr print a file [root@armitage temp]# lpr basicio3.adb lprm stop printing a file, if the file hasn't started printing yet [root@armitage temp]# lprm dfA017Aa01370 dequeued cfA017Aa01370 dequeued lpq list your files waiting to be printed [root@armitage temp]# lpq no entries cat display a file [root@armitage temp]# cat hello.adb with Ada.Text_IO; use Ada.Text_IO; procedure hello is begin Put_Line( "Hello world!" ); end hello; less display a file one screen at a time, allowing you to move around
tr translate characters. To translate a DOS text file to a Linux text file, use
<--Chapter 20
Table of Contents
Appendix B-->
<--Appendix A
Table of Contents
Appendix C-->
EFBIG ENOSPC ESPIPE EROFS EMLINK EPIPE EDOM ERANGE EDEADLK ENAMETOOLONG ENOLCK ENOSYS ENOTEMPTY ELOOP EWOULDBLOCK ENOMSG EIDRM ECHRNG EL2NSYNC EL3HLT EL3RST ELNRNG EUNATCH ENOCSI EL2HLT EBADE EBADR EXFULL ENOANO EBADRQC EBADSLT EDEADLOCK EBFONT ENOSTR ENODATA
File too large No space left on device Illegal seek Read-only file system Too many links Broken pipe Math argument out of domain of func Math result not representable Resource deadlock would occur File name too long No record locks available Function not implemented Directory not empty Too many symbolic links encountered Operation would block No message of desired type Identifier removed Channel number out of range Level 2 not synchronized Level 3 halted Level 3 reset Link number out of range Protocol driver not attached No CSI structure available Level 2 halted Invalid exchange Invalid request descriptor Exchange full No anode Invalid request code Invalid slot Bad font file format Device not a stream No data available
ETIME ENOSR ENONET ENOPKG EREMOTE ENOLINK EADV ESRMNT ECOMM EPROTO EMULTIHOP EDOTDOT EBADMSG EOVERFLOW ENOTUNIQ EBADFD EREMCHG ELIBACC ELIBBAD ELIBSCN ELIBMAX ELIBEXEC EILSEQ ERESTART ESTRPIPE EUSERS ENOTSOCK EDESTADDRREQ EMSGSIZE EPROTOTYPE ENOPROTOOPT EPROTONOSUPPORT ESOCKTNOSUPPORT EOPNOTSUPP EPFNOSUPPORT
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
Timer expired Out of streams resources Machine is not on the network Package not installed Object is remote Link has been severed Advertise error Srmount error Communication error on send Protocol error Multihop attempted RFS specific error Not a data message Value too large for defined data type Name not unique on network File descriptor in bad state Remote address changed Can not access a needed shared library Accessing a corrupted shared library .lib section in a.out corrupted Linking in too many shared libraries Cannot exec a shared library directly Illegal byte sequence Interrupted system call should be restarted Streams pipe error Too many users Socket operation on non-socket Destination address required Message too long Protocol wrong type for socket Protocol not available Protocol not supported Socket type not supported Operation not supported on transport endpoint Protocol family not supported
EAFNOSUPPORT EADDRINUSE EADDRNOTAVAIL ENETDOWN ENETUNREACH ENETRESET ECONNABORTED ECONNRESET ENOBUFS EISCONN ENOTCONN ESHUTDOWN ETOOMANYREFS ETIMEDOUT ECONNREFUSED EHOSTDOWN EHOSTUNREACH EALREADY EINPROGRESS ESTALE EUCLEAN ENOTNAM ENAVAIL EISNAM EREMOTEIO EDQUOT ENOMEDIUM EMEDIUMTYPE
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
Address family not supported by protocol Address already in use Cannot assign requested address Network is down Network is unreachable Network dropped connection because of reset Software caused connection abort Connection reset by peer No buffer space available Transport endpoint is already connected Transport endpoint is not connected Cannot send after transport endpoint shutdown Too many references: cannot splice Connection timed out Connection refused Host is down No route to host Operation already in progress Operation now in progress Stale NFS file handle Structure needs cleaning Not a XENIX named type file No XENIX semaphores available Is a named type file Remote I/O error Quota exceeded No medium found Wrong medium type
<--Appendix A
Table of Contents
Appendix C-->
<--Appendix B
Table of Contents
Appendix D-->
_exit - terminate the current process _llseek - reposition read/write file offset _newselect - NQS sysctl - read/write system parameters accept - accept a connection on a socket access - check user's permissions for a file acct - switch process accounting on or off adjtimex - tune kernel clock afs_syscall - unimplemented alarm - set an alarm clock for delivery of a signal bdflush - start, flush, or tune buffer-dirty-flush daemon bind - bind a name to a socket break - unimplemented brk - change data segment size cacheflush - (MIPS) flush contents of instruction and/or data cache chdir - change working directory chmod - change permissions of a file chown - change ownership of a file chroot - change root directory __clone - create a child process for multithreading close - close a file descriptor connect - initiate a connection on a socket creat - open and possibly create a file or device create_module - create a loadable module entry delete_module - delete a loadable module entry dup - duplicate a file descriptor dup2 - duplicate a file descriptor execve - execute program exit - cause normal program termination fchdir - change working directory fchmod - change permissions of a file fchown - change ownership of a file fcntl - manipulate file descriptor fdatasync - synchronize a file's in-core data with that on disk flock - apply or remove an advisory lock on an open file fork - create a child process fstat - get file status fstatfs - get file system statistics
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/99_c.html (1 of 5) [7/20/2001 11:39:50 AM]
fsync - synchronize a file's complete in-core state with that on disk ftruncate - truncate a file to a specified length get_kernel_syms - retrieve exported kernel and module symbols getdents - get directory entries getdomainname - get domain name getdtablesize - get descriptor table size getgid - get group identity geteuid - get user identity getgid - get group identity getgroups - get/set list of supplementary group gethostid - get the unique identifier of the current host gethostnamee - get host name getitimer - get value of an interval timer getpagesize - get system page size getpeername - get name of connected peer getpgid - get process group getpgrp - get process group getpid - get process identification getppid - get process identification getpriority - get/set program scheduling priority getresgid - get real, effective and saved group ID getresuid - get real, effective and saved user ID getrlimit - get resource limits getrusage - get resource limits getsid - get session ID getsockname - get socket name getsockopt - get options on sockets gettimeofday - get time getuid - get user identity gtty - unimplemented idle - make process 0 idle init_module - initialize a loadable module entry ioctl - control device ioperm - set port input/output permissions iopl - change I/O privilege level ipc - System V IPC system calls kill - send signal to a process killpg - send signal to a process group lchown - change ownership of a file link - make a new name for a file listen - listen for connections on a socket llseek - reposition read/write file offset lock - unimplemented lseek - reposition read/write file offset lstat - get file status mkdir - create a directory mknod - create a directory or special or ordinary file mlock - disable paging for some parts of memory mlockall - disable paging for calling process mmap - map files or devices into memory
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/99_c.html (2 of 5) [7/20/2001 11:39:50 AM]
modify_ldt - get or set ldt mount - mount and unmount filesystems. mprotect - control allowable accesses to a region of memory mpx - unimplemented mremap - re-map a virtual memory address msgctl - message control operations msgget - get a message queue identifier msgrcv - receive a messsage msgsnd - send a message msync - synchronize a file with a memory map munlock - reenable paging for some parts of memory munlockall - reenable paging for calling process munmap - unmap files or devices into memory nanosleep - pause execution for a specified time nfsservctl - syscall interface to kernel nfs daemon nice - change process priority oldfstat - obsolete oldlstat - obsolete oldolduname - obsolete oldstat - obsolete olduname - obsolete open - open a file or device outb, outw, outl - port output macros pause - wait for signal personality - set the process execution domain pipe - create pipe poll - wait for some event on a file descriptor prctl - operations on a process prof - unimplemented ptrace - process trace query_module - query the kernel for various bits pertaining to modules quotactl - manipulate disk quotas read - read from a file descriptor readdir - read directory entry readlink - read value of a symbolic link readv - read a vector reboot - reboot or enable/disable Ctrl-Alt-Del recv - receive a message from a socket recvfrom - receive a message from a socket recvmsg - receive a message from a socket rename - change the name or location of a file rmdir - delete a directory sbrk - change data segment size sched_get_priority_max - get static priority range sched_get_priority_min - get static priority range sched_getparam - get scheduling parameters sched_setscheduler - get schedule algorithm/parameters sched_rr_get_interval - get the SCHED_RR interval for the named process sched_setparam - set scheduling parameters sched_setscheduler - set schedule algorithm/parameters
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/99_c.html (3 of 5) [7/20/2001 11:39:50 AM]
sched_yield - yield the processor select - synchronous I/O multiplexing semctl - semaphore control operations semget - get a semaphore set identifier semop - semaphore operations send - send a message from a socket sendfile - transfer data between file descriptors sendmsg - send a message from a socket sendto - send a message from a socket setdomainname - set domain name setegid - set effective group ID seteuid - set effective user ID setfsgid - set group identity used for file system checks setfsuid - set user identity used for file system checks setgid - set group identity setgroups - set list of supplementary group sethostid - set the unique identifier of the current host sethostname - set host name setitimer - get or set value of an interval timer setpgid - set process group ID setpgrp - set process group setpriority - set program scheduling priority setregid - set real group ID setresgid - set real, effective and saved user setresuid - set real, effective and saved user setreuid - set real and / or effective user ID setrlimit - set resource limits setsid - creates a session and sets the process group ID setsockopt - set options on sockets settimeofday - get / set time setuid - set user identity setup - setup devices and file systems, mount root file (not available) sgetmask - ANSI C signal handling shmat - shared memory operations shmctl - shared memory control shmdt - shared memory operations shmget - allocates a shared memory segment shutdown - shut down part of a full-duplex connection sigaction - change signal action sigblock - change blocked signals siggetmask - get blocked signals sigmask - C macro to create signal masks signal - install signal handler sigpause - atomically release blocked signals and wait for interrupt sigpending - examine pending signals sigprocmask - change blocked signals sigreturn - return from signal handler and cleanup stack sigsetmask - set net group of blocked signals sigsuspend - replace signal mask and suspend process sigvec - obsolete
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/99_c.html (4 of 5) [7/20/2001 11:39:50 AM]
socket - create an endpoint for communication socketcall - socket system calls entry point socketpair - create a pair of connected sockets ssetmask - NQS stat - get file status statfs - get file system statistics stime - set time stty - unimplemented swapoff - stop swapping to file/device swapon - start swapping to file/device symlink - make a new name for a file sync - commit buffer cache to disk sysctl - read/write system parameters sysfs - get file system type information sysinfo - returns information on overall system statistics syslog - read and/or clear kernel message ring buffer; set console_loglevel time - get time in seconds times - get process times truncate - truncate a file to a specified length umask - set file creation mask umount - unmount filesystems uname - get name and information about current kernel unlink - delete a name and possibly the file it refers to uselib - select shared library ustat - get file system statistics utime - change access and/or modification times of an inode utimes - change access and/or modification times of an inode vfork - alias for fork vhangup - virtually hangup the current tty vm86 - (Intel) enter virtual 8086 mode vm86old - obsolete wait - wait for process termination wait3 - wait for process termination, BSD style wait4 - wait for process termination, BSD style waitpid - wait for process termination write - write to a file descriptor writev - read or write a vector
<--Appendix B
Table of Contents
Appendix D-->
<--Appendix C
Table of Contents
Appendix E-->
Appendix D: Signals
Be aware that the mapping of names to signals may be -to-one. There may be aliases. Also, for allsignal names that are not supported on the current systemthe value of the corresponding constant will be zero. SIGHUP -- hangup SIGINT -- interrupt (rubout) SIGQUIT -- quit (ASCD FS) SIGILL -- illegal instruction (not reset) SIGTRAP -- trace trap (not reset) SIGIOT -- IOT instruction SIGABRT used by abort, SIGIOT in the future SIGFPE -- floating point exception SIGKILL -- kill (cannot be caught or ignored) SIGBUS -- bus error SIGSEGV -- segmentation violation SIGPIPE -- write on a pipe with no one to read it SIGALRM -- alarm clock SIGTERM -- software termination signal from kill SIGUSR1 -- user defined signal 1 SIGUSR2 -- user defined signal 2 SIGCLD -- child status change SIGCHLD -- 4.3BSD's/POSIX name for SIGCLD SIGWINCH -- window size change SIGURG -- urgent condition on IO channel SIGPOLL -- pollable event occurred SIGIO -- input/output possible, SIGPOLL alias (Solaris) SIGSTOP -- stop (cannot be caught or ignored) SIGTSTP -- user stop requested from tty SIGCONT -- stopped process has been continued SIGTTIN -- background tty read attempted SIGTTOU -- background tty write attempted SIGVTALRM -- virtual timer expired SIGPROF -- profiling timer expired SIGXCPU -- CPU time limit exceeded SIGXFSZ -- filesize limit exceeded SIGUNUSED -- unused signal SIGSTKFLT -- stack fault on coprocessor SIGLOST -- Linux alias for SIGIO
file:///G|/Linux/http-www.vaxxine.com-pegasoft-people-html/book/99_d.html (1 of 2) [7/20/2001 11:40:07 AM]
<--Appendix C
Table of Contents
Appendix E-->
<--Appendix D
Table of Contents
Appendix F-->
-- Ioctls defined internal to the kernel ('scsi_ioctl.h'). -- Ioctls defined in modules distributed separately from the kernel. And, of course, it may have errors and omissions. // Main table. // <include/asm-i386/socket.h> 0x00008901 0x00008902 0x00008903 0x00008904 0x00008905 0x00008906 FIOSETOWN const int * SIOCSPGRP const int * FIOGETOWN int * SIOCGPGRP int * SIOCATMARK int * SIOCGSTAMP timeval *
// <include/asm-i386/termios.h> 0x00005401 0x00005402 0x00005403 0x00005404 0x00005405 0x00005406 0x00005407 0x00005408 0x00005409 0x0000540A 0x0000540B 0x0000540C 0x0000540D 0x0000540E 0x0000540F 0x00005410 0x00005411 0x00005412 0x00005413 0x00005414 0x00005415 0x00005416 0x00005417 0x00005418 0x00005419 0x0000541A 0x0000541B 0x0000541B 0x0000541C // MORE 0x0000541D 0x0000541E 0x0000541F 0x00005420 0x00005421 0x00005422 0x00005423 0x00005424 0x00005425 0x00005426 0x00005450 TIOCCONS void TIOCGSERIAL struct serial_struct * TIOCSSERIAL const struct serial_struct * TIOCPKT const int * FIONBIO const int * TIOCNOTTY void TIOCSETD const int * TIOCGETD int * TCSBRKP int TIOCTTYGSTRUCT struct tty_struct * FIONCLEX void TCGETS struct termios * TCSETS const struct termios * TCSETSW const struct termios * TCSETSF const struct termios * TCGETA struct termio * TCSETA const struct termio * TCSETAW const struct termio * TCSETAF const struct termio * TCSBRK int TCXONC int TCFLSH int TIOCEXCL void TIOCNXCL void TIOCSCTTY int TIOCGPGRP pid_t * TIOCSPGRP const pid_t * TIOCOUTQ int * TIOCSTI const char * TIOCGWINSZ const struct winsize * TIOCSWINSZ struct winsize * TIOCMGET int * TIOCMBIS const int * TIOCMBIC const int * TIOCMSET const int * TIOCGSOFTCAR int * TIOCSSOFTCAR const int * FIONREAD int * TIOCINQ int * TIOCLINUX const char *
0x00005451 0x00005452 0x00005453 0x00005454 0x00005455 0x00005456 0x00005457 0x00005458 0x00005459 0x0000545A 0x0000545B
FIOCLEX void FIOASYNC const int * TIOCSERCONFIG void TIOCSERGWILD int * TIOCSERSWILD const int * TIOCGLCKTRMIOS struct termios * TIOCSLCKTRMIOS const struct temios * TIOCSERGSTRUCT struct async_struct * TIOCSERGETLSR int * TIOCSERGETMULTI struct serial_multiport_struct * TIOCSERSETMULTI const struct serial_multiport_struct *
// <include/linux/ax25.h> 0x000089E0 0x000089E1 0x000089E2 0x000089E3 0x000089E4 0x000089E5 0x000089E6 SIOCAX25GETUID const struct sockaddr_ax25 * SIOCAX25ADDUID const struct sockaddr_ax25 * SIOCAX25DELUID const struct sockaddr_ax25 * SIOCAX25NOUID const int * SIOCAX25DIGCTL const int * SIOCAX25GETPARMS struct ax25_parms_struct * // I-O SIOCAX25SETPARMS const struct ax25_parms-struct *
0x00007316 STL_BSTOP void 0x00007317 STL_BRESET void // <include/linux/cdrom.h> 0x00005301 0x00005302 0x00005303 0x00005304 0x00005305 0x00005306 CDROMPAUSE void CDROMRESUME void CDROMPLAYMSF const struct cdrom_msf * CDROMPLAYTRKIND const struct cdrom_ti * CDROMREADTOCHDR struct cdrom_tochdr * CDROMREADTOCENTRY struct cdrom_tocentry * // I-O
0x00005307 0x00005308 0x00005309 0x0000530A 0x0000530B 0x0000530C 0x0000530D 0x0000530E 0x0000530F 0x00005310 0x00005311 0x00005312 0x00005313 0x00005314 0x00005315 0x00005316
CDROMSTOP void CDROMSTART void CDROMEJECT void CDROMVOLCTRL const struct cdrom_volctrl * CDROMSUBCHNL struct cdrom_subchnl * // I-O CDROMREADMODE2 const struct cdrom_msf * // MORE CDROMREADMODE1 const struct cdrom_msf * // MORE CDROMREADAUDIO const struct cdrom_read_audio * // MORE CDROMEJECT_SW int CDROMMULTISESSION struct cdrom_multisession * // I-O CDROM_GET_UPC struct { char [8]; } * CDROMRESET void CDROMVOLREAD struct cdrom_volctrl * CDROMREADRAW const struct cdrom_msf * // MORE CDROMREADCOOKED const struct cdrom_msf * // MORE CDROMSEEK const struct cdrom_msf *
// <include/linux/cm206.h> 0x00002000 CM206CTL_GET_STAT int 0x00002001 CM206CTL_GET_LAST_STAT int // <include/linux/cyclades.h> 0x00435901 0x00435902 0x00435903 0x00435904 0x00435905 0x00435906 0x00435907 0x00435908 0x00435909 CYGETMON struct cyclades_monitor * CYGETTHRESH int * CYSETTHRESH int CYGETDEFTHRESH int * CYSETDEFTHRESH int CYGETTIMEOUT int * CYSETTIMEOUT int CYGETDEFTIMEOUT int * CYSETDEFTIMEOUT int
// <include/linux/ext2_fs.h> 0x80046601 0x40046602 0x80047601 0x40047602 EXT2_IOC_GETFLAGS int * EXT2_IOC_SETFLAGS const int * EXT2_IOC_GETVERSION int * EXT2_IOC_SETVERSION const int *
// <include/linux/fd.h> 0x00000000 FDCLRPRM void 0x00000001 FDSETPRM const struct floppy_struct * 0x00000002 FDDEFPRM const struct floppy_struct *
0x00000003 0x00000004 0x00000005 0x00000006 0x00000007 0x00000008 0x0000000A 0x0000000B 0x0000000C 0x0000000E 0x00000010 0x00000014 0x00000015 0x00000016 0x00000017 0x00000018 0x00000019 0x0000001B 0x0000001C 0x0000001E 0x00000028
FDGETPRM struct floppy_struct * FDMSGON void FDMSGOFF void FDFMTBEG void FDFMTTRK const struct format_descr * FDFMTEND void FDSETEMSGTRESH int FDFLUSH void FDSETMAXERRS const struct floppy_max_errors * FDGETMAXERRS struct floppy_max_errors * FDGETDRVTYP struct { char [16]; } * FDSETDRVPRM const struct floppy_drive_params * FDGETDRVPRM struct floppy_drive_params * FDGETDRVSTAT struct floppy_drive_struct * FDPOLLDRVSTAT struct floppy_drive_struct * FDRESET int FDGETFDCSTAT struct floppy_fdc_state * FDWERRORCLR void FDWERRORGET struct floppy_write_errors * FDRAWCMD struct floppy_raw_cmd * // MORE I-O FDTWADDLE void
// <include/linux/fs.h> 0x0000125D 0x0000125E 0x0000125F 0x00001260 0x00001261 0x00001262 0x00001263 0x00000001 0x00000002 BLKROSET const int * BLKROGET int * BLKRRPART void BLKGETSIZE int * BLKFLSBUF void BLKRASET int BLKRAGET int * FIBMAP int * // I-O FIGETBSZ int *
// <include/linux/hdreg.h> 0x00000301 0x00000302 0x00000304 0x00000307 0x00000308 0x00000309 0x0000030A 0x0000030B 0x0000031F 0x00000321 HDIO_GETGEO struct hd_geometry * HDIO_GET_UNMASKINTR int * HDIO_GET_MULTCOUNT int * HDIO_GET_IDENTITY struct hd_driveid * HDIO_GET_KEEPSETTINGS int * HDIO_GET_CHIPSET int * HDIO_GET_NOWERR int * HDIO_GET_DMA int * HDIO_DRIVE_CMD int * // I-O HDIO_SET_MULTCOUNT int
HDIO_SET_UNMASKINTR int HDIO_SET_KEEPSETTINGS int HDIO_SET_CHIPSET int HDIO_SET_NOWERR int HDIO_SET_DMA int
// <include/linux/if_eql.h> 0x000089F0 0x000089F1 0x000089F2 0x000089F3 0x000089F4 0x000089F5 EQL_ENSLAVE struct ifreq * // MORE I-O EQL_EMANCIPATE struct ifreq * // MORE I-O EQL_GETSLAVECFG struct ifreq * // MORE I-O EQL_SETSLAVECFG struct ifreq * // MORE I-O EQL_GETMASTRCFG struct ifreq * // MORE I-O EQL_SETMASTRCFG struct ifreq * // MORE I-O
// <include/linux/if_plip.h> 0x000089F0 SIOCDEVPLIP struct ifreq * // I-O // <include/linux/if_ppp.h> 0x00005490 0x00005491 0x00005492 0x00005493 0x00005494 0x00005495 0x00005497 0x00005498 0x00005499 0x0000549A 0x0000549B 0x0000549C 0x0000549D 0x0000549E 0x0000549F PPPIOCGFLAGS int * PPPIOCSFLAGS const int * PPPIOCGASYNCMAP int * PPPIOCSASYNCMAP const int * PPPIOCGUNIT int * PPPIOCSINPSIG const int * PPPIOCSDEBUG const int * PPPIOCGDEBUG int * PPPIOCGSTAT struct ppp_stats * PPPIOCGTIME struct ppp_ddinfo * PPPIOCGXASYNCMAP struct { int [8]; } * PPPIOCSXASYNCMAP const struct { int [8]; } * PPPIOCSMRU const int * PPPIOCRASYNCMAP const int * PPPIOCSMAXCID const int *
// <include/linux/ipx.h> 0x000089E0 SIOCAIPXITFCRT const char * 0x000089E1 SIOCAIPXPRISLT const char * 0x000089E2 SIOCIPXCFGDATA struct ipx_config_data * // <include/linux/kd.h> 0x00004B60 GIO_FONT struct { char [8192]; } *
0x00004B61 0x00004B6B 0x00004B6C 0x00004B70 0x00004B71 0x00004B2F 0x00004B30 0x00004B31 0x00004B32 0x00004B33 0x00004B34 0x00004B35 0x00004B36 0x00004B37 0x00004B3A 0x00004B3B 0x00004B3C 0x00004B3D 0x00004B40 0x00004B41 0x00004B69 0x00004B6A 0x00004B66 0x00004B67 0x00004B68 0x00004B44 0x00004B45 0x00004B62 0x00004B63 0x00004B64 0x00004B65 0x00004B46 0x00004B47 0x00004B48 0x00004B49 0x00004B4A 0x00004B4B 0x00004B4C 0x00004B4D 0x00004B4E
PIO_FONT const struct { char [8192]; } * GIO_FONTX struct console_font_desc * // MORE I-O PIO_FONTX const struct console_font_desc * //MORE GIO_CMAP struct { char [48]; } * PIO_CMAP const struct { char [48]; } KIOCSOUND int KDMKTONE int KDGETLED char * KDSETLED int KDGKBTYPE char * KDADDIO int // MORE KDDELIO int // MORE KDENABIO void // MORE KDDISABIO void // MORE KDSETMODE int KDGETMODE int * KDMAPDISP void // MORE KDUNMAPDISP void // MORE GIO_SCRNMAP struct { char [E_TABSZ]; } * PIO_SCRNMAP const struct { char [E_TABSZ]; } * GIO_UNISCRNMAP struct { short [E_TABSZ]; } * PIO_UNISCRNMAP const struct { short [E_TABSZ]; } * GIO_UNIMAP struct unimapdesc * // MORE I-O PIO_UNIMAP const struct unimapdesc * // MORE PIO_UNIMAPCLR const struct unimapinit * KDGKBMODE int * KDSKBMODE int KDGKBMETA int * KDSKBMETA int KDGKBLED int * KDSKBLED int KDGKBENT struct kbentry * // I-O KDSKBENT const struct kbentry * KDGKBSENT struct kbsentry * // I-O KDSKBSENT const struct kbsentry * KDGKBDIACR struct kbdiacrs * KDSKBDIACR const struct kbdiacrs * KDGETKEYCODE struct kbkeycode * // I-O KDSETKEYCODE const struct kbkeycode * KDSIGACCEPT int
// <include/linux/lp.h> 0x00000601 LPCHAR int 0x00000602 LPTIME int 0x00000604 LPABORT int
LPSETIRQ int LPGETIRQ int * LPWAIT int LPCAREFUL int LPABORTOPEN int LPGETSTATUS int * LPRESET void LPGETSTATS struct lp_stats *
// <include/linux/mroute.h> 0x000089E0 SIOCGETVIFCNT struct sioc_vif_req * // I-O 0x000089E1 SIOCGETSGCNT struct sioc_sg_req * // I-O // <include/linux/mtio.h> 0x40086D01 0x801C6D02 0x80046D03 0x80206D04 0x40206D05 MTIOCTOP const struct mtop * MTIOCGET struct mtget * MTIOCPOS struct mtpos * MTIOCGETCONFIG struct mtconfiginfo * MTIOCSETCONFIG const struct mtconfiginfo *
// <include/linux/netrom.h> 0x000089E0 0x000089E1 0x000089E2 0x000089E3 SIOCNRGETPARMS struct nr_parms_struct * // I-O SIOCNRSETPARMS const struct nr_parms_struct * SIOCNRDECOBS void SIOCNRRTCTL const int *
// <include/linux/sbpcd.h> 0x00009000 DDIOCSDBG const int * 0x00005382 CDROMAUDIOBUFSIZ int // <include/linux/scc.h> 0x00005470 0x00005471 0x00005472 0x00005473 0x00005474 TIOCSCCINI void TIOCCHANINI const struct scc_modem * TIOCGKISS struct ioctl_command * // I-O TIOCSKISS const struct ioctl_command * TIOCSCCSTAT struct scc_stat *
0x00005384 SCSI_IOCTL_TAGGED_DISABLE void 0x00005385 SCSI_IOCTL_PROBE_HOST const int // MORE // <include/linux/smb_fs.h> 0x80027501 SMB_IOC_GETMOUNTUID uid_t * // <include/linux/sockios.h> 0x0000890B 0x0000890C 0x00008910 0x00008911 0x00008912 0x00008913 0x00008914 0x00008915 0x00008916 0x00008917 0x00008918 0x00008919 0x0000891A 0x0000891B 0x0000891C 0x0000891D 0x0000891E 0x0000891F 0x00008920 0x00008921 0x00008922 0x00008923 0x00008924 0x00008925 0x00008926 0x00008927 0x00008929 0x00008930 0x00008931 0x00008932 0x00008940 0x00008941 0x00008950 0x00008951 0x00008952 0x00008960 0x00008961 SIOCADDRT const struct rtentry * // MORE SIOCDELRT const struct rtentry * // MORE SIOCGIFNAME char [] SIOCSIFLINK void SIOCGIFCONF struct ifconf * // MORE I-O SIOCGIFFLAGS struct ifreq * // I-O SIOCSIFFLAGS const struct ifreq * SIOCGIFADDR struct ifreq * // I-O SIOCSIFADDR const struct ifreq * SIOCGIFDSTADDR struct ifreq * // I-O SIOCSIFDSTADDR const struct ifreq * SIOCGIFBRDADDR struct ifreq * // I-O SIOCSIFBRDADDR const struct ifreq * SIOCGIFNETMASK struct ifreq * // I-O SIOCSIFNETMASK const struct ifreq * SIOCGIFMETRIC struct ifreq * // I-O SIOCSIFMETRIC const struct ifreq * SIOCGIFMEM struct ifreq * // I-O SIOCSIFMEM const struct ifreq * SIOCGIFMTU struct ifreq * // I-O SIOCSIFMTU const struct ifreq * OLD_SIOCGIFHWADDR struct ifreq * // I-O SIOCSIFHWADDR const struct ifreq * // MORE SIOCGIFENCAP int * SIOCSIFENCAP const int * SIOCGIFHWADDR struct ifreq * // I-O SIOCGIFSLAVE void SIOCSIFSLAVE void SIOCADDMULTI const struct ifreq * SIOCDELMULTI const struct ifreq * SIOCADDRTOLD void SIOCDELRTOLD void SIOCDARP const struct arpreq * SIOCGARP struct arpreq * // I-O SIOCSARP const struct arpreq * SIOCDRARP const struct arpreq * SIOCGRARP struct arpreq * // I-O
0x00008962 SIOCSRARP const struct arpreq * 0x00008970 SIOCGIFMAP struct ifreq * // I-O 0x00008971 SIOCSIFMAP const struct ifreq * // <include/linux/soundcard.h> 0x00005100 0x00005101 0xC08C5102 0xC0045103 0x80045104 0x80045105 0x40045106 0x40285107 0x40045108 0x40045109 0x8004510A 0x8004510B 0xC074510C 0x4004510D 0xC004510E 0x4004510F 0xCFB85110 0x00005111 0x40085112 0xC0045401 0x00005402 0x00005403 0x00005404 0xC0045405 0xC0045406 0x40045407 0x40045408 0xCFB85001 0xC0046D00 0xC0046D01 0xC0216D02 0x00005000 0x00005001 0xC0045002 0xC0045003 0xC0045004 0xC0045006 0xC0045007 0x00005008 0xC0045009 SNDCTL_SEQ_RESET void SNDCTL_SEQ_SYNC void SNDCTL_SYNTH_INFO struct synth_info * // I-O SNDCTL_SEQ_CTRLRATE int * // I-O SNDCTL_SEQ_GETOUTCOUNT int * SNDCTL_SEQ_GETINCOUNT int * SNDCTL_SEQ_PERCMODE void SNDCTL_FM_LOAD_INSTR const struct sbi_instrument * SNDCTL_SEQ_TESTMIDI const int * SNDCTL_SEQ_RESETSAMPLES const int * SNDCTL_SEQ_NRSYNTHS int * SNDCTL_SEQ_NRMIDIS int * SNDCTL_MIDI_INFO midi_info * // I-O SNDCTL_SEQ_THRESHOLD const int * SNDCTL_SYNTH_MEMAVL int * // I-O SNDCTL_FM_4OP_ENABLE const int * SNDCTL_PMGR_ACCESS struct patmgr_info * // I-O SNDCTL_SEQ_PANIC void SNDCTL_SEQ_OUTOFBAND const struct seq_event_rec * SNDCTL_TMR_TIMEBASE int * // I-O SNDCTL_TMR_START void SNDCTL_TMR_STOP void SNDCTL_TMR_CONTINUE void SNDCTL_TMR_TEMPO int * // I-O SNDCTL_TMR_SOURCE int * // I-O SNDCTL_TMR_METRONOME const int * SNDCTL_TMR_SELECT int * // I-O SNDCTL_PMGR_IFACE struct patmgr_info * // I-O SNDCTL_MIDI_PRETIME int * // I-O SNDCTL_MIDI_MPUMODE const int * SNDCTL_MIDI_MPUCMD struct mpu_command_rec * // I-O SNDCTL_DSP_RESET void SNDCTL_DSP_SYNC void SNDCTL_DSP_SPEED int * // I-O SNDCTL_DSP_STEREO int * // I-O SNDCTL_DSP_GETBLKSIZ int * // I-O SOUND_PCM_WRITE_CHANNELS int * // I-O SOUND_PCM_WRITE_FILTER int * // I-O SNDCTL_DSP_POST void SNDCTL_DSP_SUBDIVIDE int * // I-O
0xC004500A 0x8004500B 0xC0045005 0x800C500C 0x800C500D 0x0000500E 0x80045002 0x80045006 0x80045005 0x80045007 0x00004300 0xCFB04301 0xC0144302 0xC0144303 0x40144304 0x40144305 0xC0144306 0xC0144307 0x4FA44308 0x8FA44309 0x80044D00 0x80044D01 0x80044D02 0x80044D03 0x80044D04 0x80044D05 0x80044D06 0x80044D07 0x80044D08 0x80044D09 0x80044D0A 0x80044D0B 0x80044D0C 0x80044D0D 0x80044D0E 0x80044D0F 0x80044D10 0x80044D1C 0x80044D1D 0x80044D1E 0x80044DFF 0x80044DFE 0x80044DFD 0x80044DFB 0x80044DFC
SNDCTL_DSP_SETFRAGMENT int * // I-O SNDCTL_DSP_GETFMTS int * SNDCTL_DSP_SETFMT int * // I-O SNDCTL_DSP_GETOSPACE struct audio_buf_info * SNDCTL_DSP_GETISPACE struct audio_buf_info * SNDCTL_DSP_NONBLOCK void SOUND_PCM_READ_RATE int * SOUND_PCM_READ_CHANNELS int * SOUND_PCM_READ_BITS int * SOUND_PCM_READ_FILTER int * SNDCTL_COPR_RESET void SNDCTL_COPR_LOAD const struct copr_buffer * SNDCTL_COPR_RDATA struct copr_debug_buf * // I-O SNDCTL_COPR_RCODE struct copr_debug_buf * // I-O SNDCTL_COPR_WDATA const struct copr_debug_buf * SNDCTL_COPR_WCODE const struct copr_debug_buf * SNDCTL_COPR_RUN struct copr_debug_buf * // I-O SNDCTL_COPR_HALT struct copr_debug_buf * // I-O SNDCTL_COPR_SENDMSG const struct copr_msg * SNDCTL_COPR_RCVMSG struct copr_msg * SOUND_MIXER_READ_VOLUME int * SOUND_MIXER_READ_BASS int * SOUND_MIXER_READ_TREBLE int * SOUND_MIXER_READ_SYNTH int * SOUND_MIXER_READ_PCM int * SOUND_MIXER_READ_SPEAKER int * SOUND_MIXER_READ_LINE int * SOUND_MIXER_READ_MIC int * SOUND_MIXER_READ_CD int * SOUND_MIXER_READ_IMIX int * SOUND_MIXER_READ_ALTPCM int * SOUND_MIXER_READ_RECLEV int * SOUND_MIXER_READ_IGAIN int * SOUND_MIXER_READ_OGAIN int * SOUND_MIXER_READ_LINE1 int * SOUND_MIXER_READ_LINE2 int * SOUND_MIXER_READ_LINE3 int * SOUND_MIXER_READ_MUTE int * SOUND_MIXER_READ_ENHANCE int * SOUND_MIXER_READ_LOUD int * SOUND_MIXER_READ_RECSRC int * SOUND_MIXER_READ_DEVMASK int * SOUND_MIXER_READ_RECMASK int * SOUND_MIXER_READ_STEREODEVS int * SOUND_MIXER_READ_CAPS int *
0xC0044D00 0xC0044D01 0xC0044D02 0xC0044D03 0xC0044D04 0xC0044D05 0xC0044D06 0xC0044D07 0xC0044D08 0xC0044D09 0xC0044D0A 0xC0044D0B 0xC0044D0C 0xC0044D0D 0xC0044D0E 0xC0044D0F 0xC0044D10 0xC0044D1C 0xC0044D1D 0xC0044D1E 0xC0044DFF
SOUND_MIXER_WRITE_VOLUME int * // I-O SOUND_MIXER_WRITE_BASS int * // I-O SOUND_MIXER_WRITE_TREBLE int * // I-O SOUND_MIXER_WRITE_SYNTH int * // I-O SOUND_MIXER_WRITE_PCM int * // I-O SOUND_MIXER_WRITE_SPEAKER int * // I-O SOUND_MIXER_WRITE_LINE int * // I-O SOUND_MIXER_WRITE_MIC int * // I-O SOUND_MIXER_WRITE_CD int * // I-O SOUND_MIXER_WRITE_IMIX int * // I-O SOUND_MIXER_WRITE_ALTPCM int * // I-O SOUND_MIXER_WRITE_RECLEV int * // I-O SOUND_MIXER_WRITE_IGAIN int * // I-O SOUND_MIXER_WRITE_OGAIN int * // I-O SOUND_MIXER_WRITE_LINE1 int * // I-O SOUND_MIXER_WRITE_LINE2 int * // I-O SOUND_MIXER_WRITE_LINE3 int * // I-O SOUND_MIXER_WRITE_MUTE int * // I-O SOUND_MIXER_WRITE_ENHANCE int * // I-O SOUND_MIXER_WRITE_LOUD int * // I-O SOUND_MIXER_WRITE_RECSRC int * // I-O
// <include/linux/umsdos_fs.h> 0x000004D2 0x000004D3 0x000004D4 0x000004D5 0x000004D6 0x000004D7 0x000004D8 0x000004D9 0x000004DA 0x000004DB 0x000004DC UMSDOS_READDIR_DOS struct umsdos_ioctl * // I-O UMSDOS_UNLINK_DOS const struct umsdos_ioctl * UMSDOS_RMDIR_DOS const struct umsdos_ioctl * UMSDOS_STAT_DOS struct umsdos_ioctl * // I-O UMSDOS_CREAT_EMD const struct umsdos_ioctl * UMSDOS_UNLINK_EMD const struct umsdos_ioctl * UMSDOS_READDIR_EMD struct umsdos_ioctl * // I-O UMSDOS_GETVERSION struct umsdos_ioctl * UMSDOS_INIT_EMD void UMSDOS_DOS_SETUP const struct umsdos_ioctl * UMSDOS_RENAME_DOS const struct umsdos_ioctl *
// <include/linux/vt.h> 0x00005600 0x00005601 0x00005602 0x00005603 0x00005604 0x00005605 0x00005606 VT_OPENQRY int * VT_GETMODE struct vt_mode * VT_SETMODE const struct vt_mode * VT_GETSTATE struct vt_stat * VT_SENDSIG void VT_RELDISP int VT_ACTIVATE int
VT_WAITACTIVE int VT_DISALLOCATE int VT_RESIZE const struct vt_sizes * VT_RESIZEX const struct vt_consize *
// More arguments. Some ioctl's take a pointer to a structure which contains additional pointers. These are documented here in alphabetical order. CDROMREADAUDIO takes an input pointer 'const struct cdrom_read_audio *'. The 'buf' field points to an output buffer of length CDROMREADCOOKED, CDROMREADMODE1, CDROMREADMODE2, and CDROMREADRAW take an input pointer 'const struct cdrom_msf *'. They use the same pointer as an output pointer to 'char []'. The length varies by request. For CDROMREADMODE1, most drivers use 'CD_FRAMESIZE', but the Optics Storage driver uses 'OPT_BLOCKSIZE' instead (both have the numerical value 2048). CDROMREADCOOKED char [CD_FRAMESIZE] CDROMREADMODE1 char [CD_FRAMESIZE or OPT_BLOCKSIZE] CDROMREADMODE2 char [CD_FRAMESIZE_RAW0] CDROMREADRAW char [CD_FRAMESIZE_RAW] EQL_ENSLAVE, EQL_EMANCIPATE, EQL_GETSLAVECFG, EQL_SETSLAVECFG, EQL_GETMASTERCFG, and EQL_SETMASTERCFG take a 'struct ifreq *'. The 'ifr_data' field is a pointer to another structure as follows: EQL_ENSLAVE const struct slaving_request * EQL_EMANCIPATE const struct slaving_request * EQL_GETSLAVECFG struct slave_config * // I-O EQL_SETSLAVECFG const struct slave_config * EQL_GETMASTERCFG struct master_config * EQL_SETMASTERCFG const struct master_config * FDRAWCMD takes a 'struct floppy raw_cmd *'. If 'flags & FD_RAW_WRITE' is non-zero, then 'data' points to an input buffer of length 'length'. If 'flags & FD_RAW_READ' is non-zero, then 'data' points to an output buffer of length 'length'. GIO_FONTX and PIO_FONTX take a 'struct console_font_desc *' or a a buffer of 'char [charcount]'. This is an output buffer for GIO_FONTX and an input buffer for PIO_FONTX. GIO_UNIMAP and PIO_UNIMAP take a 'struct unimapdesc *' or a of 'struct unipair [entry_ct]'. This is an output buffer for GIO_UNIMAP and an input buffer for PIO_UNIMAP.
KDADDIO, KDDELIO, KDDISABIO, and KDENABIO enable or disable access to I/O ports. They are essentially alternate interfaces to 'ioperm'. KDMAPDISP and KDUNMAPDISP enable or disable memory mappings or I/O port access. They are not implemented in the kernel. SCSI_IOCTL_PROBE_HOST takes an input pointer 'const int *', which is a length. It uses the same pointer as an output pointer to a 'char []' buffer of this length. SIOCADDRT and SIOCDELRT take an input pointer whose type depends on the protocol: protocols const struct rtentry * AX.25 const struct ax25_route * NET/ROM const struct nr_route_struct * SIOCGIFCONF takes a 'struct ifconf *'. The 'ifc_buf' field points to a buffer of length 'ifc_len' bytes, into which the kernel writes a list of type 'struct ifreq []'. SIOCSIFHWADDR takes an input pointer whose type depends on the protocol: Most protocols; const struct ifreq * AX.25 const char [AX25_ADDR_LEN] TIOCLINUX takes a 'const char *'. It uses this to distinguish several independent sub-cases. In the table below, 'N + foo' means 'foo' after an N-byte pad. 'struct selection' is implicitly defined in TIOCLINUX-2 1 + const struct selection * TIOCLINUX-3 void TIOCLINUX-4 void TIOCLINUX-5 4 + const struct { long [8]; } * TIOCLINUX-6 char * TIOCLINUX-7 char * TIOCLINUX-10 1 + const char * // Duplicate ioctls This list does not include ioctls in the range SIOCDEVPRIVATE and SIOCPROTOPRIVATE. 0x00000001 FDSETPRM FIBMAP 0x00000002 FDDEFPRM FIGETBSZ 0x00005382 CDROMAUDIOBUFSIZ SCSI_IOCTL_GET_IDLUN
<--Appendix D
Table of Contents
Appendix F-->
<--Appendix E
Table of Contents
Glossary-->
a-finali a-flteio a-fwteio a-inteio a-interr a-intnam a-ioexce a-iwteio a-lfteio a-lfwtio a-liteio a-liwtio a-llftio a-llfwti a-llitio a-lliwti a-ncelfu a-ngcefu a-ngcoty a-ngelfu
Standard Ada controlled tagged record package Ada.Float_Text_IO Instantiated Text_IO for floats Instantiated Wide_Text_IO for Ada.Float_WideText_IO floats Instantiated Text_IO for Ada.Integer_Text_IO integers Standard Ada signal handling Ada.Interrupts package Ada.Interrupts.Names Linux signal names I/O exceptions used in std Ada.IO_Exceptions packages Instantiated Wide_Text_IO for Ada.Integer_Wide_Text_IO integers Instantiated Text_IO for long Ada.Long_Float_Text_IO floats Instantiated Wide_Text_IO for Ada.Long_Float_Wide_Text_IO long floats Instantiated Text_IO for long Ada.Long_Integer_Text_IO integers Instantiated Wide_Text_IO for Ada.Long_Integer_Wide_Text_IO long integers Instantiated Text_IO for long Ada.Long_Long_Float_Text_IO long floats Inst. Wide_Text_IO for long Ada.Long_Long_Float_Wide_Text_IO long floats Inst. Text_IO for long long Ada.Long_Long_Integer_Text_IO integers Inst. Wide_Text_IO for long Ada.Long_Long_Integer_Wide_Text_IO long integers Inst. of std ops for complex Ada.Numerics.Complex_Elementary_Function nbrs Generic package of std ops for Ada.Numerics.Generic_Complex_Elementary_Functions complex nbrs Generic complex numbers Ada.Numerics.Generic_Complex_Types package Generic std ops for complex Ada.Numerics.Generic_Elementary_Functions numbers Ada.Finalization
a-nlcefu a-nlcoty a-nlelfu a-nllcef a-nllcty a-nllefu a-nscefu a-nscoty a-nselfu a-nucoty a-nudira a-nuelfu a-nuflra a-numaux a-numeri a-reatim a-retide a-sequio a-sfteio a-sfwtio a-siocst
Ada.Numerics.Long_Complex_Elementary_Functions Ada.Numerics.Long_Complex_Types Ada.Numerics.Long_Elementary_Functions Ada.Numerics.Long_Long_ Complex_Elementary_Functions Ada.Numerics.Long_Long_ Complex_Types Ada.Numerics.Long_Long_ Elementary_Functions Ada.Numerics.Short_Complex_ Elementary_Functions Ada.Numerics.Generic_Complex_Types Ada.Numerics.Short_Elementary_Functions Ada.Numerics.Complex_Types Ada.Numerics.Discrete_Random Ada.Numerics.Elementary_Function Ada.Numerics.Float_Random Ada.Numerics.Aux Ada.Numerics Ada.Real_Time Ada.Real_Time.Delays Ada.Sequential_IO Ada.Short_Float_Text_IO Ada.Short_Float_Wide_Text_IO Ada.Sequential_IO.C_Streams
Inst. of std ops for long complex nbrs Instantiation of long float complex nbrs Instantiation of std ops for long floats Inst. of std ops for long long complex nbrs Instantiation of long long float complex nbrs Inst. of std ops for long long floats Inst. of std ops for short float complex nbrs Instantiation of short float complex nbrs Inst. of std ops for short floats Instantiation of float complex numbers Generic integer random number package Inst. of std ops for float complex nbrs Floating point random number package Internal use Defn's of Pi and epsilon Real-time timing declarations Sleeping using real-time types Standard Ada generic sequential I/O package Instantiated Text_IO package for short floats Instantiated Wide_Text_IO package for short floats Generic package for reading/writing sequential C files
a-siteio a-siwtio a-ssicst a-ssitio a-ssiwti a-stmaco a-storio a-strbou a-stream a-strfix a-string a-strmap a-strsea a-strunb a-ststio a-stunau a-stwibo a-stwifi a-stwima a-stwise a-stwiun a-suteio
Ada.Short_Integer_Text_IO Ada.Short_Integer_Wide_Text_IO Ada.Streams.Stream_IO.C_Streams Ada.Short_Short_Integer_Text_IO Ada.Short_Short_Integer_ Wide_Text_IO Ada.Strings.Maps.Constants Ada.Storage_IO Ada.Strings.Bounded Ada.Streams Ada.Strings.Fixed Ada.Strings Ada.Strings.Maps Ada.Strings.Search Ada.Strings.Unbounded Ada.Streams.Stream_IO Ada.Streams.Unbounded.Aux Ada.Strings.Wide_Bounded Ada.Strings.Wide_Fixed Ada.Strings.Wide_Maps Ada.Strings.Wide_Search Ada.Strings.Wide_Unbounded Ada.Strings.Unbounded.Text_IO
a-swmwco Ada.Strings.Wide_Maps.Wide_Constant
Instantiated Text_IO package for short integers Inst. Wide_Text_IO package for short integers Package for reading/writing C streams Inst. Text_IO package for short short integers Inst. Wide_Text_IO package for short short integers Upper_Set, Lower_Set and other char mappings Standard Ada bounded strings package Standard Ada streams package Standard Ada fixed strings package Standard Ada string defn's Standard Ada string mapping package Internal Use Standard Ada unbounded strings package Standard Ada streams I/O package Additional unbounded string subprograms Wide bounded strings package Wide fixed strings package Wide version of strings.maps Internal Use Wide unbounded strings package Unbounded strings package Upper_Set, Lower_Set and other wide char mappings
a-swuwti a-sytaco a-tags a-tasatt a-taside a-teioed a-textio a-ticoau a-ticoio a-tideau a-tideio a-tienau a-tienio a-tifiio a-tiflau a-tiflio a-tigeau a-tiinau a-tiinio a-timoau a-timoio a-tiocst a-titest a-unccon a-uncdea a-witeio
Ada.Strings.Wide_Unbounded.Wide_Text_IO Ada.Synchronous_Task_Control Ada.Tags Ada.Task_Attributes Ada.Task_Identification Ada.Text_IO.Editing Ada.Text_IO Ada.Text_IO.Complex_Aux Ada.Text_IO.Complex_IO Ada.Text_IO.Decimal_Aux Ada.Text_IO.Decimal_IO Ada.Text_IO.Enumeration_Aux Ada.Text_IO.Enumeration_IO Ada.Text_IO.Fixed_IO Ada.Text_IO.Float_Aux Ada.Text_IO.Float_IO Ada.Text_IO.Generic_Aux Ada.Text_IO.Integer_Aux Ada.Text_IO.Integer_IO Ada.Text_IO.Modular_Aux Ada.Text_IO.Modular_IO Ada.Text_IO.C_Streams Ada.Text_IO.Text_Streams Ada.Unchecked_Conversion Ada.Unchecked_Deallocation Ada.Wide_Text_IO
Wide unbounded strings package Subprograms to synchronize tasks Standard Ada tag package Set/get task attributes Task ID package Package for formatted Text_IO Standard generic Text_IO package Basic long long complex I/O package Generic Text_IO package for complex numbers Internal Use Internal Use Internal Use Internal Use Internal Use Internal Use Internal Use Internal Use Internal Use Internal Use Internal Use Internal Use Text_IO for reading/writing C text files Text_IO stream definition Standard Ada unchecked conversions subprogram Standard Ada unchecked deallocation subprogram Text_IO package for wide characters
a-wtcoau a-wtcoio a-wtcstr a-wtdeau a-wtdeio a-wtedit a-wtenau a-wtenio a-wtfiio a-wtflau a-wtflio a-wtgeau a-wtinau a-wtinio a-wtmoau a-wtmoio a-wttest g-busora g-busorg g-calend g-casuti g-catiio g-comlin g-curexc
Ada.Wide_Text_IO.Complex_Aux Ada.Wide_Text_IO.Complex_IO Ada.Wide_Text_IO.C_Streams Ada.Wide_Text_IO.Decimal_Aux Ada.Wide_Text_IO.Decimal_IO Ada.Wide_Text_IO.Editing Ada.Wide_Text_IO.Enumeration_Aux Ada.Wide_Text_IO.Enumeration_IO Ada.Wide_Text_IO.Fixed_IO Ada.Wide_Text_IO.Float_Aux Ada.Wide_Text_IO.Float_IO Ada.Wide_Text_IO.Generic_Aux Ada.Wide_Text_IO.Integer_Aux Ada.Wide_Text_IO.Integer_IO Ada.Wide_Text_IO.Modular_Aux Ada.Wide_Text_IO.Modular_IO Ada.Wide_Text_IO.Text_Streams GNAT.Bubble_Sort_A GNAT.Bubble_Sort_G GNAT.Calendar GNAT.Case_Util GNAT.Calendar.Time_IO GNAT.Command_Line GNAT.Current_Exception
Basic Text_IO package for long long float complex numbers Generic Wide_Text_IO package for complex numbers Wide_Text_IO package for reading/writing wide C text files Internal Use Internal Use Package for formatted Wide_Text_IO Internal Use Internal Use Internal Use Internal Use Internal Use Used by wide character IO generic packages Internal Use Internal Use Internal Use Internal Use Definition of wide text I/O streams Bubblesort using access types Generic bubblesort package Ada.Calendar plus day of week, second duration, etc. Character case conversion without Characters.Handling Formatted I/O for time values, like Linux strftime() More powerful than Ada.Command_Line, like Linux getopts() DEC Ada 83 / VADS Ada style exception handling
g-debpoo g-debuti g-dirope g-except g-flocon g-hesora g-hesorg g-htable g-io g-io_aux g-locfil g-os_lib g-regexp
GNAT.Debug_Pools GNAT.Debug_Utilities GNAT.Directory_Operations GNAT.Exceptions GNAT.Float_Control GNAT.Heap_Sort_A GNAT.Heap_Sort_G GNAT.HTable GNAT.IO GNAT.IO_Aux GNAT.Lock_Files GNAT.OS_Lib GNAT.Regexp
Storage pool with allocation and dereference error checking Program debugging utilities: eg. system address output Linux directory changing, creating, walking Ada predefined exceptions for pure packages Set the floating point processor back to the Gnat defaults Heapsort package using access types Generic heapsort package Generic hash table package Text I/O for preelaborated packages Get_Line functions and file existence test for Text_IO Package for locking files/directories with retry capability Package for common Linux O/S operations Simple package for Linux globbing pattern matching and Ada BNF Package providing full UNIX regular expression pattern matching Check for a typo, similar to my Typo_Of in TextTools Package providing SPITBOL pattern matching SPITBOL string processing data structures Boolean type SPITBOL table Integer type SPITBOL table
g-sptavs g-table g-tasloc g-thread g-traceb g-trasym i-c i-cexten i-cobol i-cpoin i-cpp i-csthre i-cstrea i-cstrin i-fortra i-os2err i-os2lib i-os2syn i-os2th i-pacdec i-vxwork i-addimg s-arit64
GNAT.Spitbol.Table_VString GNAT.Table GNAT.Task_Lock GNAT.Threads GNAT.Traceback GNAT.Traceback.Symbolic Interfaces.C Interfaces.C.Extensions Interfaces.COBOL Interfaces.C.Pointers Interfaces.CPP Interfaces.C.Sthreads Interfaces.C_Streams Interfaces.C.Strings Interfaces.Fortran Interfaces.OS2Lib.Errors Interfaces.OS2Lib Interfaces.OS2Lib.Synchronization Interfaces.OS2Lib.Threads Interfaces.Packed_Decimal Interfaces.VxWorks System.Address_Image System.Arith_64
Unbounded string type SPITBOL table Dynamic one-dimensional arrays package Package for protecting critical regions in tasks Import C threads as Ada tasks Non-symbolic traceback support Symbolic tracebacks Standard Ada C interfacing package Additional C types not covered by Interfaces.C Standard Ada COBOL interfacing package C style pointer arithmetic GNAT C++ class interfacing package Dummy package Thin binding to C sequential files GNAT C string operations Standard Ada Fortran interfacing package OS/2 error codes OS/2 support OS/2 support OS/2 support Packed decimal fixed types support for Machine_Radix 10 computers VxWords API support Function returning a system.address image 64 bit arithmetic with support for intermediate results > 64 bits
s-atacco
System.Address_To_Access_Conversions
s-bitops.ads System.Bit_Ops s-chepoo i-exngen s-pooglo s-pooloc s-powtab s-stoele System.Checked_Pools Exn_Float_Type System.Pool_Global System.Pool_Local System.Powten_Table System.Storage_Elements
Converting between simple pointers and access types Low-level bitwise operations for 1, 2 or 4 bytes Storage pool with a function called for any dereference Generic function for signed integer exponentiation normal heap for GNAT global access types normal heap for GNAT local access types table of powers of 10 Standard Ada package
<--Appendix E
Table of Contents
Glossary-->
<--Appendix F
Table of Contents
End of Book
Glossary
AARM The Annotated Ada Reference Manual contains the entire text of the Ada 95 standard (ISO/IEC 8652:1995(E)), plus various annotations. It is intended primarily for compiler writers, validation test writers, and other language lawyers. The annotations include detailed rationale for individual rules and explanations of some of the more arcane interactions among the rules. Ada 9X the working title of Ada 95 before the language was completed. ASIS The Ada Semantic Interface Specification is a layered vendor-independent open architecture. ASIS queries and services provide a consistent interface to information within the Ada compilation environment. Dynamic Polymorphism Polymorphism implemented at run-time using a "tag" to determine the type of item; tagged records, objects. Inheritance Creating new items containing an original item's features without changing the original item. LRM is the abbreviated name of the Language Reference Manual, sometimes called Ada Reference Manual. "LRM" was often used in the days of Ada 83; "RM" or "rm95" Multiple Inheritance Creating new items from two or more original item's features without changing the original item. Polymorphism A means of factoring out differences amongst a collection of items so that programs may be written in terms of the common features. RM see LRM. RM95 see LRM. Static Polymorphism Polymorphism implemented at compile-time; generics.
<--Appendix F
Table of Contents
End of Book