2007
| Revision History | |
|---|---|
| Revision 1.3 | 09 June 2008 |
| Clean up for DocBook publishing. | |
| Revision 1.2 | 22 August 2007 |
| Reorganize and rework. | |
| Revision 1.1 | 19 June 2007 |
| Converted to DocBook, plus grammatical edits. | |
| Revision 1.0 | 22 May 2007 |
| First ODF version. | |
Table of Contents
A good software is coded properly. The software not only does its job well, but is also easy to add to, maintain and debug. Many great software is a collaboration of many developers, located at one single locations or distributed across continents and seas. The different coding styles are very much differed from individuals taste and culture. Every developers have habitually developed unique coding style in their native environment. The codes written by one developer may seem to be unfamiliar by another developer. Inheriting or needing to maintain and make changes to the codes that require a lot of energy to decipher, will end up trawling through lines after lines of code that doesn't make its purpose clear. Multiform codes waste countless and efforts hours of developers and thwart development progress. Looking through unfamiliar codes is much easier if it is laid out well and everything is neatly commented with details that explains any complicated construct and the reasoning behind them.
The maintainable code is most talk about among those involved in software development. A coding standards is in regardless of any programming languages. This Ada Coding Standards is specifically written for the development team at organization where Ada is the primary de facto programming language used for software development.
Ada is known as an excellent design and implementation language for all kinds of systems. Its semantic and structure make the Ada source code easy to read, understand and maintain. Because Ada is designed with software engineering in mind, Ada is also an excellent documentation language. More than source code for a compiler to compile to machine executable code, the Ada source code can also contain information about the code itself to be read and understood by humans.
This ACS (Ada Coding Standards) document describes guidelines to organize the Ada source code into a clean format. Well formatted Ada code provides visual aids in displaying important details of the code, together with additional information (comments) to help reading, understanding, and maintaining.
The examples are extracted from some project sources with permission. This document is a work-in-progress.
There are many editors available for writing program code. Many of them have language support for Ada. These editors provide a set of useful functions to Ada developers which increase productivity in many ways. These functions include:
Syntax sensitive text coloring - Quickly identify keywords, identifiers, numbers, text and etc.
Auto indentation - Context sensitive indentation reduces keystroke and developers can concentrate better in coding without worrying about code formatting.
Auto case adjust - Automatically recognizes and diffrentiate keywords and lexicons from user-defined names and identifiers and automatically capitalize first character in names and identifiers.
Auto completion of parantheses - Helps to reduce incomplete parantheses in complex syntax.
The choice of editor is completely a sole determination of developers according to their likings. However, the use of standard editor will be of benefits because editor's preference or configuration can be shared and standardized for adhering to a uniform coding standard.
Emacs is a very powerful editor and well support in the Unix environment including Linux, FreeBSD, Mac OS X and any other UNIX flavors. It is widely accepted in both university and industrial software development. It is completely customizable according to every needs and support many languages. Emacs has very good and matured version control and it works seemlessly with SCM system such as CVS and SVN. Developer can execute commands, e.g. compile or build, from within Emacs itself without having to switch to terminal or other tools. Syntax sensitive text coloring support together with auto identation, auto case adjust, and auto completion of parantheses are superb.
Xcode is an IDE (Integrated Development Environment by Apple and is supported on on Mac OS X. It comes with every Mac OS X installation disks as an optional installation. Xcode is a humongous IDE supporting C/C++, Objective-C, Ruby, Python, and of course AppleScript by default. MacAda provides downloadable Ada gnat compiler which integrates with Xcode and thus Xcode is able to support development with Ada. The Ada support in Xcode is not official and usually is bleeding-edge but usually working release by MacAda volunteers.
Xcode does not have good support in auto indentation and case adjust. Its SCM support is superlative with many SCM features built into it.
GPS or GNAT Programming Studio is written entirely with Ada. It is a tightly integrated IDE built for Ada in mind. Its advanced features are designed specifically for GNAT Pro and Ada developers. GPS offers many advanced features including multi-language support (including Ada, C and C++) on a wide range of environment for both native and cross-development platforms including UNIX, Linux and Windows.
GPS is suited for large and complex development with many powerful and useful features including viusal comparison tools, auto generate documentation from source, remote debugging/compilation, visualization of Ada metrics and many several others. GPS is aimed at streamlining entire Ada development process from inital coding stage through testing, debugging, system integration and maintenance.
GPS provides support for configuration management through an interface to third-party Version Control Systems, and supports a variety of platforms, including Alpha Tru64, Altix Linux, MIPS-IRIX, PA-RISC HP-UX, SPARC Solaris, x86 GNU Linux, x86 Solaris, and x86 Windows.
With many editors and IDEs around, developers have wide range of choices to choose. Most developers have developed a strong personal taste with their editors of choice. The first problem is the choice of editor for each individual developer. Fortunately, most editors are flexible enough to accomodate both personal choice and coding standards. The most obvious customizable option in the editors is the tabulation or identation.
In order to make this as simple as possible, we will be using spaces, not tabs. Indent 3 spaces per indentation level and organize code into blocks. Make sure when saving source codes, all indentation are saved as spaces not tabs. A well formatted codes with proper block indentation is easier to read as in Example 1. Some editor like Emacs can automatically indent while you type to the new line.
Besides proper indentation at the beginning of every lines to form a readable block, there will be good practice to format in every each lines to separate operators and semantics with proper spacing, position and alignment.
Limit line length to 130 columns. When a line is longer than 130 characters (including spaces), have a line break at proper position, such as after a binary operator, before or after an open parantheses or in the case of passing too many variables with long name to a subprogram. See Example 2.
There must be at least one white space on both side of
':', ":=", "=>", and all
binary and unary operators. While multiple space is permitted, it is
generally discouraged except to conform to other standards, such as
lining things up. There must be no white space between unary operators
that are symbols ("+", "-") and their
operands. Put white spaces between multiple parentheses of the same
type. For example, "Factor ( ( X + Y ) * Z );".
The "is" of a procedure body should be on the same
line as "procedure" if that's possible; when it can't be on
the same line, it must be on a line by itself, lined up with
"procedure" as in Example 1.
The "return T is" of a function body should be on the
same line as "function" if that's possible; when it can't
be on the same line, it must be on a line by itself, lined up with
"function" as in Example 3.
The "when B is" of an entry body should be on the
same line as "entry" if that's possible; when it can't be
on the same line, it must be on a line by itself, lined up with
"entry".
The "then" of an if (or
elsif) should be on the same line as "if" (or
"elsif") if that's possible; when it can't be on the same
line, it must be on a line by itself, lined up with "if"
(or "elsif"). See Example 1 and Example 4.
The rational is to make reserved word visually distinguishable from
other elements of the program. Reserved words must be all lower-case
except when used as an attribute, in which case they must have an Initial
Capital Letter (George'Access;
Martha'Range).
Sometimes it is difficult to think up a good and meaningful name for identifier. Try to think of a name of closest meaning. See Example 5.
Do not use CamelCase or Hungarian Notation (szFoo).
Use underscores to separate words. Use mixed case for all identifiers, a
capital letter beginning every word separated by underscore. Consecutive
upper case letters in identifiers may only be used to represent acronyms
from the Official List of Acceptable Acronyms (OLAA). An acronym must be
a complete word of an identifier. "Text_IO" is acceptable,
whereas "BString" is not. The best practice is to construct
the OLAA based on software design specification before coding.
Attributes must follow the formatting rules for identifiers above. The use of editor's auto-capitalization feature such as Ada-mode in Emacs, makes coding easier and comply to the standards.
Associate names with loops when they are nested and with any loop
that contains an exit statement. Naming a loop helps
readers to identify the associated end of the loop. This is particularly
helpful when loops are broken over screen or page boundaries. A good
loop name also provides explanation to the algorithm. It is sometimes
difficult to think up a descriptive name for every loop. The benefit in
readability and second thought outweigh the inconvenience of naming the
loop.
When there are nested blocks, it is difficult to determine which
end associates with which block. It is even more difficult
when the blocks are broken across screen and page boundaries. As with
loop names, sometimes, it will be difficult to think up a name for every
block. Again, the benefits in readability and second thought outweigh
the inconvenience of naming the block.
Repeat unit names as comments after "begin",
"exception", "private", "generic"
and "end".
Use verb instead of noun for clarity of subprogram names. Verb
indicates operation. For example, procedure
Calculate_Account_Balance; and procedure
Account_Balance_Calculation;. The former gives more descriptive
information when used as Calculate_Account_Balance; rather
Account_Balance_Calculation;. Under some circumstances, it
is not necessary to use verb for subprogram names, for example,
function Factorial. For more details of subprogram,
parameters and parameter modes, please see Section 5 Subprograms.
Subprograms or referred as procedures and/or functions throughout this document. Since Ada is not only an implementation language, it is also a documentation language, a well-thought and meaningful subprogram names will increase readability of the program source codes. Please refer to Section 4.4 Subprogram Names for further details.
Use named notation for procedures, except where parameter names are
meaningless and named notation would detract from readability
(Ada.Unchecked_Deallocation for instance).
Use positional notation for the first parameter of functions.
Remaining parameters may use positional or named notation, whichever makes
the code clearer. "Is_Member ( Item, Set )" is clear;
"Image ( X, 7 )" may not be, since 7 might be the
Width or the Base parameter. "Image ( X,
Width => 7 )" is better.
Like identifiers, refer to Section 4.1 Identifier Names for further details.
Do not specify parameter modes for functions, but always specify them for procedures and entries. Align ":" and parameter modes when parameters are not on the same line. See Example 6.
Avoid all unnecessary "use" of packages. There must be
no "use" clauses in package specifications. In bodies,
"use type" is acceptable. Acceptable "use"
clauses are limited in scope (to small subprograms or block statements,
Example 7), apply to 3 or more references to the
package, and leave it clear where the abbreviated names are
declared.
Do not "use" parent packages. Their contents are
directly visible without a use clause.
Like many other applications, an Ada application in this case, may contain language elements not of Ada origin. Most notably, an interface to some external library written in C, to enable exchange of information or interface with an external program, for instance, Asterisk. There will be, most likely, some other language element embedded into Ada codes. MySQL for instance, the Ada application not only have a C interface to call MySQL C library functions, but also need to use SQL to manipulate data stored in MySQL database.
Annex B provides specific interface and support to C, COBOL and FORTRAN. This documentation provides a general coding standard for interfacing with these languages.
When importing from libraries written in other languages, always quote the originating location of the import. It will be a good cross-reference and makes tracking back to the source easier. See Example 8.
If you are developing an application with database, you will most
likely to encounter explicit SQL statement in your Ada codes. It is
important to distingusihed Ada keywords from SQL keywords. Use all
capital letter for all SQL keywords and lower case for all SQL
identifiers. Precedes database related identifiers with capitalized
"DB" and capitalize all SQL keywords give instant
distinction of SQL and database elements among Ada codes as in Example 9.
Since SQL is a database language and has its own lexicon and semantic, it is always a good practice to represent SQL codes in more distingusih way rather than as a string embedded in an Ada statement as it is represented in Example 9. Example 10 shows a clearer and more practical approach which has been helpful to developers to distinguish SQL statement from Ada statements and strings.
Example 1. Indentation using 3 spaces. good spacing and alignment
procedure Forward ( L : access List;
Position : in Unsigned := 0 )
is
begin
if Position = 0 then
L.Current := L.Tail;
else
for N in 1 .. Position loop
Move ( L, 1 );
if L.Current = null then
L.Current := L.Tail;
exit;
end if;
end loop;
end if;
end Forward;
Example 2. Line breaks at maximum column width
Account_Balance := ( Fixed_Asset_Balance + Current_Asset_Balance + Stock_Balance + Account_Receivable_Balance ) -
( Total_Liability + Total_Expenses + Future_Dated_Transaction );
Do_Multiple_Processing ( Total_Asset_Balance_Before_Tax => Ledger_1.Total_Asset_Baance_Before_Tax,
Tax_Rate => 0.35,
Total_Asset_Balance_After_Tax => Ledger_1.Total_Asset_Balance_After_Tax );
Example 3. Alignment of return with function
function Recurse_Available ( Total : Integer;
I : Integer;
Conf : Integer_Array;
Rank : Integer_Array;
Lambda : Float_Array;
Mu : Float_Array;
Bad : Integer )
return Long_Long_Float is
Answer : Long_Long_Float := 0.0;
K : Integer;
...
begin
...
end Recurse_Available;
Example 4. Position and alignment of if .. then
if Stock_Balance > 0 then
Stock_Available := True;
elsif Stock_Balance < 0 then
Do_Critical_Reorder;
else
Replenish_Stock;
end if;
...
if Stock_Balance + Account_Receivable_Balance > Fixed_Asset_Balance + Current_Asset_Balance - Cash_In_Hand
then
Do_Something;
elsif Current_Asset_Balance + Fixed_Asset_Balance + Total_Income < Total_Liability + Total_Expenses
then
Do_Something_Nice;
end if;
Example 5. Indentifier names and position alignment
type Speed is new Integer range 0 .. 300; Vehicle_Speed : Speed; Relative_Vehicle_Speed : Speed;
Example 6. Alignments and parameter modes
procedure Dynamic_Sensor_Fusion ( N : in Integer;
F : in Integer;
Value : in out Object_Type;
Bounds : in out Reading_Type;
Data : in out Reading_Type_Array;
Print_Details : in Integer;
Results : out Result_Type )
is
I : Integer;
Cardinality : Integer := 0;
Factors : Integer := 0;
...
begin
...
end Dynamic_Sensor_Fusion;
Example 7. Use clause in limted scope
declare
use Stack;
begin
...
Push ( M );
...
N := Pop;
...
exception
when Error =>
Put ( ”Stack manipulation error.” );
when others =>
Put ( ”Something else went wrong with Stack.” );
end;
Example 8. Importing from a C library (MySQL)
function Mysql_Kill ( Mysql : MYSQL_SERVER; Pid : C.Unsigned_Long ) return C.Int; pragma Import ( C, Mysql_Kill, "mysql_kill" ); -- mysql.h:193
Example 9. SQL statements in Ada code (MySQL)
DB.Connect ( DB_Tms, "localhost", "tms" ); DB_Qid := DB.Query ( DB_Tms, "SELECT first_name,last_name,login_name FROM users;" );
Example 10. Clearly distinguished SQL statement
procedure Get_Employee_Record ( Employee_ID : in String; Employee_Detail : out Employee_Record )
Sql_Get_Employee : constant string := "select * from employee where employee_id = '" & Emplyee_ID & "';";
Qid : Db.Query_Id;
begin
Qid := Db.Query ( DB_Payroll, Sql_Get_Employee );
...
end Get_Employee_Record;