Contents
1. Issue Status (1100.1200.10.1) |
Issue 1.0 - December 22, 1982
Issue 1.0 - The 5ESS Switch® is the first switching product to use the C language. Many people were involved in the development: people who were experienced in C coding, people who had software knowledge but no C experience, and people who had no previous software experience. As a result, a wide variety of programming styles appear in the software product.
This issue was prepared by the following people: M. T. Abdel-Moneim, A. J. Beranek, M. D. Bratcher, D. F. Christian, J. R. Cray, C. P. Lawes, R. H. McCullough, P. V. Polli, S. R. Ringwood, J. A. Rypka, J. R. Towne, Y. Winograd, N. A. Yates.
Issue 2.0 - July 24, 1986
Issue 2.0 of this document includes corrections, modularity changes, and some terminology revisions.
Issue 2.1 - June 15, 1988
Issue 2.1 of this document includes changes which advise 5ESS Switch software programmers of the fact that AT&T source code is an AT&T product which is distributed to customers and maintenance personnel. Standards for source code comments which present a professional and courteous image to these people are established by these changes.
This issue was prepared by E. G. Halline of the Switching Systems Quality Department.
Issue 2.2 - June 25, 1990
Issue 2.2 of this document includes changes which were suggested by IMRs written against this document. These changes include the use of a constant width font for examples, an updated index, and a variety of corrections.
This issue was prepared by the International Code and Code Review Process Team and edited by J. Jay.
Issue 3.0 - June 1, 1991
Issue 3.0 of this document was prepared by the combined efforts of the International Coding Process Team, the U.S. Coding Process Team, and representatives of RTR.
2. Introduction (1100.1200.10.2) |
The 5ESS Switch Coding Standards Task Force was created at the beginning of August 1982, consisting of members from several software development areas. The charter of the Task Force was to develop and document standards for current and future 5ESS Switch code in order to promote uniformity and improve quality of the 5ESS Switch code.
This document contains the Task Force recommended standards for the following areas:
code documentation
naming standards
language features and data types
coding techniques, style and format
Section 3 (Documentation) covers the information that should be given in file and function prologues. Section 4 (Naming Standards) provides rules for the choice of function and data names. Section 5 (Language Features and Data Types) and Section 6 (Coding Techniques, Style, and Format) cover the use of C statements and data types and provide the required layout of the code. The Language Features and Data types section is more oriented toward what parts of the C language should be used for the 5ESS Switch whereas the Coding Techniques, Style, and Format section concentrates more on how the statements should be used. To conclude, some recommended programming tips are given in Section 7.
A major consideration in the creation of the standards was to ensure that the individual software developer can be comfortable with them. In the future, required standards will be added, deleted or modified as more experience and better understanding is gained on the needs of the 5ESS Switch code and the needs of the 5ESS Switch code developers.
All 5ESS Switch software programmers must consider the fact that Lucent Technologies source code is a Lucent Technologies product which is distributed to our customers and maintenance personnel to assist them in understanding the operating programs of the switch and in clearing troubles. It is thus extremely important to present a courteous and professional image in code commentary, both to Lucent Technologies developers and to these external readers.
This document applies to all code that goes out to the customer. As of Issue 3.0, this document represents RTR code development in addition to 5ESS Switch code development. Where a difference exists between RTR and 5ESS Switch code, it is noted.
The coding examples will adhere to the standards specified in this document. For clarity, the category code (see section 4) used will be EX (for EXAMPLE) except when clarifying a detail that pertains to actual naming standards. Otherwise, examples that reflect existing code will be avoided. When a "good" example is compared with a "bad" example, each will be noted with "GOOD PRACTICE" and "BAD PRACTICE", respectively.
For the most part, we have tried to make the examples conform to the standard. The examples are generally simplified in terms of robustness, data definition specific semantics, etc. for brevity.
3. Documentation (1100.1200.10.3) |
Every file (source or header) must begin with a file prologue and every function within a source file must be preceded by a function prologue.
Contents of a new file should be enclosed within the relevant feature constructs. In addition, the user must use the same feature constructs to feature headers files and the related text files.
The following describe the mandatory formats of file and function prologues. The user may add more information to the prologues (for example, the user may wish to describe how the functions in the file relate to the product in the file prologue) but cannot delete information which is marked as "required". CMS/ECMS supporting tools provide the means for obtaining names of software developers who were/are directly responsible for modifications to the source file(s). Therefore developer name should not appear in the file or function prologue.
3.1 File Prologue (1100.1200.10.3) |
The purpose of a file prologue is to identify the file. (Individual function descriptions are found in the function prologues.)
/* * File: (required) the full pathname of the header file or * source code file (relative to the $NODE), e.g.: * * uhdr// .G for 5ESS switch global header files, * /hdr/ .L for 5ESS switch local header files, * / / .h for 5ESS switch module header files, * (xr)head/ .h for RTR global header files, and * / / .c for 5ESS switch C source code files. * / / .c for RTR C source code files. * * Additional Information: (optional) any information which may be useful * for the user, e.g. general description of the * functions in the file as a whole, etc. */
3.2 Function Prologue (1100.1200.10.3) |
The purpose of a function prologue is to provide useful information about the function, i.e. the function's description, return value(s), and any additional information about using the function.
The following is the minimal information that needs to be included within a function prologue. Each subsystem may have additional fields which are considered necessary.
/* * Name: (required) the name of the function. * * Abstract: (required) one/two sentence abstract of the * function performed by the routine. * * Returns: (required) the value(s) returned by the function. * * If the returned value is predefined in a header file, * then the name of the header file where the * value can be found should be given. * * If the function does not return a value, then * 'void' is specified. * * If the function does not return control to the * calling routine, then 'the function does not return' * is specified. * * Description: (required) multi-line description giving a global * picture of the function. * * Additional Information: (optional) any information which may be useful * for the user, e.g. information that may affect * the manner or environment in which the * function should be used, other related functions, * side effects, whether it's part of a loadable package, * etc. Pitfalls, warnings, function operation may * be added here or under separate subheading. */
4. Naming Standards (1100.1200.10.4) |
Naming conventions make programs more understandable by making them easier to read. They can also give information about the nature of the identifier and should describe its use. This section presents standards for identifiers (variables, function names, structure members, etc.) and for source file and 5ESS Switch module names. Some subsystems may define additional standards to follow.
All names must reflect high standards of professionalism and good taste.
4.1 5ESS Switch Category Codes (1100.1200.10.4) |
In 5ESS Switch code, file names, and some identifiers, contain a "category code", which is defined as follows. 5ESS Switch software is divided into "subsystems". Associated with each subsystem are one or more category codes, consisting of two letters. Many category codes have the same name as the subsystem to which they belong (for instance, category code UP for subsystem "up"), and some category codes are used in more than one subsystem. See Attachment 100 for the list of category codes and their associated subsystems. Note that a few category codes have special, restricted uses.
4.2 5ESS Switch Module Names (1100.1200.10.4) |
For modularity subsystems, the module name cannot exceed nine characters. It must consist of the following:
A category code in upper case letters.
4.3 Source File Names (1100.1200.10.4) |
Due to UNIX and ECMS restrictions, a file name cannot exceed 12 characters. It must consist of the following:
A category code (in 5ESS Switch code only) in upper case letters.
A descriptive name, consisting of one or more lower case letters, digits, and underscores.
A period.
A suffix. A list of commonly used suffixes is listed in Attachment 200.
In general, source file names should be descriptive of the function(s) within the file. It is highly recommended that each source file have only one source function. In such a case, the file name should be the same as the function name, up to the first 10 characters. When more than one function is declared in a source file, the cost to build that source file using the "source=" option in the build command is increased. Furthermore, the cost to generate an inspection package with that source file in it increases since the entire file is printed, not just the changed function. These benefits should be carefully considered when adding a second/subsequent function to a source file.
4.4 Identifiers (1100.1200.10.4) |
Identifiers should be short, yet meaningful. With compilers that do not support "flexnames", only the first eight characters of the name are significant. This means that all identifiers must be unique within the first eight characters for the compiler to recognize them as different.
Identifiers cannot match a C language keyword or an M4 keyword. This causes lint warnings (even if M4 is not used on the file). Users of the M4 and NM4 macro processors should be aware that these tools implicitly define a set of macros. These macros differ from those usually defined by 5ESS(R) Switch software header files in that they are in all lower case letters ('index' is an example of one of the implicitly defined M4 macros). Thus, care must be take to avoid defining variables with the same name as these macros. The use of 'index' as an identifier is a particularly troublesome occurrence. The M4 macro preprocessor will replace all occurrences of 'index' with (-1). This can easily be avoided by adding additional characters before the word 'index', such as loop_index. Alternatively, the built-in macros can be undefined using the built-in M4 'undefine' macro. Users of M4 and NM4 must also realize that these macros processors do not by default use the same comment delimiters as C. However, the comment delimiters can be changed using the following pair of statements (each start in column 1 of a line):
changecom() changecom(/*,*/) |
Pointer variables must end with "_ptr". Other variables must not end in "_ptr". For example, a pointer to a node structure can be named node_ptr .
An identifier must consist of the following:
A category code (in 5ESS Switch code only), when required. The table below specifies when a category code is required.
A descriptive name, consisting of one or more letters, digits, and underscores.
The following table shows the naming conventions for identifiers.
Table 1. Naming Conventions
|
Note the following:
There are some macros which used to be functions, and so follow the naming rules for functions (for example, DBfrdtup).
(5ESS Switch only) For enumerations defined outside any function, the members' category codes need not all be the same or match the category code of the tag and typedef . In particular, "DM" is used for the category code of domains ( enum s or otherwise), but never for the enum members.
For convenience of using the output of the "nm" command (namelist command which prints out symbol table information), all structures, unions, and enumerations should be given a tag, even if they also have a typedef . The typedef , if present, must be named the same as the tag. If there is a category code (outside a 5ESS function), the category code must be upper case for the typedef and lower case for the tag. This, however, does not apply for structures or unions which occur within another structure or union. In that case, the embedded structures or unions may have the tag omitted. The below example illustrates this:
typedef struct exTYPE { /* TAG NECESSARY */ struct { /* NO TAG NECESSARY */ EXTYPE1 type1; EXTYPE2 type2; } type; } EXTYPE; |
4.5 Unreferenced Labels (1100.1200.10.4) |
Unreferenced labels are used by developers in 5ESS Switch code so scripts written for lab testing can reference the label instead of a breakpoint number or to force a breakpoint number at a specific line of code that the optimizer had removed. Side-effects from using unreferenced labels are:
Labels force the optimizer to break the code into smaller blocks where the next optimized block starts at the location of the label. Inefficient code generation and a real time performance degradation are the result. The impact will depend on the number of labels in a function and the location of the labels in a function (i.e. inside a loop).
Unreferenced labels clutter code making it more difficult to read and understand.
If not clearly documented, unreferenced labels confuse the developer as to their purpose (i.e was it intentionally placed in the code or did someone remove the goto but forgot the label?).
To avoid these undesirable effects, the following guidelines were established for unreferenced labels.
Permanent unreferenced labels are labels that will remain in the 5ESS Switch code after cutover.
Permanent unreferenced labels are allowed in critical functions used by Load Bringup to debug the bringup session. Such labels should be clearly marked with the following comments.
/* * Unreferenced Label - requested by Load Bringup * Label will remain permanently in 5ESS Switch code. */ |
No more than three labels should exist within a critical function. Non-critical functions should never contain permanent unreferenced labels.
Permanent labels should never be inserted in real time critical code.
Permanent labels should not appear in loops.
Temporary unreferenced labels are labels put in code during the developer's testing stage to be removed at a later time or not included in the official build of a product.
All temporary unreferenced labels should be noted with the appropriate comments.
/* * Unreferenced Label * Temporary use by developer to set testing breakpoints. */ |
All temporary unreferenced labels should be inserted between the C-preprocessor flag, DEBUG, (like so).
#ifdef DEBUG LABEL1: #endif |
Since the official load does not build with the DEBUG flag set, this label is not built as part of the official load. When a developer runs a private build, (s)he must explicitly set the flag to use the label.
Also note that the official load will fail to build if a user inserts a goto for a temporary label that is properly ifdef 'ed.
No more than two temporary unreferenced labels should exist within a function.
Executing "nlint" will warn the developer about every label that is defined but not referenced. The "nlint" listing should be checked to ensure that any label commented as unreferenced is not referenced.
5. Code Maintainability (1100.1200.10.5) |
Code maintainability as an extension of coding standards deals with issues beyond syntax. It addresses issues with code complexity that make C code easier to maintain. Maintainability issues described in subsections 6.2 (Variables/Functions per line) and 6.5 (Stack Usage).
5.1 Expressions for Logic Control (1100.1200.10.5) |
The use of an arithmetic expression for logic control, where zero is taken as false and anything else as true, is not allowed. Instead, an explicit comparison to zero must be made.
BAD CODING PRACTICE:
if ( charge_index )
{
/* Charge the call */
|
GOOD CODING PRACTICE:
if ( charge_index != 0 )
{
/* Charge the call */
|
5.2 Assignment Statements (1100.1200.10.5) |
Embedded assignments and statements with side effects are not allowed. They are harder to read, harder to debug, the order of evaluation may be undefined, and may sometimes create unpredictable results.
BAD CODING PRACTICE: /* * Embedding one assignment inside another. * The following statement is difficult to debug * because a breakpoint cannot be set on the * assignment of the value of "yvar". */ xvar = (yvar = aparm + bparm) + cval; /* * Embedded assignment. * The following statement is difficult to debug * because a breakpoint cannot be set on the * assignment of the value of "b_ptr". */ if ( a_ptr != NULL && ( (b_ptr = a_ptr->next)->value ) != 0 ) { EXfunc( b_ptr->value ); } |
GOOD CODING PRACTICE: yvar = aparm + bparm; xvar = yvar + cval; /* GOOD PRACTICE */ if ( a_ptr != NULL ) { b_ptr = a_ptr->next; if ( b_ptr->value != 0 ) { EXfunc( b_ptr->value ); } } |
The following are examples of an assignment statement and an embedded assignment statement in which the order of evaluation is undefined and they are not allowed.
BAD CODING PRACTICE: /* * The point at which "i" is incremented is compiler dependent. */ array[i++] = i; /* * The point at which the array index is evaluated is * compiler dependent. Consequently, the array index * depends on an unknown value. */ array[weight] = (weight = wparm); |
GOOD CODING PRACTICE: array[i] = i; i++; GOOD CODING PRACTICE: weight = wparm; array[weight] = weight; |
5.3 goto Statement (1100.1200.10.5) |
The use of the goto statement is strongly discouraged. The goto should only be used when absolutely necessary (for example, to go to a single error processing point under abnormal conditions or to break out of several levels of switch , for , and/or while nesting). When used, all goto statements must be clearly commented to indicate the change in program flow. When considering the use of a goto statement, one should first consider a redesign of the code to avoid the use of a goto .
Section 3.9 of reference [a] and chapters 2 and 3 of reference [b] have good discussions on the use of the goto statement.
5.4 Loops (1100.1200.10.5) |
Changing the for loop variable or changing the terminating boundaries in the body of the loop should be avoided. Termination of the loop should depend on a test involving the loop variable.
BAD CODING PRACTICE:
for ( i = 0; i < max; i++ )
{
if ( condition == TRUE )
{
/* Change loop variable */
i += 7; /* BAD PRACTICE */
}
|
The while and do-while constructs are better choices for complicated looping and where a change of the loop variable or terminating expression is required.
The use of break and continue statements is encouraged, especially when the use of a goto is tempting. However, the use of a break or continue statement must be clearly commented to indicate the change in program flow. This is particularly important when nesting loops.
5.5 switch Statement (1100.1200.10.5) |
The switch statement is a much better choice than a series of else-if 's that test different discrete or distinct values of the same variable. Conversely, a series of else-if statements is a much better choice than a switch when the execution of a leg of code is conditional on ranges of values of the same variable.
A switch statement must have a default case. The default case must be the last case in the case list.
In a switch construct, if falling through is necessary it must be clearly commented.
Every case , including the default , must end with either a break statement, a return statement, a continue statement (when in a loop), or with a comment stating that fall through is expected when the case falls into the next case . If the last instruction in the case is a non-returning function, a break statement must still following the function call.
5.6 break and continue Statements (1100.1200.10.5) |
Any break statement not at the end of a case construct within a switch statement and all continue statements must be clearly commented. The comment should indicate the change in program flow.
When in a loop, attempt to use the else part of the if construct instead of the break or continue statement. However, consider program readability and use additional variables, when necessary, to control program flow.
QUESTIONABLE CODING PRACTICE:
while ( i < max )
{
if ( special_case == TRUE )
{
|
GOOD CODING PRACTICE:
while ( i < max )
{
if ( special_case == TRUE )
{
|
5.7 The '? :' Construct (1100.1200.10.5) |
Except in macros, the ' ? : ' construct is not allowed for logic control. When used in in-line source code, the ' ? : ' construct be can used in either an assignment statement, a return statement, or when passing parameters to a called function. When used in a C macro, nesting of the construct is left to the discretion of the programmer, otherwise, they must not be nested.
BAD CODING PRACTICE: a_ptr = ( nitems == 0 ) ? malloc( |
GOOD CODING PRACTICE: if ( nitems == 0 ) { a_ptr = malloc( |
5.8 Unions (1100.1200.10.5) |
When using union s, it is the programmer's responsibility to keep track of which data type is currently stored in the union . Among other things this can be done by defining a variable to track the last data type stored in the union or by embedding the union within a structure and defining a member within the same structure which serves as a tag to track the last data type stored in the union .
The following is an example of the second alternative. The structure member "type" serves as a tag to track the last data type stored in the structure member "variant" which is defined to be a union :
/* Define the node types. */ typedef enum exNODETYPE { UNARY, BINARY, LEAF, } EXNODETYPE; /* Define a node. */ typedef struct exNODE { EXNODETYPE type; /* Variant tag */ union { struct { /* Pointer to left node */ EXNODE *left_ptr; } unary; struct { /* Pointer to left node */ EXNODE *left_ptr; /* Pointer to right node */ EXNODE *right_ptr; } binary; struct { short info; /* Leaf data */ } leaf; } variant; } EXNODE; |
5.9 Bit Manipulation (1100.1200.10.5) |
Bit fields offer the capability of defining and accessing adjacent bits within a word directly, rather than by bitwise and shift operators (see section 10.6.4). Although bit fields provide direct access, bit manipulation using bitwise and shift operators may be more conducive for looping. Consider the following example which packs a sequence of 4-bit BCD digits into a single 32-bit word:
/* Define bit field size of BCD digit. */ #define BFBCDDIG 4 value = 0; for ( i = 0; i < numdig; i++ ) { value |= (str[i] - '0') << ( (numdig - i - 1) * BFBCDDIG ); } |
Almost everything about bit fields is implementation dependent. For example, they are assigned left to right on some machines and right to left on others. Consequently, although bit fields are useful for maintaining internally-defined data, the question of which end comes first must be carefully considered when dealing with externally-defined data. Programs that depend on such details are not portable.
The underlying data type of a bit field should be either an appropriate enum type or an unsigned type. The size of a bit field should be a #define constant (for 5ESS only).
Consider the following example in which a process id is packed into an unsigned long data type. The process id is made up of three values: a process number, a processor id, and a uniqueness value.
BAD CODING PRACTICE: /* Define masks for components of process id. */ #define EXMSK_PROCNO 0xffff #define EXMSK_PCRID 0xff #define EXMSK_UNIQ 0xff /* Define bit offsets for components of process id. */ #define EXLSB_PROCNO 0 #define EXLSB_PCRID 16 #define EXLSB_UNIQ 24 /* BAD PRACTICE */ /* Extract components of process id. */ procno = ( procid >> EXLSB_PROCNO ) & EXMSK_PROCNO; pcrid = ( procid >> EXLSB_PCRID ) & EXMSK_PCRID; uniq = ( procid >> EXLSB_UNIQ ) & EXMSK_UNIQ; |
GOOD CODING PRACTICE: /* Define bit field sizes */ #define BFPROCNO 16 #define BFPCRID 8 #define BFUNIQ 8 typedef struct exPROCID { unsigned long procno : BFPROCNO; unsigned long pcrid : BFPCRID; unsigned long uniq : BFUNIQ; } EXPROCID; EXPROCID procid; GOOD CODING PRACTICE: /* Extract components of process id. */ procno = procid.procno; pcrid = procid.pcrid; uniq = procid.uniq; |
5.10 Data Types (1100.1200.10.5) |
Data types should be chosen to give an accurate description of the data being defined. Two logically similar items should use the same typedef . Two logically different items should use different typedef s, even if they have the same range of values.
enum s should be used to define values whose logical meanings are more important than the actual numbers used to represent them. For example, the logical value of a signal, whether it is ONHOOK, OFFHOOK or FLASH, is more important than the actual numbers which represent these values.
5.11 Source Featuring (1100.1200.10.5) |
Source featuring is a component of the Extended Change Management System (ECMS) that permits the definition and maintenance of different, concurrent versions of source files within the same ECMS generic release. It is used to isolate and maintain (in a given software release) functionality that is required by some software releases and not by others. For example, featuring can be used to isolate functionality between Software Release 5E4(2) and 5EE3.
Feature names are defined (by the ECMS administrator) in the ECMS relation GPF for each ECMS generic and project. It is the developer's responsibility to be aware of the valid features to use on which source files and to understand the impacts of using featuring. Refer to the ECMS User's Manual for a more detailed discussion of featuring, its use, and its impact on software development.
Implementation restrictions are listed here:
Feature lines, including any continuation of the expression, must be less than or equal to 512 characters.
The #feature and #endfeature constructs must begin in column 1.
The exclusion of source code may be done with either "not" or "!" in the expression. For consistency, use whichever method is currently in the source code.
Use existing #feature statements rather than introducing more featuring when the MR adding the original featuring constructs has not been added to your target load yet. Reusing featuring constructs added by mrs which are not yet approved can lead to dependencies. If you cannot accept the dependency, one may add additional featuring statements to avoid any MR dependencies.
The following is an example of featuring and the extracted code for both feature "A" and feature "B" versions.
Editable Version /* Common code. */ a b c #feature ( A ) /* Code for feature A only.*/ x x x #endfeature ( A ) /* More common code. */ d e f #feature ( B ) /* Code for feature B only.*/ g h i #endfeature ( B ) /* Yet more common code. */ j k l |
Extracted Versions Feature A Version Feature B Version /* Common code. */ /* Common code. */ a a b b c c /* Code for feature A only.*/ /* More common code. */ x d x e x f /* More common code. */ /* Code for Feature B only */ d g e h f i /* Yet more common code. */ /* Yet more common code. */ j j k k l l |
The following illustrates the correct and incorrect methods of adding code 'b' for the software release supporting feature C:
BAD CODING PRACTICE: /* Common code */ #feature (C) a #endfeature (C) #feature (C) b #endfeature (C) /* More common code */ |
GOOD CODING PRACTICE: /* Common code */ #feature (C) a b #endfeature (C) /* More common code */ |
6. Coding Techniques, Style, and Format (1100.1200.10.6) |
6.1 Function Definitions, Invocations, and Templates (1100.1200.10.6) |
A "function template" refers to a function declaration that also contains argument and return type information. Function templates are contained in template header files that (when included) enable the compiler to verify that actual function calls agree with the template in type and number of arguments. The purpose of this "type-checking" is to catch certain function interface faults between programs as early as possible.
Function templating saves the effort of manually finding and fixing function interface faults at some increased expense in load building costs. Also, function templating is a more automatic and reliable way of catching function interface faults than manual inspection or testing. A cost/benefit analysis of function templating indicates that the savings in finding and fixing faults meets or exceeds the increased expense in load building cost (for more details, see the cost/benefit analysis in 112000/v1100/10/1200.t).
Function templates must be implemented by all subsystems. All functions must have a function template. This coding standard applies not only to new code but also to existing code. The long-term goal is to be able to compile with the option that indicates that a function is called without a template being defined. (Adding USR_CFLAGS="-WARN,TEMPLATES" to the modbuild command line will cause the compiler to warn of missing templates for both defined and referenced functions.)
Exceptions are allowed only by consensus within a subsystem if the templating causes extraordinary resource demands. Exceptions by a subsystem must be fully documented and maintained under CMS control in a
This templating coding standard applies to all subsystems, promotes commonality between the U.S. and International Products, and allows flexibility. Subsystems must follow these standards as they add new code or as they modify existing code. Subsystems may grandfather existing templating schemes; otherwise, they must use the t-file organization scheme described below.
Templates must be of the form ...
[extern|static] return_type func( arg1_type, arg2_type, ..., argN_type); For example: extern EXRETVAL EXfunc( EXSHORT, EXLONG * ); |
declares that the function EXfunc returns a value of type EXRETVAL and takes exactly two parameters: an EXSHORT and a pointer to an EXLONG.
A function template or declaration should be preceded by a linkage specifier, either extern or static . A static linkage specifier can only be used in a template header file of module scope. Any function template or declaration without a linkage specifier is extern by default.
Functions must be defined with explicit return types. If a function does not return a value, the type void should be used. Defaulting to an implicit return type of int is not permitted.
A function template is always a function declaration but a function declaration is not necessarily a function template. e.g.
extern void EXfunc( void ); <-- template AND declaration extern void EXfunc(); <-- declaration only |
The template declares that function EXfunc does not return a value and that it expects no parameters. The void in the parameter list must be used in this instance, otherwise the compiler will see the template as a declaration only and will not warn if a parameter is passed.
The use of ellipses (...) to disable the checking for arguments should be used only in special cases when necessary.
Function templates should be organized into template header files that have three logical levels of scope as follows:
Global (uhdr/ss): Functions called outside of the subsystem.
Subsystem (ss/hdr): Functions called local to the subsystem.
Module (ss/module): Functions called local to the module.
Functions should be scoped at the lowest level possible to prevent unnecessary dependencies and rebuilds.
The scheme described below is the recommended way to organize function templates at all scopes, and is the default scheme for globally scoped templates. Where possible, subsystems should implement a common function templating scheme for all releases (e.g., a common implementation for both the U.S. and International Products).
If a subsystem has already created function template files using a different scheme, the subsystem can keep the existing naming convention and scheme and document the scheme in the
If a subsystem has not yet created function template files, the subsystem should follow the recommended scheme.
6.1.1 Recommended Template Organization Scheme (1100.1200.10.6) |
A common function templating organization scheme should be implemented for all releases (e.g., a common implementation within a subsystem for both the U.S. and International Products). To ease in reusing code between the U.S. and International Products, templating code should be added with feature construct names that are not CBU dependent.
A module may produce several products (low-level objects). It is recommended that a subsystem's template header files be organized so that its defined function templates are maintained on either a module or product basis, depending on the design of the individual subsystem. The product basis, described below, is the most general case, and must be used when a module builds products that use the same function name defined differently across these products. For many subsystems that do not have this situation, using the module basis to organize their templates is sufficient and will reduce the number of template header files that must be maintained. In the descriptions below, substitute module for product and mod for prod to get the module basis scheme.
Each low-level product must maintain template header files containing the templates for all functions defined in that product. Each product will have at most three template header files depending upon the scope of where the functions are called. The product's template header files should be organized and named as follows:
uhdr/ |
Using the naming convention above, if functions are built into more than a single product, then these functions must maintain more than one template, i.e. one per product.
The templates should be enclosed in #if
In the module declaration (.md) file, you must include the template header files for all the functions your product defines and references.
The order of header files will help in locating extra inappropriate function declarations. If a subsystem has already included template header files in their module declaration files, the subsystem can keep their existing ordering.
For the product EXprod in module EXmod, the header files in the uses list should be maintained in this order:
uses = |
The header files in the header list should be maintained in this order:
header = ... /* All non-template module header files */ tEXprod.h /* Module scope functions defined in */ /* EXprod, if any */ ... /* Other module scope template headers */ ... /* for functions called by EXprod (i.e. */ ... /* functions defined in another EXmod */ /* product), if any */ ; |
All data types referenced by function templates must be available at the proper scope. Global and subsystem scope template headers must contain the appropriate #include directives for any data types referenced in the templates. For module scope template headers, referenced data types must be made visible to the template via the header or uses list in the module declaration file. Any referenced data types declared in source files must be moved into a header file.
If a macro exists in a non-template header file for the purpose of hiding the actual function being called, the function template should precede the macro definition in the non-template header file. This is an exception to the above templating organization scheme.
For scoping purposes, FAT table entry references to a function can be ignored. Also, FAT table entries do not have to reference function templates; they may continue to use function declarations. This is because the use of OSfap() to invoke a function forfeits the capability to perform type-checking.
Function invocations must match function definitions and templates in number and type of parameters. In other words, the arguments passed to a function must agree in number and type with the arguments defined for that function. The exception to this rule is functions that have templates that allow for a variable number of parameters may use ellipses (...). For example, a function that requires at least one parameter, an EXSHORT, but may have more parameters would have a template of the form:
extern EXRETVAL EXfunc(EXSHORT, ...); |
When passing struct or union pointers as parameters, do not define the body of the struct or union in the function definition parameter list. Always use previously defined struct or union types so that a function template can be provided. For example,
BAD CODING PRACTICE: EXRETVAL EXmsgfunc(msg_ptr) struct { EXMSGHEAD msghead; EXMSGTEXT text; } *msg_ptr; { ... } |
GOOD CODING PRACTICE: typedef struct exMESSAGE { EXMSGHEAD msghead; EXMSGTEXT text; } EXMESSAGE; /* GOOD PRACTICE in source file */ EXRETVAL EXmsgfunc(msg_ptr) EXMESSAGE *msg_ptr; { ... } |
6.2 Data Definitions and Declarations (1100.1200.10.6) |
Function declarations and data declarations with global scope (across source files) must be provided and extern ed in global, local, or module header files provided by the referenced subsystems. Use a module header file for scope within a module, a local header file for scope within a subsystem, and a global header file for scope crossing subsystem boundaries.
When using unions for OSDS messages, ensure the union contains only those messages which are expected (or a large majority of them). If the union contains many other unnecessary messages, consider creating another union containing only the required messages. Avoid declaring message unions within a function. This can lead to numerous function changes when adding new expected messages.
Choose integral data types according to the following criteria:
If the quantity must be of a fixed size (in an inter-processor message, for example), use a type that resolves to a char , short or a long , as required.
Otherwise, if the quantity may exceed 16 bits, use a type that resolves to a long .
Otherwise, if the quantity has storage class auto and space efficiency is more important than time efficiency, use a type that resolves to a char or a short .
Otherwise, use a type that resolves to an int .
Initialization of non-constant non-static local variables must be done in the code segment rather than at the point of definition.
It is recommended that only one variable or function be declared per line. This enhances readability, provides the opportunity for better commenting, and makes maintenance easier if some variables or functions change type. This is a recommendation rather than a rule because it may occasionally make sense to group closely related variables that act as part of larger conceptual entity. For example:
EXSHORT currow, curcol; /* Current screen position */ |
When declarations are not one-per-line, the following rules apply.
Don't mix variables and functions within the same declaration.
Don't mix initialized and uninitialized variables within the same declaration.
6.3 Type Conversion (1100.1200.10.6) |
The following integral promotions are performed:
Operands of type char or short are converted to type int . Promotion of char to int may involve zero-padding or sign extension, depending on the compiler. Promotion of short to int is always done with sign extension.
Operands of type unsigned char may be converted to type int or unsigned int , depending on the compiler.
Operands of type unsigned short are converted to type unsigned int .
Bit fields are promoted to either signed or unsigned quantities, depending on the compiler and the size of the bit field.
Given two operands with promoted types as shown in the left column and top row below, the operands undergo arithmetic conversion to the type shown in the table, and the result of the operation will have the same type (see section 6 of reference[p]):
With the exception of the values NULL and 0, use type casts for all conversions not listed above or that are listed as being compiler dependent. The values NULL and 0 need not be type cast unless they are being passed as function call parameters.
Table 6.-2. Arithmetic Conversions
|
where uint stands for unsigned int and ulong stands for unsigned long . The [u]long entries may be either long or unsigned long , depending on the compiler.
Integral types undergo arithmetic or storage conversion as follows:
If the source and destination are the same size, the bit pattern is unchanged.
A source is converted to a smaller destination by truncating the high-order bits.
An unsigned source is converted to a larger destination by zero-padding.
A signed source is converted to a larger signed destination by sign extension.
A signed source is converted to a larger unsigned destination by zero-padding or sign extension, depending on the compiler.
6.4 Variables and Constants (1100.1200.10.6) |
Variables may not be used for more than one purpose within their scopes, except when used only as loop counters ,array indices or return values.
Register variables can be freely used to obtain real-time efficiency. However, they cause a minor problem in debugging 3B20 programs using DART because only the register variables in the currently active function can be displayed. The restriction does not apply to debugging MC68000 programs using ITS because the MC68000 stack design is different from that of the 3B20.
Consideration should be given to the fact that register variables less than four bytes will increase stack space. This is because registers used for variables are saved on the stack at function entry. For example, if a two-byte variable is registerized, two bytes will be wasted on the stack. For MC68000, stack overflow must be considered when registerizing variables. See reference [q].
The called function is responsible for the saving and restoring of all registers. So the stack space used for registers does not depend on whether a function calls another function. It will always be used.
Some compilers will automatically registerize variables based on usage.
Definitions of automatic variables should be ordered by size and type to take advantage of addressing alignment characteristics. Exceptions may be necessary for ASSERTs, which dump only the first 180 bytes of the stack frame.
Constants that have a specific meaning (e.g., number of line units, maximum buffer size, number of array elements, etc.) must be given a descriptive #define name. The exceptions to this rule are when the number 0 is used to reference the first item in an array, and the use of 0 or 1 as the initial or final value of a counter.
The method of initialization of constants depends on the type of constant to be initialized. The following types of constants are distinguished:
#define constants: true constants which are defined using #define .
Local constants: local variables whose values remain constant during the execution of their enclosing functions.
Global constants: global variables whose values remain constant for a particular load or installation.
Local and global constants must be initialized at their definitions.
All variables must be initialized before use.
6.5 Stack Usage (1100.1200.10.6) |
Various techniques can be used to minimize stack usage in order to prevent stack overflow problems. Here are few examples which can help minimize stack usage.
Declare all local variables from largest to smallest in size. Therefore, all structures should be declared first, followed by longs, pointers, integers, shorts, and then characters or booleans, in that order. This will compact the local declarations as much as possible on the stack.
Instead of declaring large structures in very high level functions (functions at the top of the stack), declare structures in .bss data if possible. When this is done, care must be used to ensure that the data will not be corrupted if a real time break is taken. The .bss data does not occupy any stack space and can be referenced much like global parameters.
Use unions instead of declaring many different structures on the stack. This is especially useful for relation structures being placed on the stack which are only used once in order to obtain data to read other relations later. For example:
union { struct rlBIGREL1 rel1; struct rlBIGREL2 rel2; } all_rels; /* Some code here. Initialize key to relation. */ ret = DBfrdtup( RLBIGREL1, (char *) &(all_rels.rel1) ); if ( ret != GLSUCCESS ) { /* Error handling. */ return; } /* Some more code possibly. */ all_rels.rel2.key = all_rels.rel1.store; ret = DBfrdtup( RLBIGREL2, (char *) &(all_rels.rel2) ); if ( ret != GLSUCCESS ) { /* Error handling. */ return; } |
In the above example, it may be necessary to temporarily store the values retrieved from the first read into local variables so that they are not overwritten when the information is copied into the keys of the second relation since they use the same memory space. If that is necessary, the amount of stack space required to store the temporary variables which are needed in the subsequent reads is likely less than the amount of space needed to declare the entire relation on the stack.
Another alternative, similar to the idea above, is to add extra function calls. In general, a function call will add to the stack space. However, smart use of additional functions can actually decrease stack space. A function which has many declarations on its local stack can be modified to call two or more functions which accomplish the same functionality as the large function, but each only containing a subset of the local declarations. This has the benefit of moving many of the local declarations to lower level functions which will be popped off the stack when they are done. Therefore, having one large function with many local declarations can use more stack space than a group of functions called by a higher level function which use a small portion of the large function's declarations.
Declare variables as locally as possible. Instead of declaring all the local variables at the beginning of the function, declare some within case statements and if statements. The compiler optimizes this stack usage, treating these similar to unions. Below are two examples of such stack usage:
Example 1: switch ( value ) { case VALUE1: { struct rlBIGREL1 rel1; /* Use BIGREL1 */ } break; case VALUE2: { struct rlBIGREL2 rel2; /* Use BIGREL2 */ } break; default: { struct rlBIGREL3 rel3; /* Use BIGREL3 */ } break; } |
Example 2: if ( value == 0 ) { struct rlBIGREL1 rel1; /* Use BIGREL1 */ } else { struct rlBIGREL2 rel2; /* Use BIGREL2 */ } |
6.6 Operand Grouping (1100.1200.10.6) |
The following table lists C operator associativity and precedence in decreasing order by row:
Table 6.-3. Operator Precedence, Associativity, and Type
|
The relative precedence of the primary, unary, multiplicative, and additive operators is fairly well known, as is the relative precedence of the relational and equality operators to the logical operators. Knowledge of other precedences varies quite a bit from person to person and should therefore be made clear through the use of parentheses. Use of parentheses to indicate the intended operator associativity of terms involving multiple '-' operators and factors involving multiple '/' or '%' operators is recommended. Use conservative judgement when eliminating redundant parentheses to increase the readability of extremely complex expressions. For example:
BAD CODING PRACTICE: (((a + b) + c) + d) /* intention is clear; ()s just add clutter */ a + b / c >> EXCONST /* precedence is not always remembered */ a > b == c > d /* ditto */ /* * Elimination of white space to indicate precedence may be * misleading. The following appears to suggest * precedence of "/" over "*". White space must not be used * to suggest precedence. */ a * b/c - d /* This is equivalent to (x & (EXXMSK == 0)) && (y & (EXYMSK != 0)) */ x & EXXMSK == 0 && y & EXYMSK != 0 /* This is equivalent to (attrib & EXTALL) | EXWIDE */ attrib & EXTALL|EXWIDE |
GOOD CODING PRACTICE: a + b + c + d /* intention is clear without ()s */ /* * "/" has higher precedence than "+" by mathematical standard; * ()s show precedence of "+" and "/" over ">>" */ (a + b / c) >> EXCONST (a > b) == (c > d) /* ()s clarify precedence */ /* * ()s show left to right associativity of equal-precedence "*" and "/"; * "*" and "/" have higher precedence than "-" by mathematical standard */ (a * b) / c - d /* * Both of the following styles are in common use. The first leaves no * questions about precedence; the second slightly reduces code clutter. */ ((x & EXXMSK) == 0) && ((y & EXYMSK) == 0) (x & EXXMSK) == 0 && (y & EXYMSK) == 0 |
More information about operator precedence can be found in section 2.12 of reference [a].
6.7 #includes (1100.1200.10.6) |
All subsystem-local and global header files must #include any header files needed to define symbols used in the header file [1] .
In the modularity environment, no module-local header files (i.e. header files which exist within a source module and end with .h or .lh) or source files may contain #include statements. Any header files that these files require should be included in the module declaration file.
1. | In the pre-modularity environment, header files began with a #define with a unique name. Inclusion of each header file in other files was conditional on the unique name to prevent multiple inclusion. This technique should no longer be practiced as current header file processing makes this unnecessary. |
6.8 Preprocessor Conditionals (1100.1200.10.6) |
Conditional preprocessing (using #if , #ifdef , #ifndef , #elif , #else , and #endif ) should be used with great care since they clutter the natural flow of the code. The following are restrictions/suggestions on their use:
Conditionals should only be used for minor differences based on the definition of a flag. Major differences should be handled by providing separate functions.
Conditional changes within a function should be localized as much as possible and not scattered throughout the function. That is, any decisions that depend upon the conditional expression should be made in one place if possible.
A conditional must not split or interrupt the flow of a complete C statement. For example:
BAD CODING PRACTICE: if (( |
GOOD CODING PRACTICE: #ifdef BAR if ( |
This approach results in increased readability and maintainability of code. Do not sacrifice clarity of code to save typing or tiny amounts of memory.
6.9 Macros (1100.1200.10.6) |
Macro definition syntax rules:
If a macro definition contains several C statements, these must be put on separate, back-slash terminated lines enclosed by a do-while(0) . For example:
BAD CODING PRACTICE: #define EXMACRO() |
The do while(0) causes the macro to expand to a single, simple statement terminated by exactly one semicolon (provided by the invoker). This format prevents problems with if-less else (caused by two or more semicolons when a macro is expanded) and else-less if (see the first example in this section) in case the macro is invoked as a simple-statement body for an if-else statement.
The /*CONSTANTCONDITION*/ prevents lint errors due to the fact that the condition within the while loop is a constant expression, always resulting in a false indication. This string must not be omitted. If not used, a lint error will be generated, one for each invocation of the macro.
Arguments to a macro that can be C expressions must be parenthesized throughout the macro definition. In addition, any macro whose result is a C expression must be parenthesized in its entirety if it contains an operator and is not of the form sizeof ( expr ). For example:
BAD CODING PRACTICE: #define EXCONST EXOTHER_CONST + 1 #define EXMAX(x, y) x > y ? x : y GOOD CODING PRACTICE: #define EXCONST (EXOTHER_CONST + 1) #define EXMAX(x, y) ((x) > (y) ? (x) : (y)) |
The parentheses are necessary to ensure that the intended operator precedence holds regardless of how the macro is used, and regardless of the nature of expressions passed as arguments.
Macro readability and maintainability rules:
Macro nesting and size should result in maintainable software. Very large macros and macros that call other macros through many levels of nesting can cause maintainability problems and excessive memory usage. Excessively nested macros can make code less readable and more difficult to maintain and debug. Minimize the levels of nesting and macro size to avoid these problems. If a macro is large, consider making it into a function.
Macros should only be used to make code easier to read and/or more flexible (when the extra flexibility is needed).
In this example, either of these two definitions of a macro to increment a variable using modulo arithmetic might be used, depending on efficiency considerations:
/* increment x modulo M assuming 0 <= x < M */ #define EXINCMOD( x, M ) ((x) = ((x) + 1) % (M)) or /* increment x modulo M assuming 0 <= x < M */ #define EXINCMOD( x, M ) / do / { / if (++(x) == (M)) / (x) = 0; / } / /* CONSTANTCONDITION */ / while (0) |
Both of these definitions perform the desired operation, but the implementation details and the fact that one is an expression and the other is not is hidden.
This next example shows how a macro might be used to hide a minor, though obscuring, detail, such as embedded structure names:
x_ptr->name1.name2.name3.name4.name5.key = 0; can be more clearly written: #define EXITEM_KEY name1.name2.name3.name4.name5.key x_ptr->EXITEM_KEY = 0; |
Macros should definitely not hide important details, significant operations or side effects (especially changes in flow control).
Consider using self-descriptive names for parameters in function-like macros to make complex macros or macros with many parameters easier to understand.
6.10 Comments (1100.1200.10.6) |
Comments should be added to source code to aid in the understanding of the program or to provide a reader with any special information needed to understand the code. Misleading comments are worse than no comments. Use the following rules when creating comments.
There are three acceptable comment styles: trailing comments, single-line comments, and block comments.
Trailing comments are short comments that appear at the end of a line of code and describe that single line of code. The text of the comment should be separated from the '/*' and '*/' delimiters by white space. Trailing comments within a block of code should be tabbed to line up with each other. For example:
int argc; /* Argument count */ char *argv[]; /* Argument list */ |
If a comment is too long to fit on the line it describes, use the single-line (or block) comment format.
Single-line comments should be used for comments that describe a single line but are too long to fit as a trailing comment or for comments that describe multiple lines but will fit on one.
A single-line comment precedes the code it describes, and should be indented to match the code. Like trailing comments, the text of single-line comments should be separated from the '/*' and '*/' delimiters by white space. For example:
/* Sort temp uniquely on the second and third fields. */ execl(EXSORT, EXSORT, "-u", "+1", "-3", temp, "-o", temp, (char *) NULL); |
See the nlint (1) manual page for a list of special single-line comments used to modify the behavior of nlint.
Block comments should be used for any comment that will not fit on one line. Do not stack single-line comments. Visually, a block comment looks like this:
/*
* This is a block comment. Note that the
* delimiters are on lines by themselves.
*/
|
All '*' in the block comment have the same indentation. The opening "/*" may be followed by comment text or followed by white space. The closing "*/" appears on a line by itself. The comment appears before the code it describes, and the opening delimiter '/*' should be indented to the level of the following code. Each non-delimiting line must begin with a space, an asterisk, and another space, as shown.
The following uses of comments are not permitted:
Do not include information that is not to be released to customers or other persons who may have been granted access to source code.
Comments may not include anything that reflects poorly on Lucent Technologies/
Comments may not contain slang expressions, graffiti-like phrases, non-standard acronyms, or terminology that is unclear to outside readers.
Personal opinion or commentary is not permitted.
Do not repeat what is self-evident from the semantics. For example:
BAD CODING PRACTICE: ++i; /* Increment i */ |
Comments are almost always called for in the following situations, but do not put in comments that do not add value.
File prologues at the beginning of each file, and function prologues before each function definition (see section 100.10.2).
A statement of purpose before the do-while , for , if , else , while , and switch control constructs. For example, the purpose of a for loop might be to examine all the elements of an array and the purpose of a switch might be determine the effect of a particular character on the column number when output.
Control constructs which result in discontinuous flow control ( break , continue , and goto ), except break at the end of a switch case or default, should be commented with a statement of explanation and destination action. For example:
/* Parse lines of input into tokens until end-of-file. */ while (EXparse_line(in_file) != EOF) { ... /* Do a quick comment check on the first token. */ if (EXpeek_token() == EXCOMMENT) { /* * There is no need to parse this line further; * resume parsing at the next line. */ continue; } ... } /* End of per-line parsing */ |
Falling through from one case to another in a switch statement must be commented.
In a long control construct, the closing brace '}' must be commented to associate it with the opening statement. Since window sizes vary, the threshold for what constitutes a long construct is about 20 to 30 lines.
Calls to non-returning functions--ones that do not return control to the calling function--must be clearly commented to state this fact.
6.11 White Space (1100.1200.10.6) |
Opening braces '{' and closing braces '}' must be surrounded by white space, except that a closing brace may be immediately followed by a semicolon ';'.
All commas, semicolons, question-marks, and colons must be followed by white space.
No white space should exist between the names of functions or function-like macros and the left parenthesis, or between array names and the left square bracket which follows the name. For example:
BAD CODING PRACTICE: EXMACRO (abc, def); EXfunc (); array [sub] = abc; GOOD CODING PRACTICE: EXMACRO(abc, def); EXfunc(); array[sub] = abc; |
Space after opening parentheses '(' or square brackets '[' and before closing parentheses ')' or square brackets ']' is optional.
All C keywords must be surrounded by white space, except that semicolons immediately follow keywords and left parentheses immediately follow asm and sizeof .
Table 4. C Keywords
|
The (primary) operators '->' and '.' must not be surrounded by white space. Unary operators must not have white space between them and the operands upon which they operate. Binary operators (arithmetic, relational, bitwise, logical, and assignment) must be surrounded by white space. One exception is multiplicative operators ('*', '/', and '%'), whose surrounding spaces may be eliminated in operations on two factors to visually emphasize precedence over the additive operators ('+' and binary '-'). For example:
BAD CODING PRACTICE: a * b/c + d /* suggests (a * (b / c)) + d) */ a*b/c + d /* defeats purpose of white space */ x=a|b|c x_ptr = & y if((x_ptr -> el&EXMASK)!=0) QUESTIONABLE CODING PRACTICE: a + b*c - x/y GOOD CODING PRACTICE: (a * b) / c + d x = a | b | c x_ptr = &y if ((x_ptr->el & EXMASK) != 0) |
See section 100.10.6.4 for a list of operators, precedences and associativity.
6.12 Code Layout (1100.1200.10.6) |
Lines longer than 80 columns should be avoided to increase code readability on screens and listings. Longer lines should be broken up and the continuation portions indented further. Acceptable breaking points are at major operators or commas. For example, these statements
if (( |
Indentation of continuation lines by small amounts distinguishes them from the bodies of control structures. Indentation to line up arguments or parts of expressions looks neat.
The bodies of if , else , for , while , do-while , and switch constructs must be compound statements (enclosed in braces) and indented a full (8 character) tab-stop. Enclosing all bodies in braces prevents problems with later code changes and non-standard-conforming macros which may expand to multiple statements or contain ' else -less if s'.
The following forms are permitted for if and if-else statements (a simple if contains only the if portion; generally, there may be zero or more else if portions and zero or one else portions):
if ( |
The first form is preferred for the greatest ease in readability and matching braces, and is the only form consistent with the placement of the opening brace in function definitions. Opening and closing braces are always required around the body of conditional expressions, even when the body in the condition is a single statement (as in the examples above).
The null statement forms of the for and while loops are emphasized by placing the comment on the line following the left curly brace as follows:
for ( |
The general forms of the for and while loops should be arranged in the same way as a simple if .
The do-while statement may have either of these forms:
do do { { } while ( |
Note that the terminating semicolon of the do-while does not represent a null statement and is therefore on the same line as the while , in contrast to the null statement form of the while loop.
The formats for switch statements are:
switch ( |
where
Case and default actions should not be individually enclosed in braces unless necessary to introduce restricted-scope variables.
When existing code does not conform to the standards, new code should be added using the existing format. This consistency will enhance code readability.
6.13 File and Function Layout (1100.1200.10.6) |
The general layout of a source file should look like the following.
|
The general layout of a function should be as follows:
|
If the function is static (should not have scope beyond this file and those that it #include s), the
|
6.14 Module Declaration File Layout (1100.1200.10.6) |
The module declaration file contains administrative information which defines the actions for the software generation systems (SGS) tools for that module. The name of the module declaration file consists of the name of the module followed by the suffix ".md". A description of each section of the .md file is as follows:
6.14.1 Identification Section (1100.1200.10.6) |
The name, subsystem and a brief description of the module are placed in this section.
6.14.2 Module Initialization Section (1100.1200.10.6) |
This section specifies the type of .md file which is dependent on the product created. Source level module declaration files with " type=sgs_src " define the attributes needed to compile and assemble source files to create low-level object files. High level module declaration files with " type=sgs_obj " define the attributes needed to link low-level products to create high level object files. Sub-system level module declaration files with " type=collection " define the attributes needed to identify the products created by the subsystem and used to create the pag-level products.
This section also indicates preprocessor macro definitions, field update type and symbols, and identifies the selector(s) for the module. A partial list of selector attributes are
EES - the execution environment
PSM - switching module
PAM - administrative module
OOKP - code specific to the OKP process
6.14.3 Product Declaration Section (1100.1200.10.6) |
This section lists the products that can be generated for this module.
6.14.4 Product Definition Section (1100.1200.10.6) |
This section spells out the resources needed to generate the declared products. For modules that generate more than one product the attributes associated with each product is done by using the #ifprod statements followed by the product name. All source and headers files used for specific products need to be #if 'd for that product. Processing files not needed for the product results in wasting resources. This section is composed of:
Package Identifier which associates a product with a specific loadable package.
Component Identifier lists all modules that this module is dependent upon.
Header Section lists all the local header files in the module.
Interface Section which is the "uses" section consisting of the lists of global and local interfaces that are needed to generate the product.
Source File Section which contains the list of all source files in the module.
Following is an example outline for a low-level module declaration file:
/* MODULE = RTxmfc */ -----------| /* Export Multifrequency Compelled Signal */ |---Identification--- /* SS = rta */ ___________| Section type = sgs_src ; -----------| fu_type = nonkillable ; | #if (PAM) | procname = okp ; | #endif | Module PRDEFS = "-DFLCTOLL" |---Initialization--- ; /* End pre-processor list */ | Section | selector = PAM | PSM | ; /* End selector list */ -----------| product = -----------| RTam_mfc /* MFC AM product */ | Product RTsm_mfc /* MFC SM product */ |---Declaration ; /* End Product list */ -----------| Section #if (PAM) -----------| Product #ifprod (RTam_mfc) |---Definition--- package = LP_EXP | Section MFC_BASE | ; /* End package list */ | | header = |---Header RTammondata.h | Section RTmonstat.h | /* module scope */ | tRTam_mfc.h /* templates for */ | /* defined funcs */ | ; /* End header list */ | | uses = |---Uses--- /* global and ss */ | Section rta/tRTam_mfc.h /* scope templates */| tRTam_mfc.h /* for defined */ | /* functions */ | | hm/tHMlib.c3o.h /* global and ss */ | si/tDDlib.h /* scope templates */| tRTx_term.h /* for referenced */ | /* functions */ | #ifpack (LP_EXP) | db/DMgport.h | db/DMport_state.h | db/RLportla.h | #endif | pag/GLtpamfc.h | rta/RTmfcmonds.h | rta/RTprimitives.h | rta/RTretrns.h | ; /* End uses list */ | | #ifpack (LP_EXP) |---Source--- source = | Section RTmfc_rsig.c | RTmfc_ssig.c | RTmfc_tsig.c | RTmfc_vsig.c | ; /* End source list for LP_EXP */ | #endif | | #ifpack (MFC_BASE) | source = | RTmfc_out.c | RTmfc_rec.c | RTmfc_send.c | RTmfc_err.c | ; /* End source list for MFC_BASE */ | #endif | #endif | #endif ----------| |
7. Some Programming Tips (1100.1200.10.7) |
Be consistent! For example, if you choose a style of indentation, and establish conventions for using white space and naming variables, make sure you use the same style and conventions throughout your software.
Write clearly don't be too clever and don't sacrifice clarity for minor improvements in efficiency.
Say what you mean, simply and directly.
Use library or standard functions where possible.
Use fundamental control constructs such as while , do-while , for and if-else and avoid using the goto statement.
Use switch when feasible instead of if-else.
Don't comment or work around (kludge) bad coderewrite it.
Test input for validity and plausibility; make sure it cannot violate the limits of your program. If your program identifies bad input, recover if possible.
Make sure all variables are initialized before use.
Avoid multiple exits from loops.
Make it right and clear before you make it faster. Also, keep it right when you make it faster.
Keep it simple to make it faster.
Let the compiler do the simple optimizations.
Before "tweaking" code to make it faster, try to find a better algorithm.
Make sure comments and code agree.
Format a program to help the reader understand it.
Comments should not repeat what the code says; they should explain the code.
Don't over-comment.
Parenthesize to avoid operator-precedence ambiguity in expressions involving mixed operators, but do not overdo it.
Use symbolic constants instead of hard-coded constant values. (The constants 0 and 1 may appear as counter values.)
Avoid side effects and expressions in which the order of evaluation is undefined.
Watch out for off-by-one errors, especially in array subscripts and loop indices.
More about programming tips can be found in reference [b], from which most of these were taken.
8. References (1100.1200.10.8) |
8.1 About The References (1100.1200.10.8) |
There are many references on good coding style, format and techniques. An excellent reference in this area is [b]. This book presents examples of both good and bad programming styles and techniques and explains very clearly what makes them so. Even though the examples are all in FORTRAN and PL/I, and some of the rules do not apply to C or 5ESS Switch development, it still presents invaluable ideas in programming.
The C Programming Language book (reference [a]) is not a good reference for 5ESS Switch coding style. Many of the programming examples in this guide use obscure combinations of language features and are poorly formatted for clarity and readability. Nevertheless, this book is an important source of information on C's interpretation of various constructs and it points out some of the pitfalls.
Any C programmer would greatly benefit from reading reference [a] and reference [b].
8.2 References (1100.1200.10.8) |
B. W. Kernighan and D. M. Ritchie, The C Programming Language , Second Edition, Prentice Hall, 1988
B. W. Kernighan and P. J. Plauger, The Elements of Programming Style , Second Edition, McGraw Hill, 1978
T. A. Burrows and D. D. Hill, C Coding Style for Conceptual Portability , 55641-810921.01TM, Case 40548-400 September 21, 1981.
J. H. Miller, A Proposed Documentation Naming Scheme for No. 5 ESS Software , 5326-780725.01MF, Case 40288-700, July 25, 1978.
N. A. Martellotto, Department 5521 Routine Header Standards , October 6, 1977.
D. F. Christian, No. 5 ESS Coding Style , Programmer's Notes, July 26, 1982.
C. G. Denenberg, T. G. Peterson and T. L. Shockey, Programming Techniques Recommendations , Case 40125, February 19, 1976.
C. M. Waters, Dynamic Data Design Guidelines , Case 40288-200, May 19, 1982.
J. M. Milner, Impact of Greater Use of Assembly Language Programming by No. 5 ESS , 5632-801216.01MF, Case 40288-700, December 16, 1980.
P. W. Rung, Use of External and Static Variables for No. 5 ESS IM Software Development , 5613-800625.01MF, Case 49460-210, June 25, 1980.
C. J. Hughes, Proposed Standards for Prologues for No. 5 ESS Software , 5613-800718.01MF, Case 49460-325, July 18, 1980.
D. R. Kertz, No. 5 ESS - Lint Libraries for Interface Checking , Case 40288-100, August 25, 1980.
D. M. De Ruyck, "C" Language Unions Cause Machine Dependent Code , 55622-820503.01MF, Case 40288-700, May 3, 1982.
J. R. Gibbons, No. 5 ESS Incremental Feature Addition (Feature Optioning) - System Level Requirements and Development Proposal , 55623-820412.01MF, Case 40288-100, April 12, 1982.
M. D. Bratcher, Coding Techniques, Style and Format , 55625.821108.02MF, November 8, 1982.
R. A. Horzempa, CMS Featuring Support , 45182-811001.01MF, Case 40125.
D. M. Ritchie, The C Programming Language - Reference Manual , (Section D.1.1 of Documents for Unix ), Bell Telephone Laboratories, Inc., 1981.
Mary Pasternak, Efficient C Coding Style for mc68 - Issue 2 , Work Project 1211-50-7062, Case 40297-2, March 21,1988.
9. Glossary (1100.1200.10.9) |
ASSERT | Asserts are a mechanism used by the 5ESS® Switch software to identify potential problems and report them to operating personnel. |
---|---|
Braces | An open brace looks like `{', and a close brace looks like `}'. See also brackets. |
Brackets | An open bracket looks like `[', and a close bracket looks like `]'. These are sometimes referred to as square brackets to differentiate them from angle brackets (an open angle bracket is the less-than symbol `<', and a close angle bracket is the greater-than symbol `>'). |
Category Code | Two-letter identifier given to cohesive sections of 5ESS Switch code, usually, but not limited to, subsystems |
CMS | Change Management System |
DART | Debug A Running Target. This is a tool that allows software running on the 3B20D to be debugged from a support processor. |
ECMS | Extended Change Management System |
IMR | Initial Modification Request |
ITS | Interactive Test System. This is a tool used to debug software running on the processors in a switch other than the AM (3B20D). |
M4 | macro processor |
Nlint | Lint is a program to check C code for bugs, nonportable constructs, and waste. See the nlint (1) manual page for more information. |
ODD | Office Dependent Data |
Operator Associativity | Operator associativity determines the order in which adjacent operators of equal precedence are evaluated. For example, the subtraction operator has left-to-right associativity, so `5 - 2 - 1' is equivalent to `(5 - 2) - 1', for a result of 2, not `5 - (2 - 1)', which would give a result of 4. In contrast, the conditional operator has right-to-left associativity, so `1? 1: 1? 2: 2' is evaluated as `1? 1: (1? 2: 2)', giving the result 1, rather than `(1? 1: 1)? 2: 2', which gives the result 2. |
Operator Precedence | Operator precedence determines the order in which expressions involving different operators are evaluated. Operators with higher precedence are evaluated before operators of lower precedence. For example, the multiplication operator `*' has higher precedence than the subtraction operator `-', so the expression `7 - 3 * 2' is computed by first multiplying 3 by 2 and then subtracting the result from 7. |
RTR | Real Time Reliable |
Sign Extension | Sign extension is a process for converting a small signed integer to a larger integer. The high-order bit of the smaller integer is replicated in the additional bits of the larger integer. |
White Space | A character inserted by hitting space bar, tab character or a control character. |
Zero-Padding | In the context of integer conversion, zero-padding means that the high-order bits of an integer are set to zero when converting from a smaller integer. |
10. Index (1100.1200.10.10) |
#define ... 1200.10.4,1-2; 1200.10.5,6-9; 1200.10.6,4,6,10-12,14,17-18 #elif ... 1200.10.6,10 #else ... 1200.10.5,1,4; 1200.10.6,8,10-11,13-14,16 #endfeature ... 1200.10.5,9-11 #if ... 1200.10.3,2; 1200.10.4,1-5; 1200.10.5,4,9-10; 1200.10.6,1-8,10-11, 13-16,18,21; 1200.10.7,1 #ifdef ... 1200.10.4,5; 1200.10.6,10-11 #ifndef ... 1200.10.6,10 #undef ... 1200.10.4,2 DART ... 1200.10.9,1 ITS ... 1200.10.9,1 M4 ... 1200.10.4,1-2; 1200.10.9,1 RTR ... 1200.10.1,1; 1200.10.2,1; 1200.10.3,1; 1200.10.4,2; 1200.10.9,1 array indices ... 1200.10.7,1 array names ... 1200.10.6,6 asm ... 1200.10.6,14 auto ... 1200.10.6,5,14 bit field ... 1200.10.5,7-9; 1200.10.6,5 block comments ... 1200.10.6,13 braces ... 1200.10.6,14,16-17 ; 1200.10.9,1 break ... 1200.10.4,4; 1200.10.5,3-4; 1200.10.6,7-8,14-15,17 category code ... 1200.10.4,1-3; 1200.10.9,1 commas ... 1200.10.6,8,15 comment ... 1200.10.1,1; 1200.10.5,4; 1200.10.6,1-2,5,12-14,16-17; 1200.10.7,1 comment styles ... 1200.10.6,12 compiler ... 1200.10.4,1; 1200.10.5,2; 1200.10.6,1-2,5-7,18 compiler dependent. ... 1200.10.5,2; 1200.10.6,5,18 consistency ... 1200.10.5,9 constants ... 1200.10.1,1; 1200.10.4,2; 1200.10.5,8; 1200.10.6,6,11; 1200.10.7,1 continuation lines ... 1200.10.5,9; 1200.10.6,15 continue ... 1200.10.5,4; 1200.10.6,4,14,17 control ... 1200.10.3,2; 1200.10.5,1,4-5; 1200.10.6,1,12,14; 1200.10.9,2 customers ... 1200.10.1,1; 1200.10.2,1 data declarations ... 1200.10.6,3-4 debugging ... 1200.10.4,4-5; 1200.10.5,1; 1200.10.6,6; 1200.10.9,1 default ... 1200.10.4,2; 1200.10.5,4; 1200.10.6,2,8,14,17 definitions ... 1200.10.2,1; 1200.10.5,9; 1200.10.6,1,4-6,10-13,16-18 do-while ... 1200.10.5,4; 1200.10.6,11,13,16-17; 1200.10.7,1 efficiency ... 1200.10.6,5 else ... 1200.10.5,1,4; 1200.10.6,8,10-11,13-14,16 else-if ... 1200.10.5,4 embedded assignment ... 1200.10.5,1 enum ... 1200.10.4,2-3; 1200.10.5,6,8-9; 1200.10.6,14 enum member ... 1200.10.4,2-3 falling through ... 1200.10.5,4; 1200.10.6,14 file ... 1200.10.3,0-2; 1200.10.4,0-2; 1200.10.5,0,9; 1200.10.6,0-4,10,13-14,17-20 file prologue ... 1200.10.2,1; 1200.10.3,1; 1200.10.6,13,17 flexibility ... 1200.10.6,1 flow control ... 1200.10.5,4; 1200.10.6,14 for ... 1200.10.1,1; 1200.10.2,1; 1200.10.3,1-2; 1200.10.4,1-5; 1200.10.5,1,3-5,7-11; 1200.10.6,1-7,9-18,20-21; 1200.10.7,1; 1200.10.8,1-2; 1200.10.9,1-2 function ... 1200.10.2,1; 1200.10.3,1-2; 1200.10.4,1-5; 1200.10.5,4-5; 1200.10.6,1-7,10,13-14,16-18,21; 1200.10.7,1 function call parameters ... 1200.10.5,5 function names ... 1200.10.2,1; 1200.10.3,1-2; 1200.10.4,1-2; 1200.10.6,2 function prologue ... 1200.10.2,1; 1200.10.3,1; 1200.10.6,13,17 global parameters ... 1200.10.4,2; 1200.10.6,7 global scope ... 1200.10.6,3-4,21 global variables ... 1200.10.4,2 goto ... 1200.10.4,4-5; 1200.10.5,3-4; 1200.10.6,14; 1200.10.7,1 header file ... 1200.10.3,1-2 ; 1200.10.4,2 ; 1200.10.6,1-4,10,17,19 identifier ... 1200.10.4,1-2; 1200.10.6,19; 1200.10.9,1 if ... 1200.10.3,2; 1200.10.4,1-5; 1200.10.5,4,9-10; 1200.10.6,1-8,10-11,13-16,18,21; 1200.10.7,1 if-else ... 1200.10.6,11,16; 1200.10.7,1 indentation ... 1200.10.6,16 initialization ... 1200.10.6,18 int ... 1200.10.6,2,5-6,13-14 keywords ... 1200.10.4,1 ; 1200.10.6,14 language ... 1200.10.1,1; 1200.10.2,1; 1200.10.4,1; 1200.10.8,1-2 lint ... 1200.10.6,11; 1200.10.8,1; 1200.10.9,1 local constants ... 1200.10.6,6 local variables ... 1200.10.6,6-7,17 logic ... 1200.10.5,1 logic control. ... 1200.10.5,1 loop ... 1200.10.4,2,4; 1200.10.5,3-5,7; 1200.10.6,11,14,16-17; 1200.10.7,1 loop variable ... 1200.10.4,2; 1200.10.5,3-4 macro definition ... 1200.10.6,4,11-12,18 macros ... 1200.10.4,1-2; 1200.10.5,5; 1200.10.6,4,11-12,14,16,18; 1200.10.9,1 maintainability ... 1200.10.5,0-1 maintenance ... 1200.10.1,1; 1200.10.2,1; 1200.10.5,9; 1200.10.6,3 module declaration file. ... 1200.10.6,1,3-4,10,18,20 module name ... 1200.10.4,1; 1200.10.6,2,18 nlint ... 1200.10.4,5; 1200.10.6,13; 1200.10.9,1 non-returning functions ... 1200.10.5,4 NULL ... 1200.10.5,1-2; 1200.10.6,5,13,16-17 operator precedence ... 1200.10.6,9; 1200.10.9,1 operators ... 1200.10.5,7; 1200.10.6,8-9,11,15; 1200.10.7,1; 1200.10.9,1 parentheses ... 1200.10.6,9,14 pointer ... 1200.10.5,6 precedence ... 1200.10.3,1; 1200.10.6,1,4,8-10,12-13; 1200.10.9,1 range ... 1200.10.5,4 readability ... 1200.10.5,4; 1200.10.6,9,16; 1200.10.8,1 references ... 1200.10.4,4-5; 1200.10.5,3,9,11; 1200.10.6,1,3-7,10,21; 1200.10.7,1; 1200.10.8,0-2 register variables ... 1200.10.6,6 return ... 1200.10.3,1-2; 1200.10.5,4-5; 1200.10.6,1-2,7,14,17-18 return type ... 1200.10.6,1-2,18 return values ... 1200.10.3,1-2; 1200.10.6,1-2 scope ... 1200.10.6,1-4,18,21 semicolons ... 1200.10.6,11,14,17 short ... 1200.10.4,1; 1200.10.6,5-6,13-14 side effects ... 1200.10.3,2 sign extension ... 1200.10.6,5-6; 1200.10.9,2 single-line comments ... 1200.10.6,13 sizeof ... 1200.10.6,8,11,14 source file ... 1200.10.3,1; 1200.10.4,1; 1200.10.5,9; 1200.10.6,3-4,10,18-19 static ... 1200.10.6,1,14; 1200.10.8,1 struct ... 1200.10.4,4; 1200.10.5,6,8; 1200.10.6,4,7-8,14 struct/union member ... 1200.10.4,2 struct/union/enum tag ... 1200.10.4,2 structure ... 1200.10.4,2-3; 1200.10.5,6; 1200.10.6,6-7,12,16 subsystem ... 1200.10.3,1; 1200.10.4,1; 1200.10.6,1-4,18; 1200.10.9,1 suffix. ... 1200.10.4,1; 1200.10.6,18 switch ... 1200.10.1,1; 1200.10.2,1; 1200.10.3,1; 1200.10.4,1-4; 1200.10.5,3-4; 1200.10.6,8,13-14,16-18; 1200.10.7,1; 1200.10.8,1; 1200.10.9,1 tag ... 1200.10.4,2-4; 1200.10.5,6 trailing comments ... 1200.10.6,13,17 type casts ... 1200.10.6,5 typedef ... 1200.10.4,2-4; 1200.10.5,9; 1200.10.6,14 union ... 1200.10.4,3; 1200.10.5,6; 1200.10.6,4,7,14; 1200.10.8,1 unsigned ... 1200.10.5,8; 1200.10.6,5-6,14 unsigned char ... 1200.10.6,5,14 unsigned int ... 1200.10.6,5-6,14 unsigned long ... 1200.10.5,8; 1200.10.6,6,14 unsigned short ... 1200.10.6,5,14 variable ... 1200.10.4,2; 1200.10.5,3-4,6; 1200.10.6,4-7,12,17; 1200.10.7,1; 1200.10.8,1 void ... 1200.10.6,2,14 while ... 1200.10.5,3-4; 1200.10.6,11,13-14,16-17; 1200.10.7,1 white space ... 1200.10.6,9,13-15; 1200.10.9,2 zero-padding ... 1200.10.6,6; 1200.10.9,2
11. ATTACHMENT - 5ESS CATEGORY CODES (1100.1200.10.100) |
All category codes should consist of two letters (some exceptions may exist for historical reasons). The letters will appear as upper case or lower case, depending on where they are being used (see table 1 in section 100.10.4.4). If you add a new category code, you must IMR this document and have it added to the list.
Table 5. 5ESS Category Code Definitions
|
A few category codes have special, restricted uses. They are described in the following table.
Table 6. Special or Restricted Category Codes
|
12. ATTACHMENT 200 - FILE SUFFIXES (1100.1200.10.200) |
The following table lists the most commonly used file suffixes. Some subsystems may define additional suffixes. The "Project" column indicates the source of the suffix.
Table 7. File Suffixes
|