|------------------------------------------------------------------------------ | sys/style.txt | 1999-02-04 | David R. Tribble |------------------------------------------------------------------------------ PROGRAMMING STYLE INTRODUCTION When we say programming "style", we mean the guidelines used for formatting source code. We do not mean the rules and guidelines used for programming idioms, algorithms, or anything having to do with the actual functioning of the code. "Style" simply means what the source code looks like on a screen or a printout. GUIDELINES Our programming style, like everyone else's, differs from everyone else's. Our style consists of taking the K&R style (see [1]) and applying a few modest changes. The resulting guidelines are listed below. (You may notice that some of these guidelines are different than those employed by the GNU folks. This is a true fact. C'est la vie.) 1. Source lines should be kept to less than 80 columns wide. (Obviously, this rule can be broken occasionally, but try to keep all lines less than 96 columns wide.) Long lines that exceed this limit should be split across two or more lines, with the second and subseqeunt lines indented another level. The line should be split following a low-precedence operator, if possible. Example: while (ch = m_input->getChar(), ch != C_EOF and ch != C_EOLN and ch != m_commentChar) { ... } 2. Spaces are good, but don't use too much of a good thing. Most adjacent (unaligned) tokens are separated by no space or by a single space. One reasonable exception is the use of two spaces on either side of low-precedence operators, such as 'and' and 'or', to visually set apart the left and right operands. Another reasonable exception is following the ';' which separates the controlling expressions in a 'for' statement with two spaces. Spaces are not needed between unary operators and their operands. Examples: k = -c*20 + j; <- Spaces between lower precedence operators b = h << ~sh&f; <- Spaces between lower precedence operators if (a > b+7 and fp->isOpen()) <- 2-space separators ... for (count = start(); count < LIMIT; count++) <- 2-space separators ... 3. Blank lines (i.e., extra newlines) are good, but there is seldom a need for more than two consecutive blank lines. Single blank lines are sufficient to separate most "paragraphs" of code; double blank lines are usually used to separate class declarations, function definitions, and the "major" sections of a source file. 4. Do not use tabs to indent; use spaces (4 spaces per indentation level). 5. You may use tabs to align things (such as member declarators); if you do use tabs, make sure they line up on 8-column boundaries; if you use spaces to align, make sure they line up in the same column as a tab would. Source code containing tabs should look correct when edited or printed using the universal default setting of 8 spaces per tab. 6. A open brace '{' is placed below its controlling keyword. (This differs from the K&R style.) Statements enclosed within braces are indented one more level than the braces. A closing brace '}' is aligned with its opening brace. Examples: if (i > 20) { <- Brace under the 'if' // Enough items to need sorting <- Statements indented a level sortItems(i); } <- Brace aligned with the 'if' else <- 'else' aligned with the 'if' { <- Brace under the 'else' // Get more items do { <- Brace under the 'do' readItem(); <- Statements indented a level i++; } while (i < 20); <- Brace aligned with the 'do' } <- Brace aligned with the 'if' 7. There is only one variable or type declared per declaration. Except for very rare exceptions (such as declaring two or three temporary ints within a block), this is critical to readability. Examples: struct XyzInfo { int num; <- One member per decl int size; <- One member per decl char name[NAMESZ+1]; <- One member per decl }; typedef struct XyzInfo Info; <- typedef is a separate decl void countPunct(const char *s) { const char * cp; <- One variable per decl int count; <- One variable per decl int i, j; <- Reasonable exception ... } 8. There is at most only one declaration or statement per line; this is especially true for structure typedefs - declare the structure, then declare the typedef, on two separate lines. This is critical for readability. Examples: struct XyzInfo { int num; <- One member decl per line int size; <- One member decl per line char name[NAMESZ+1]; <- One member decl per line }; void countPunct(const char *s) { const char * cp; <- One variable decl per line int count; <- One variable decl per line int i, j; <- Reasonable exception ... } 9. Pointer variables are declared so that the '*' is grouped with the type, not the variable; this allows the variable name to be aligned with other variable names within a group of declarations. 10. Variable declarations within a block are grouped together and separated from the statements that follow. Example: void fooBar() { int i; const char * cp; <- Blank line separating decls and stmts cp = getPtr(); i = countItems(cp); } 11. A comment appears at the beginning of each "paragraph" of code; a "paragraph" is an intentionally vague term specifying a logically related group of one or more statements. (Paragraphs are usually separated by a single blank line.) The comment only needs to be a single line comment. Examples: // Open the file <- A code paragraph comment m_fp = fopen(m_fname, "r"); \ if (m_fp == null) | { | // Can't open/read the file |- A code paragraph m_error = E_READ; | return (RC_FAIL); | } / <- Blank line separates paragraphs // Read the first block from the file <- Another code paragraph comment if (not readHeader(m_fp)) \ { | // Can't read the file header |- A code paragraph m_error = E_NOHDR; | return (RC_FAIL); | } / 12. A block comment appears at the beginning of every source and header file. This is the first thing a programmer will read when attempting to understand the contents of the file, so make the description be specific to the contents of the file and avoid vague or general terms. The following style is recommended: //========================================================================= // // Short descriptive sentence or paragraph describing the contents and // purpose of this file. % // % // Usage % // Description and examples of how to use the items (types, functions, % // variables, and constants) defined in this file. % // % // See also % // List of other files, classes, and functions to refer to. // // History // , CCYY-MM-DD, // Description of changes made from the previous version. // // Copyright © by , all rights reserved. // See the file for more information. //------------------------------------------------------------------------- The lines marked with a '%' are optional. The "Usage" clause is only needed if the file contains several related or interacting classes and functions that cannot be explained adequately enough individually. In this case, a single description and examples placed in the block comment for the entire file is better suited than separate block comments for each class or function. The is a number indicating the version number of the file (see elsewhere for guidelines about using version numbers). 13. A simple line comment appears at the end of every source file, indicating the end of the file. This is useful when looking at long printouts to know A) where the end of the file is, and B) that the listing hasn't been truncated. Example: //========================================================================= // foo.cpp ... //------------------------------------------------------------------------- ... // End foo.cpp 14. A block comment appears at the beginning of every structure or class declaration. The following style is recommended: //------------------------------------------------------------------------- // Class // Short descriptive sentence or paragraph describing what this // class is used for. % // % // Usage % // Description and examples of how to use this class. % // % // Notes % // Lengthier description of algorithms used, special cases, that this % // is an asbtract base class, etc. % // % // Caveats % // Warnings about unpredictable behavior, or behavior with ill-formed % // argument values. % // % // Hierarchy % // % // | % // +- % // | % // +- % // +- % // % // See also % // List of other files, classes, and functions to refer to. // // History // , CCYY-MM-DD, // Description of changes made from the previous version. //------------------------------------------------------------------------- The lines marked with a '%' are optional. The short description and the "Usage" clause comprise the most useful parts of the documentation for the class, so fill these with generous amounts of helpful text. A casual programmer wishing to use the class should be able to gain the bulk of his understanding of the external (client) interfaces of the class from these two clauses. These clauses should also contain sufficient references and pointers to other pertinent files and documentation to help him understand the meaning of the class. In situations where a header file contains several related or interacting class types, the primary "Usage" comment should appear in the block comment for the entire file. This usually makes the intent and purpose of the classes more obvious that trying to explain each class's role within whole scheme separately. The is an integer indicating the version number of the class (see elsewhere for guidelines about using version numbers). 15. A block comment appears at the beginning of every function. The following style is recommended: //------------------------------------------------------------------------- // class::function() // Short descriptive sentence or paragraph describing what this // function does. % // % // Usage % // Examples of how to use this class. % // % // Notes % // Lengthier description of algorithms used, special cases, etc. % // % // Returns % // Brief description of the return values, including return values for % // error conditions. % // % // Caveats % // Warnings about unpredictable behavior, behavior for ill-formed % // argument values, and other error conditions. % // % // See also % // List of other files, classes, and functions to refer to. //------------------------------------------------------------------------- The lines marked with a '%' are optional; in particular, the "Returns" clause is not necessary for void functions. The "Usage" clause is usually needed only if a more elaborate example is needed to convey the meaning of the function than the short description can provide. 16. Member declarators, variable declarators, typespec declarators, and macro text definitions are all aligned (on a tab boundary). For example: #define MINSZ 0 // Minimum size allowed #define MAXSZ 800 // Maximum size allowed #define itoc(i) ((i) + '0') // Convert digit to char class FullAddress { public: // Variables char street[20+1]; // Street char city[12+1]; // City char st[2+1]; // State char zip[5+4+1]; // Zipcode }; typedef FullAddress Address; class Employee { private: // Variables char name[12+1]; // Name Address addr; // Address public: // Functions /*void*/ ~Employee(); // Destructor /*void*/ Employee(); // Default constructor int toString(char *buf); // Catenate into a string }; int countThem(const Emp *ls) { int count = 0; while (ls->next() != null) { count++; // Adjust counter ls = ls->next(); // Advance to next item } return (count); } 17. The use of the alternate punctuation keywords 'and', 'or', and 'not' is preferred over their equivalent operators '&&', '||', and '!'. These words really do make the code easier to read. (These keywords are standard in C++, and are available in C by including .) Examples: if (i < IMAX and item[i].isSet()) <- 'and' replaces '&&' ... if (not isOpen()) <- 'not' replaces '!' ... #if not defined(wordsize_h) <- 'not' replaces '!' ... #endif 18. Dates and times use the international standard (ISO-8601) format of "CCYY-MM-DD" and "HH:MM" (24-hour clock). A full date and time specification appears as "CCYY-MM-DD HH:MM:SS.TTT ±HHMM", which specifies the full year number, month, day, hour (in a 24-hour clock), minutes, seconds, fractional seconds (e.g., milliseconds), and timezone offset in hours and minutes from the UTC (Zulu) timezone. Acceptable variations include omitting the punctuation separators, and alternate forms such as "13h45m25s" and "19990820T1345". EXAMPLES //============================================================================= // main.cpp // An example C++ main program. // // See also // program.h // // History // 1.00, 1999-02-04, David R Tribble. // First cut. // // 1.01, 1999-02-28, David R Tribble. // Minor corrections. // // Copyright ©1999 by David R. Tribble, all rights reserved. // See the "notice.txt" file for more information. //----------------------------------------------------------------------------- // System includes #include // Local includes #ifndef program_h #include "program.h" #endif //----------------------------------------------------------------------------- // ::main() // Main program function. // // Returns // Zero (EXIT_SUCCESS) on success, otherwise a non-zero failure code. //----------------------------------------------------------------------------- int main(int argc, char **argv) { Program pgm; return (pgm.main(argc, (const char *const *) argv)); } ... // End main.cpp REFERENCES [1] "The C Programming Language", 2nd edtion (or later), Kernighan and Ritchie, Prentice-Hall, ... . END