Docstoc

CPP programming guidlines

Document Sample
CPP programming guidlines Powered By Docstoc
					            AGH 343 Team




                   DFTPS
C++ Programming Guidelines

                  Version 1.1
DFTPS                                                                       Version:       1.1
C++ Programming Guidelines                                                  Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc


                                    Revision History
          Date            Version                    Description                            Author
12/Mar/2002              1.0         Inception, collection tips from many           Łukasz Dutka
                                     sources
02/Apr/2002              1.0         Corrections                                    Łukasz Dutka
01/Apr/2003              1.1         Final corrections 1                            Andrzej Ruta




Confidential                               AGH 343 Team, 2011                                       ii
DFTPS                                                                               Version:       1.1
C++ Programming Guidelines                                                          Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc


                                          Table of Contents
1.   Introduction                                                                                             1
     1.1       Fundamental Principles                                                                         2
     1.2       Assumptions                                                                                    2
     1.3       The First and Last Guideline                                                                   2

2.   Code Structure                                                                                           3
     2.1       Place module specifications and implementations in separate files                              3
     2.2       Pick a single set of file name extensions to distinguish headers from implementation files     4
     2.3       Avoid defining more than one class per module specification                                    4
     2.4       Avoid putting implementation-private declarations in module specifications                     5
     2.5       Always use #include to gain access to a module's specification                                 5
     2.6       Place module inline function definitions in a separate file                                    6
     2.7       Break large modules into multiple translation units if program size is a concern               7
     2.8       Isolate platform dependencies                                                                  7
     2.9       Protect against repeated file inclusions                                                       8
     2.10         Use a "No_Inline" conditional compilation symbol to subvert inline compilation              8

3.   Code Style                                                                                               9
     3.1       Use a small, consistent indentation style for nested statements                               9
     3.2       Indent function parameters from the function name or scope name                              10
     3.3       Use a maximum line length that would fit on the standard printout paper size                 10
     3.4       Use consistent line folding                                                                  10

4.   Comments                                                                                               11
     4.1       Use C++ style comments rather than C-style comments                                          11
     4.2       Maximize comment proximity to source code                                                    11
     4.3       Avoid end of line comments                                                                   12
     4.4       Avoid the use of vertical bars etc.                                                          12
     4.5       Use an empty comment line to separate comment paragraphs                                     12
     4.6       Avoid redundancy                                                                             12
     4.7       Write self-documenting code rather than comments                                             12
     4.8       Document classes and functions                                                               13
     4.9       Document classes in compatible way to the modeler application                                13

5.   Naming                                                                                                 14
     5.1       Choose a naming convention and apply it consistently                                         14
     5.2       Never declare names beginning with one or more underscores ('_')                             15
     5.3       Avoid using type names that differ only by letter case                                       15
     5.4       Avoid the use of abbreviations                                                               15
     5.5       Avoid the use of suffixes to denote language constructs                                      15
     5.6       Choose clear, legible, meaningful names                                                      15
     5.7       Use correct spelling in names                                                                16
     5.8       Use positive predicate clauses for Booleans                                                  16
     5.9       Use namespaces to partition potential global names by subsystems or by libraries             16
     5.10        Use nouns or noun phrases for class names                                                  17
     5.11        Use verbs for procedure-type function names                                                17
     5.12        Use function overloading when the same general meaning is intended                         18
     5.13        Objects and Function Parameters - Augment names with grammatical elements to emphasize


Confidential                                    AGH 343 Team, 2011                                         iii
DFTPS                                                                             Version:       1.1
C++ Programming Guidelines                                                        Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

     meaning   18
     5.14      Exceptions - Choose exception names with a negative meaning                                 18
     5.15      Exceptions - Use project defined adjectives for exception names                             19
     5.16      Use capital letters for floating point exponent and hexadecimal digits.                     19

6.   Declarations                                                                                          19
     6.1    Namespaces                                                                                     19
     6.2    Namespaces - Limit global declarations to just namespaces                                      19
     6.3    Namespaces - Use a namespace to group non-class functionality                                  19
     6.4    Namespaces - Minimize the use of global and namespace scope data                               19
     6.5    Classes                                                                                        19
     6.6    Classes - Use class rather than struct for implementing abstract data types                    19
     6.7    Classes - Declare class members in order of decreasing accessibility                           20
     6.8    Classes - Avoid declaring public or protected data members for abstract data types             20
     6.9    Classes - Use friends to preserve encapsulation                                                20
     6.10      Classes - Avoid providing function definitions in class declarations                        20
     6.11      Classes - Always provide a default constructor for classes with explicitly-declared
     constructors                                                                                          20
     6.12      Classes - Always declare copy constructors and assignment operators for classes with
     pointer type data members                                                                             21
     6.13      Classes - Never re-declare constructor parameters to have a default value                   21
     6.14      Classes - Always declare destructors to be virtual                                          22
     6.15      Classes - Avoid declaring too many conversion operators and single parameter constructors   23
     6.16      Classes - Never redefine non-virtual functions                                              23
     6.17      Classes - Use non-virtual functions judiciously                                             23
     6.18      Classes - Use constructor-initializers rather than assignments in constructors              23
     6.19      Classes - Never call member functions from a constructor initializer                        24
     6.20      Classes - Beware when calling member functions in constructors and destructors              24
     6.21      Classes - Use static const for integral class constants                                     24
     6.22      Functions – Always declare an explicit function return type                                 25
     6.23      Functions – Always provide formal parameter names in function declarations                  25
     6.24      Functions – Strive for functions with a single point of return                              25
     6.25      Functions – Avoid creating function with global side-effects                                25
     6.26      Functions – Declare function parameters in order of decreasing importance and volatility    25
     6.27      Functions – Avoid declaring functions with a variable number of parameters                  26
     6.28      Functions – Avoid re-declaring functions with default parameters                            26
     6.29      Functions – Maximize the use of const in function declarations                              26
     6.30      Functions – Avoid passing objects by value                                                  26
     6.31      Functions – Never return a reference to a local object                                      27
     6.32      Functions – Never return a de-referenced pointer initialized by new                         27
     6.33      Functions – Never return a non-const reference or pointer to member data                    27
     6.34      Functions – Use inline functions in preference to #define for macro expansion               27
     6.35      Functions – Use default parameters rather than function overloading                         28
     6.36      Functions – Use function overloading to express common semantics                            28
     6.37      Functions – Avoid overloading functions taking pointers and integers                        28
     6.38      Functions – Have operator= return a reference to *this                                      28
     6.39      Functions – Have operator= check for self-assignment                                        29
     6.40      Functions – Minimize complexity                                                             29
     6.41      Types - Define project-wide global system types                                             29
     6.42      Types - Avoid the use of fundamental types                                                  29
     6.43      Types - Use typedef to create synonyms to strengthen local meaning                          29
     6.44      Constants and Objects – Avoid using the preprocessor #define directive for defining

Confidential                                  AGH 343 Team, 2011                                          iv
DFTPS                                                                                Version:       1.1
C++ Programming Guidelines                                                           Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

      constants 30
      6.45      Constants and Objects – Always initialize const objects at declaration                    30
      6.46      Constants and Objects – Never cast away the "constness" of a constant object              30
      6.47      Constants and Objects – Initialize objects at definition                                  30

7.    Expressions                                                                                         31
      7.1      Use redundant parentheses to make compound expressions clearer                             31
      7.2      Avoid nesting expressions too deeply                                                       31
      7.3      Do not assume any particular expression evaluation order                                   31
      7.4      Use 0 for null pointers rather than NULL                                                   31
      7.5      Don't use old-style casting                                                                31
      7.6      Use the new bool type for Boolean expressions                                              32
      7.7      Never compare directly against the Boolean value true                                      32
      7.8      Never compare pointers to objects not within the same array                                33
      7.9      Always assign a null pointer value to a deleted object pointer                             33

8.    Statements                                                                                          33
      8.1      Use an if-statement when branching on Boolean expressions                                  33
      8.2      Use a switch-statement when branching on discrete values                                   33
      8.3      Always provide a default branch for switch-statements for catching errors                  33
      8.4      Use a for-statement or a while-statement when a pre-iteration test is required in a loop   33
      8.5      Use a do-while-statement when a post-iteration test is required in a loop                  33
      8.6      Avoid the use of jump statements in loops                                                  33
      8.7      Don't use the goto-statement                                                               33
      8.8      Avoid the hiding of identifiers in nested scopes                                           33

9.    Memory Management                                                                                   33
      9.1      Avoid mixing C and C++ memory operations                                                   33
      9.2      Always use delete[] when deleting array objects created by new                             34

10.          Error Handling and Exceptions                                                                34
      10.1       Use assertions liberally during development to detect errors                             34
      10.2       Use exceptions only for truly exceptional conditions                                     34
      10.3       Derive project exceptions from standard exceptions                                       34
      10.4       Minimize the number of exceptions used by a given abstraction                            35
      10.5       Declare all exceptions thrown                                                            35
      10.6       Report exceptions at first occurrence                                                    35
      10.7       Define exception handlers in most-derived, to most-base class order                      35
      10.8       Avoid catch-all exception handlers                                                       36
      10.9       Make sure function status codes have an appropriate value                                36
      10.10      Perform safety checks locally; do not expect your client to do so                        36

11.          Portability                                                                                  36
      11.1       Never use hardcoded file pathnames                                                       36
      11.2       Do not assume the representation of a type                                               36
      11.3       Do not assume the alignment of a type                                                    36
      11.4       Do not depend on a particular underflow or overflow behavior                             36
      11.5       Use "stretchable" constants whenever possible                                            36
      11.6       Do not convert from a "shorter" type to a "longer" type                                  37

12.          Reuse                                                                                        37

Confidential                                    AGH 343 Team, 2011                                       v
DFTPS                                                                            Version:       1.1
C++ Programming Guidelines                                                       Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

      12.1      Use standard library components whenever possible                                     37
      12.2      Use templates to reuse data independent behavior                                      37
      12.3      Use public inheritance to reuse class interfaces (subtyping)                          37
      12.4      Use containment rather than private inheritance to reuse class implementations        37
      12.5      Use multiple inheritance judiciously                                                  37

13.          Compilation issues                                                                       38
      13.1      Minimize compilation dependencies                                                     38
      13.2      Define the NDEBUG symbol with a specific value                                        38

14.          Bibliography                                                                             38




Confidential                                  AGH 343 Team, 2011                                     vi
DFTPS                                                                              Version:       1.1
C++ Programming Guidelines                                                         Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc


                            C++ Programming Guidelines
1.      Introduction
        Large software projects are generally undertaken by correspondingly large teams of developers. For the
        code produced by large teams to have project-wide measurable quality, the code must be written in
        accordance with, and be judged against a standard. It is therefore important for large project teams to
        establish a programming standard or set of guidelines.
        The use of a programming standard also makes it possible:
                  to foster the development of robust, legible, easier to maintain code, and to reduce the mental
                   programming efforts required from both more and less experienced developers;
                  to enforce a consistent project-wide coding style;
                  to apply quality measures to the resultant software, both by human and automated means;
                  to quickly acclimatize new developers to the project culture;
                  to support the reuse of project resources: to allow developers to be moved from one project area
                   (or sub-project team) to another without requiring re-learning of new sub-project team cultures.
        The aim of this text is to present C++ programming rules, guidelines and hints (generally referred to as
        guidelines) that can be used as the basis for a standard. It is intended for software engineers working in
        large project teams.
        The current version is purposely focused on programming (although at times it is difficult to draw the line
        between programming and design); design guidelines will be added at a later date.
        The guidelines presented cover the following aspects of C++ development:
                  how the project code should be organized;
                  the programming style (how source code should actually be written);
                  how the code should be documented at the source level;
                  the naming conventions to be employed for both source files and names within the code;
                  when certain language constructs should be used, and when they should be avoided;
                  the behavior in the AGH 343 Team
        They have been collected from a large base of industry knowledge. (See the bibliography for the sources:
        authors and references.) They are based on:
                  well-known software principles;
                  "good" software practices;
                  lessons learned;
                  subjective opinions.
        Most are based on a handful of the first category, and large doses of the second and third. Unfortunately,
        some are also based on the last category; mainly, because programming is a highly subjective activity: there
        is no widely accepted "best" or "right" way to code everything.




Confidential                                    AGH 343 Team, 2000                                                   1
DFTPS                                                                               Version:       1.1
C++ Programming Guidelines                                                          Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

1.1     Fundamental Principles
        Clear, understandable C++ source code is the primary goal of most of the rules and guidelines and a major
        contributing factor to software reliability and maintainability. What is meant by clear and understandable
        code can be captured in the following three simple fundamental principles [Kruchten, 94].
        Minimal Surprise-Over its lifetime, source code is read more often than it is written, especially
        specifications. Ideally, code should read like an English-language description of what is being done, with
        the added benefit hat it executes. Programs are written more for people than for computers. Reading code is
        a complex mental process that can be eased by uniformity, also referred to in this guide as the minimal-
        surprise principle. A uniform style across an entire project is a major reason for a team of software
        developers to agree on programming standards, and it should not be perceived as some kind of punishment
        or as an obstacle to creativity and productivity.
        Single Point of Maintenance-Whenever possible, a design decision should be expressed at only one point in
        the source, and most of its consequences should be derived programmatically from this point. Violations of
        this principle greatly jeopardize maintainability and reliability, as well as understandability.
        Minimal Noise-Finally, as a major contribution to legibility, the minimal-noise principle is applied. That is,
        an effort made to avoid cluttering the source code with visual "noise": bars, boxes, and other text with low
        information content or information that does not contribute to the understanding of the purpose of the
        software.
        The intended spirit of the guidelines expressed herein, is not to be overly restrictive, but rather to attempt to
        provide guidance for the correct and safe usage of language features. The key to good software resides in:
                  knowing each feature, its limitations and potential dangers;
                  knowing exactly under which circumstances the feature is safe to use;
                  making the decision to use the feature highly visible;
                  using the feature with care and moderation, where appropriate.

1.2     Assumptions
        The guidelines presented here make a small number of basic assumptions:
        The reader knows C++
        The use of advanced C++ features is encouraged wherever beneficial, rather than discouraged due to the
        fact that some programmers are unfamiliar with them. This is the only way in which the project can really
        benefit from using C++. C++ should not be used as if it was C, in fact the object-oriented features of C++
        preclude its use as in C. Paraphrasing the code in comments is discouraged; on the contrary, the source
        code should be used in place of comments wherever feasible.
        Follow large project practice.
        Many rules offer the most value in large systems, although they can also be used in a small system, if only
        for the sake of practice and uniformity at the project or corporate level.
        Coding follows an object-oriented design
        Many rules will support a systematic mapping of object-oriented (OO) concepts to C++ features and
        specific naming conventions.

1.3     The First and Last Guideline
        When you cannot find an applicable rule or guideline; when a rule obviously does not apply, or when
        everything else fails: use common sense, and check the fundamental principles. This rule overrides all of
        the others. Common sense is required even when rules and guidelines exist.


Confidential                                   AGH 343 Team, 2000                                                    2
DFTPS                                                                               Version:       1.1
C++ Programming Guidelines                                                          Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

2.      Code Structure
        Large systems are usually developed as a number of smaller functional subsystems. Subsystems themselves
        are usually constructed from a number of code modules. In C++, a module normally contains the
        implementation for a single, or on rare occasions, a set of closely related abstractions. In C++, an
        abstraction is normally implemented as a class. A class has two distinct components: an interface visible to
        the class clients, providing a declaration or specification of the class capabilities and responsibilities; and
        an implementation of the declared specification (the class definition).
        Similar to the class, a module also has an interface and an implementation: the module interface contains
        the specifications for the contained module abstractions (class declarations); and the module
        implementation contains the actual implementation of the abstractions (class definitions).
        In the construction of the system; subsystems may also be organized into collaborative groups or layers to
        minimize and control their dependencies.

2.1     Place module specifications and implementations in separate files
        A module's specification should be placed in a separate file from its implementation-the specification file is
        referred to as a header. A module's implementation may be placed in one or more implementation files.
        If a module implementation contains extensive inline functions, common implementation-private
        declarations, test code, or platform-specific code, then separate these parts into their own files and name
        each file after its part's content.
        If program executable sizes are a concern, then rarely used functions should also be placed in their own
        individual files.
        Construct a part file name in the following manner:
                  Use the module's main abstraction name as the module name.
                  Append a part type name to the module name. Choose part type names that are indicative of their
                   content.
                  The module name and part name should be separated by a separator (e.g. '_' (underscore) or '.'
                   (period)); choose a separator and apply it consistently.
                   File_Name::=
                   <Module_Name> [<Separator> <Part_Name>] '.' <File_Extension>
                  For better predictability, use the same letter case for file names as for names within the code.
        The following is an example module partitioning and naming scheme:
                  module.inlines.cc-if a module has many potentially inline-able functions, then place the function
                   definitions in a separate .inlines file (see "Place module inline function definitions in a separate
                   file").
                  module.private.hh-if a module has many common implementation-private declarations that are
                   referenced by other parts, then separate these declarations out into a .private part for inclusion by
                   other implementation files.
                  module.private.cc-a module's implementation-private function definitions, separated out for
                   editing convenience.
                  module.function_name.cc-if executable size is of concern, then specialized member functions that
                   are not required by many programs should be separated out into their own individual
                   implementation files (see "Break large modules into multiple translation units if program size is a
                   concern"). If overloaded functions are placed in separate files, each file function name should be
                   suffixed with an instance number. E.g., function_name1 for the first instance of a separate
                   overloaded function.


Confidential                                   AGH 343 Team, 2000                                                        3
DFTPS                                                                               Version:       1.1
C++ Programming Guidelines                                                          Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

                  module.nested_class.cc-the member functions of a module's nested class, placed in their own file.
                  module.test.[hh\cc]-if a module requires extensive test code, then the test code should be declared
                   in a friend test class. The friend test class should be called Module.Test. Declaring the test code as
                   a friend class facilitates the independent development of the module and its test code and allows
                   the test code to be omitted from the final module object code without source changes.
                  module.platform_name.cc-separate out any module platform dependencies and call the part name
                   after the platform name (see "Isolate platform dependencies").
        Choose a module partitioning and naming scheme and apply it consistently.

2.1.1   Example
         SymaNetwork.hh          //                Contains the declaration for a
                                 //                class named "SymaNetwork".
          SymaNetwork.Inlines.cc //                Inline definitions sub-unit
          SymaNetwork.cc         //                Module's main implementation unit
          SymaNetwork.Private.cc //                Private implementation sub-unit
          SymaNetwork.Test.cc    //                Test code sub-unit
2.1.2   Rationale
        Separating out a module's specification from its implementation facilitates independent development of
        user and supplier code.
        Breaking a module's implementation into multiple translation units provides better support for object code
        removal, resulting in smaller executable sizes.
        Using a regular and predictable file naming and partitioning convention allows a module's content and
        organization to be understood without inspection of its actual contents.
        Passing names through from the code to the file name increases predictability and facilitates the building of
        file-based tools without requiring complex name mapping [Ellemtel, 1993].

2.2     Pick a single set of file name extensions to distinguish headers from implementation files
        Commonly used file name extensions are: .h, .H, .hh, .hpp, and .hxx for header files; and .c, .C, .cc, .cpp,
        and .cxx for implementations. Pick a set of extensions and use them consistently.

2.2.1   Example
         SymaNetwork.hh             //   The extension ".hh" used to designate
                                    //   a "SymaNetwork" module header.
          SymaNetwork.cc            //   The extension ".cc" used to designate
                                    //   a "SymaNetwork" module implementation.
2.2.2   Notes
        The C++ draft standard working paper also uses the extension ".ns" for headers encapsulated by a
        namespace.

2.3     Avoid defining more than one class per module specification
        Only upon rare occasions should multiple classes be placed together in a module; and then only if they are
        closely associated (e.g., a container and its iterator). It is acceptable to place a module's main class and its
        supporting classes within the same header file if all classes are always required to be visible to a client
        module.

2.3.1   Rationale
        Reduces a module's interface and others dependencies upon it.

Confidential                                   AGH 343 Team, 2000                                                     4
DFTPS                                                                              Version:       1.1
C++ Programming Guidelines                                                         Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

2.4     Avoid putting implementation-private declarations in module specifications
        Aside from class private members, a module's implementation-private declarations (e.g. implementation
        types and supporting classes) should not appear in the module's specification. These declarations should be
        placed in the needed implementation files unless the declarations are needed by multiple implementation
        files; in that case the declarations should be placed in a secondary, private header file. The secondary,
        private header file should then be included by other implementation files as needed.
        This practice ensures that:
                  a module specification cleanly expresses its abstraction and is free of implementation artifacts;
                  a module specification is kept as small as possible and thus minimizes inter-module compilation
                   dependencies (see also "Minimize compilation dependencies");

2.4.1   Example
         // Specification of module foo, contained in file "foo.hh"
         //
         class foo
         {
         .. declarations
         };
         // End of "foo.hh"
         // Private declarations for module foo, contained in file
         // "foo.private.hh" and used by all foo implementation files.
         ... private declarations
         // End of "foo.private.hh"
         // Module foo implementation, contained in multiple files
         // "foo.x.cc" and "foo.y.cc"
         // File "foo.x.cc"
         //
         #include "foo.hh" // Include module's own header
         #include "foo.private.hh" // Include implementation
         // required declarations.
         ... definitions
         // End of "foo.x.cc"
         // File "foo.y.cc"
         //
         #include "foo.hh"
         #include "foo.private.hh"
         ... definitions
         // End of "foo.y.cc"
2.5     Always use #include to gain access to a module's specification
        A module that uses another module must use the preprocessor #include directive to acquire visibility of
        the supplier module's specification. Correspondingly, modules should never re-declare any part of a
        supplier module's specification.
        When including files, only use the #include <header> syntax for "standard" headers; use the
        #include "header" syntax for the rest.
        Use of the #include directive also applies to a module's own implementation files: a module
        implementation must include its own specification and private secondary headers (see "Place module
        specifications and implementations in separate files").




Confidential                                   AGH 343 Team, 2000                                                     5
DFTPS                                                                                Version:       1.1
C++ Programming Guidelines                                                           Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

2.5.1   Example
         // The specification of module foo in its header file
         // "foo.hh"
         //
         class foo
         {
         ... declarations
         };
         // End of "foo.hh"

          // The implementation of module foo in file "foo.cc"
          //
          #include "foo.hh" // The implementation includes its own
          // specification
          ... definitions for members of foo
          // End of "foo.cc"
        An exception to the #include rule is when a module only uses or contains a supplier module's types
        (classes) by-reference (using pointer or reference-type declarations); in this case the by-reference usage or
        containment is specified using a forward declaration (see also "Minimize compilation dependencies") rather
        than a #include directive.
        Avoid including more than is absolutely needed: this means that module headers should not include other
        headers that are required only by the module implementation.

2.5.2   Example
         #include "a_supplier.hh"

          class needed_only_by_reference;// Use a forward declaration
                                         //for a class if we only need
                                         // a pointer or a reference
                                         // access to it.
          void operation_requiring_object(a_supplier required_supplier, ...);
          //
          // Operation requiring an actual supplier object; thus the
          // supplier specification has to be #include'd.

          void some_operation(needed_only_by_reference& a_reference, ...);
          //
          // Some operation needing only a reference to an object; thus
          // should use a forward declaration for the supplier.
2.5.3   Rationale
        This rule ensures that:
                  there is always only a single declaration of a module's interface, and that all clients see exactly the
                   same interface;
                  the compilation dependencies between modules are minimized;
                  clients don't needlessly incur compilation overhead for code that is not required.

2.6     Place module inline function definitions in a separate file
        When a module has many inline functions, their definitions should be placed in a separate, inline-
        function-only file. The inline function file should be included at the end of the module's header file.
        See also "Use a No_Inline conditional compilation symbol to subvert inline compilation".

Confidential                                    AGH 343 Team, 2000                                                     6
DFTPS                                                                                Version:       1.1
C++ Programming Guidelines                                                           Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

2.6.1   Rationale
        This technique keeps implementation details from cluttering a module's header, thus, preserving a clean
        specification. It also helps reduce code replication when not compiling inline: using conditional
        compilation, the inline functions can be compiled into a single object file as opposed to being compiled
        statically into every using module. Correspondingly, inline function definitions should not be defined in
        class definitions unless they are absolutely trivial.

2.7     Break large modules into multiple translation units if program size is of concern
        Break large modules into multiple translation units to facilitate un-referenced code removal during program
        linking. Member functions that are rarely referenced should be segregated into separate files from those
        that are commonly used. In the extreme, individual member functions can be placed in their own files
        [Ellemtel, 1993].

2.7.1   Rationale
        Linkers are not all equally capable of eliminating un-referenced code within an object file. Breaking large
        modules into multiple files allows these linkers to reduce executable sizes by eliminating the linking of
        whole object files [Ellemtel, 1993].

2.7.2   Notes
        It may also be worthwhile considering first whether the module should be broken down into smaller
        abstractions.

2.8     Isolate platform dependencies
        Separate out platform-dependent code from platform-independent code; this will facilitate porting.
        Platform-dependent modules should have file names qualified by their platform name to highlight the
        platform dependence.

2.8.1   Example
         SymaLowLevelStuff.hh          //                     "LowLevelStuff"
                                       //                     specification
          SymaLowLevelStuff.SunOS54.cc //                     SunOS 5.4 implementation
          SymaLowLevelStuff.HPUX.cc    //                     HP-UX implementation
          SymaLowLevelStuff.AIX.cc     //                     AIX implementation
2.8.2   Notes
        From an architectural and maintenance viewpoint, it is also good practice to contain platform dependencies
        in a small number of low level subsystems.
        Adopt a standard file content structure and apply it consistently
        A suggested file content structure consists of the following parts in the following order:
               1.   Repeated inclusion protection (specification only).
               2.   Optional file and version control identification.
               3.   File inclusions needed by this unit.
               4.   The module documentation (specification only).
               5.   Declarations (class, type, constants, objects and functions) and additional textual specifications
                    (preconditions and postconditions, and invariants).
               6.   Inclusion of this module's inline function definitions.
               7.   Definitions (objects and functions) and implementation private declarations.

Confidential                                    AGH 343 Team, 2000                                                      7
DFTPS                                                                             Version:       1.1
C++ Programming Guidelines                                                        Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

               8.   Copyright notice.
               9.   Optional version control history.

2.8.3   Rationale
        The above file content ordering, presents the client pertinent information first; and is consistent with the
        rationale for the ordering of a class' public, protected and private sections.

2.8.4   Notes
        Depending on corporate policy, the copyright information may need to be placed at the top of the file.

2.9     Protect against repeated file inclusions
        Repeated file inclusion and compilation should be prevented by using the following construct in each
        header file:
          #if !defined(module_name) // Use preprocessor symbols to
          #define module_name                   // protect against repeated
                                                // inclusions... // Declarations go here
          #include "module_name.inlines.cc" // Optional inline
                                                            // inclusion goes here.
          // No more declarations after inclusion of module's
          // inline functions.
          #endif // End of module_name.hh
        Use the module file name for the inclusion protection symbol. Use the same letter-case for the symbol as
        for the module name.

2.10    Use a "No_Inline" conditional compilation symbol to subvert inline compilation
        Use the following conditional compilation construct to control inline versus out-of-line compilation of
        inline-able functions:
          // At the top of module_name.inlines.hh
          #if !defined(module_name_inlines)
          #define module_name_inlines

          #if defined(No_Inline)
          #define inline // Nullify inline keyword
          #endif

          ... // Inline definitions go here
          #endif // End of module_name.inlines.hh

          // At the end of module_name.hh
          //
          #if !defined(No_Inline)
          #include "module_name.inlines.hh"
          #endif

          // At the top of module_name.cc after inclusion of
          // module_name.hh
          //
          #if defined(No_Inline)
          #include "module_name.inlines.hh"
          #endif



Confidential                                    AGH 343 Team, 2000                                                    8
DFTPS                                                                            Version:       1.1
C++ Programming Guidelines                                                       Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

        The conditional compilation construct is similar to the multiple inclusion protection construct. If the
        No_Inline symbol is not defined, then the inline functions are compiled with the module specification
        and automatically excluded from the module implementation. If the No_Inline symbol is defined, then
        the inline definitions are excluded from the module specification but included in the module
        implementation with the keyword inline nullified.

2.10.1 Rationale
       The above technique allows for reduced code replication when inline functions are compiled out-of-line.
       By using conditional compilation, a single copy of the inline functions is compiled into the defining
       module; versus replicated code, compiled as "static" (internal linkage) functions in every using module
       when out-of-line compilation is specified by a compiler switch.

2.10.2 Notes
       Use of conditional compilation increases the complexity involved in maintaining build dependencies. This
       complexity is managed by always treating headers and inline function definitions as a single logical unit:
       implementation files are thus dependent upon both header and inline function definition files.

3.      Code Style
3.1     Use a small, consistent indentation style for nested statements
        Consistent indentation should be used to visually delineate nested statements; indentation of between 2 and
        4 spaces has been proven to be the most visually effective for this purpose. The AGH 343 Team internal
        standard is a regular indentation of 2 spaces.
        The block statement delimiters ({}) for class, function or method, should be at the same level of
        indentation as surrounding statements (by implication, this means that {} are vertically aligned).
        However, the block statement delimiters ({}) for other part of a code e.g. if, while, switch, do etc. should
        be written as on the below example.
        Statements within each kind of blocks should be indented by the standard number of spaces.
        Case labels of a switch statement should be intended by 2 indentation level from the switch statement;
        statements within the switch statement can then be indented by 2 indentation level from the switch
        statement itself and the case labels.

3.1.1   Example
         void some_function()
         {// function block is in new line without indentation
           if (true){// New block
             foo(); // Statement(s) within block
                     // indented by 2 spaces.
           }else{
             bar();
           }

               while (expression){
                 statement();
               }

               switch (i){
                 case 1:
                   do_something();// Statements indented by
                                // 1 indentation level from

Confidential                                 AGH 343 Team, 2000                                                       9
DFTPS                                                                             Version:       1.1
C++ Programming Guidelines                                                        Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

                 break;                   // the switch statement itself.
               case 2:
                 //...
               default:
                 //...
            }
          }//end of function block in new line without indentation
3.1.2   Rationale
        An indentation of 2 spaces is a compromise between allowing easy recognition of blocks, and allowing
        sufficient nested blocks before code drifts too far off the right edge of a display monitor or printed page.

3.2     Indent function parameters from the function name or scope name
        If a function declaration cannot fit on a single line, then place the first parameter on the same line as the
        function name; and subsequent parameters each on a new line, indented at the same level as the first
        parameter. This style of declaration and indentation, shown below, leaves white spaces below the function
        return type and name; thus, improving their visibility.

3.2.1   Example
          void foo::function_decl( some_type first_parameter,
                                               some_other_type second_parameter,
                                               status_type and_subsequent);
        If following the above guideline happens to cause line wrap, or parameters to be too far indented, then
        indent all parameters from the function name or scope name (class, namespace), with each on a separate
        line:

3.2.2   Example
         void foo::function_with_a_long_name( // function name is
                                              // much less visible
                                              some_type first_parameter,
                                              some_other_type second_parameter,
                                              status_type and_subsequent);
        See also alignment rules below.

3.3     Use a maximum line length that would fit on the standard printout paper size
        The maximum length of program lines should be limited to prevent loss of information when printed on
        either standard (letter) or default printout paper size.
        Assuming 80 characters per one line is an internal standard of AGH 343 Team.

3.3.1   Notes
        If the level of indentation causes deeply nested statements to drift too far to the right, and statements to
        extend much beyond the right margin, then it is probably a good time to consider breaking the code into
        smaller, more manageable functions.

3.4     Use consistent line folding
        When parameter lists in function declarations, definitions and calls, or enumerators in an enum declarations
        cannot fit on a single line, break the line after each list element and place each element on a separate line
        (see also "Indent function parameters from the function name or scope name").

3.4.1   Example
         enum color { red,

Confidential                                  AGH 343 Team, 2000                                                      10
DFTPS                                                                              Version:       1.1
C++ Programming Guidelines                                                         Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

                               orange,
                               yellow,
                               green,
                               //...
                               violet
                                        };
        If a class or function template declaration is overly long, fold it onto consecutive lines after the template
        argument list. For example (declaration from the standard Iterators Library, [X3J16, 95]):
          template <class InputIterator, class Distance>
          void advance(InputIterator& i, Distance n);

4.      Comments
        This chapter provides guidance on the use of comments in the code.
        Comments should be used to complement source code, never to paraphrase it:
                  They should supplement source code by explaining what is not obvious; they should not duplicate
                   the language syntax or semantics.
                  They should help the reader to grasp the background concepts, the dependencies, and especially
                   complex data encoding or algorithms.
                  They should highlight: deviations from coding or design standards; the use of restricted features;
                   and special "tricks."
        For each comment, the programmer should be able to easily answer the question: "What value is added by
        this comment?" Generally, well-chosen names often eliminate the need for comments. Comments, unless
        they participate in some formal Program Design Language (PDL), are not checked by the compiler;
        therefore, in accordance with the single-point-of-maintenance principle, design decisions should be
        expressed in the source code rather than in comments, even at the expense of a few more declarations.

4.1     Use C++ style comments rather than C-style comments
        The C++ style "//" comment delimiter should be used in preference to the C-style "/*...*/".

4.1.1   Rationale
        C++ style comments are more visible and reduce the risk of accidentally commenting-out vast expanses of
        code due to a missing end-of-comment delimiter.

4.1.2   Counter-Example
         /* start of comment with missing end-of-comment delimiter
         do_something();
         do_something_else(); /* Comment about do_something_else */
                                       // End of comment is here. ^
                                       // Both do_something and
                                       // do_something_else
                                       // are accidentally commented out!
         Do_further();
4.2     Maximize comment proximity to source code
        Comments should be placed near the code they are commenting upon; with the same level of indentation,
        and attached to the code using a blank comment line.
        Comments that apply to multiple, successive source statements should be placed above the statements-
        serving as an introduction to them. Likewise, comments associated with individual statements should be
        placed below the statements.


Confidential                                   AGH 343 Team, 2000                                                      11
DFTPS                                                                              Version:       1.1
C++ Programming Guidelines                                                         Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

4.2.1   Example
         // A pre-statements comment applicable
         // to a number of following statements
         //
         ...
         void function();
         //
         // A post-statement comment for
         // the preceding statement.
4.3     Avoid end of line comments
        Avoid comments on the same line as a source construct: they often become misaligned. Such comments are
        tolerated, however, for descriptions of elements in long declarations, such as enumerators in an enum
        declaration.

4.4     Avoid the use of vertical bars etc.
        Avoid the use of vertical bars, closed frames or boxes, even for major construct (such as functions and
        classes); they just add visual noise and are difficult to keep consistent. Use blank lines to separate related
        blocks of source code rather than heavy comment lines. Use a single blank line to separate constructs
        within functions or classes. Use double blank lines to separate functions from each other.
        Frames or forms may have the look of uniformity, and of reminding the programmer to document the code,
        but they often lead to a paraphrasing style [Kruchten, 94].

4.5     Use an empty comment line to separate comment paragraphs
        Use empty comments, rather than empty lines, within a single comment block to separate paragraphs

4.5.1   Example
         // Some explanation here needs to be continued
         // in a subsequent paragraph.
         //
         // The empty comment line above makes it
         // clear that this is another
         // paragraph of the same comment block.
4.6     Avoid redundancy
        Avoid repeating program identifiers in comments, and replicating information found elsewhere-provide a
        pointer to the information instead. Otherwise any program change may require maintenance in multiple
        places. And failure to make the required comment changes everywhere will result in misleading or wrong
        comments: these end up being worse than no comments at all.

4.7     Write self-documenting code rather than comments
        Always aim to write self-documenting code rather than providing comments. This can be achieved by
        choosing better names; using extra temporary variables; or re-structuring the code. Take care of the style,
        syntax, and spelling in comments. Use natural language comments rather than telegraphic, or cryptic style.

4.7.1   Example
        Replace:
          do{
          ...
          } while (string_utility.locate(ch, str) != 0);
          // Exit search loop when found it.


Confidential                                  AGH 343 Team, 2000                                                    12
DFTPS                                                                               Version:       1.1
C++ Programming Guidelines                                                          Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

        with:
          do{
          ...
            found_it = (string_utility.locate(ch, str) == 0);
          } while (!found_it);
4.8     Document classes and functions
        Although self-documenting code is preferred over comments; there is generally a need to provide
        information beyond an explanation of complicated parts of the code. The information that is needed is
        documentation of at least the following:
                  the purpose of each class;
                  the purpose of each function if its purpose is not obvious from its name;
                  the meaning of any return values; e.g., the meaning of a Boolean return value for a non-predicate
                   function: that is, does a true value mean the function was successful;
                  conditions under which exceptions are raised;
                  preconditions and postconditions on parameters, if any;
                  additional data accessed, especially if it is modified: especially important for functions with side-
                   effects;
                  any limitations or additional information needed to properly use the class or function;
                  for types and objects, any invariants or additional constraints that cannot be expressed by the
                   language.

4.8.1   Rationale
        The code documentation in conjunction with the declarations should be sufficient for a client to use the
        code; documentation is required since the full semantics of classes, functions, types and objects cannot be
        fully expressed using C++ alone.

4.9     Document classes in compatible way with the modeler application
        If whole project is under control of some modeler application then it is important to document new features
        of classes (methods, fields etc.) using standard compatible with reverse engineering application connected
        to used modeler.

4.9.1   Example for Rational Rose
        If project is generated and controlled by Rational Rose then documentation should look like:
         //##ModelId=3C7586A60222
         //##Documentation
         //## It sets serverName filed using configuration object.
         void set_serverName();
        First line is reserved for ModelId field, which is generated automatically by Rose, so it can‟t be changed.
        Second line is formatted in compliance with Rational Rose internal standard and must look like in the
        above example.
        The next lines contains text, which is essential documentation; each line must be started with “//## “
        (with one space character after //##).




Confidential                                    AGH 343 Team, 2000                                                   13
DFTPS                                                                             Version:       1.1
C++ Programming Guidelines                                                        Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

4.10    Update revision table
        Each file has a revision table. When you finish work with some file, update this table according to
        guidelines included in document „Revision commenting style‟.

4.11    AGH 343 Team internal standard
        AGH 343 Team defined its own standard of commenting the source code. It stays in compliance with the
        guidelines described above. However, for the sake of code commenting integrity with its development in
        specific semi-distributed environment, several extensions were specified. For details you are encouraged to
        look into “Code review guide” and “Revision commenting style” doucuments.

5.      Naming
        This chapter provides guidance on the choice of names for various C++ entities. Coming up with good
        names for program entities (classes, functions, types, objects, literals, exceptions, namespaces) is not easy
        matter. For medium-to-large applications the problem is made even more challenging: here name conflicts
        and lack of synonyms to designate distinct but similar concepts add to the degree of difficulty.
        Using a naming convention can lessen the mental effort required for inventing suitable names. Aside from
        this benefit, a naming convention has the added benefit of enforcing consistency in the code. To be useful,
        a naming convention should provide guidance on: typographical style (or how to write the names); and
        name construction (or how to choose names).

5.1     Choose a naming convention and apply it consistently
        It is not so important which naming convention is used as long as it is applied consistently. Uniformity in
        naming is far more important than the actual convention: uniformity supports the principle of minimal
        surprise.
        Because C++ is a case-sensitive language, and because a number of distinct naming conventions are in
        widespread use by the C++ community, it will rarely be possible to achieve absolute naming consistency.
        We recommend picking a naming convention for the project based upon the host environment (e.g., UNIX
        or Windows) and the principle libraries used by the project; to maximize the code consistency:


        UNIX-hosted projects that don't make much use of commercial libraries (e.g., the X Window library, X
        Toolkit Intrinsics and Motif) may prefer to use an all-lower-case, underscore-separated-word convention:
        this is the convention used for UNIX system calls and also by the C++ draft standard working paper.
        UNIX-hosted projects that are centered around commercial libraries may prefer to use a capitalized style,
        also commonly referred to as the Smalltalk style-a style where the initial letter of words are capitalized, and
        the words are concatenated together without separators.
        Microsoft®Windows-based projects may elect to use the unusual Microsoft®"Hungarian" notation. We,
        don't recommend this style; however, as it is contrary to the fundamental principles underlying the
        guidelines in this text.

5.1.1   Notes
        The careful reader will observe that the examples in this text currently do not follow all the guidelines. This
        is partly due to the fact that examples are derived from multiple sources, and also due to the desire to
        conserve paper. Therefore the formatting guidelines have not been meticulously applied. But the message is
        "do as I say, not as do".




Confidential                                 AGH 343 Team, 2000                                                   14
DFTPS                                                                              Version:       1.1
C++ Programming Guidelines                                                         Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

5.2     Never declare names beginning with one or more underscores ('_')
        Names with a single leading underscore ('_') are often used by library functions ("_main" and "_exit").
        Names with double leading underscores ("__"); or a single leading underscore followed by a capital letter
        are reserved for compiler internal use.
        Also avoid names with adjacent underscores, as it is often difficult to discern the exact number of
        underscores.

5.3     Avoid using type names that differ only by letter case
        It is hard to remember the differences between type names that differ only by letter case, and thus easy to
        get confused about them.

5.4     Avoid the use of abbreviations
        Abbreviations may be used if they are either commonly used in the application domain (e.g., FFT for Fast
        Fourier Transform), or they are defined in a project-recognized list of abbreviations. Otherwise, it is very
        likely that similar but not quite identical abbreviations will occur here and there, introducing confusion and
        errors later (e.g., track_identification being abbreviated trid, trck_id, tr_iden, tid, tr_ident, and so on).

5.5     Avoid the use of suffixes to denote language constructs
        The use of suffixes for categorizing kinds of entities (such as type for type, and error for exceptions) is
        usually not very effective for imparting understanding of the code. Suffixes such as array and struct also
        imply a specific implementation, which, in the event of an implementation change-changing the
        representation from a struct or array-would either have an adverse effect upon any client code, or would be
        misleading.
        Suffixes can however be useful in a number of limited situations:
                  when the choice of appropriate identifiers is very limited; give the best name to the object and use
                   a suffix for the type;
                  when it represents an application-domain concept, e.g., aircraft_type.

5.6     Choose clear, legible, meaningful names
        Choose names from the usage perspective; and use adjectives with nouns to enhance local (context
        specific) meaning. Also make sure that names agree with their types.
        Choose names so that constructs such as:
          object_name.function_name(...);
          object_name->function_name(...);
        are easy to read and appear meaningful.
        Speed of typing is not an acceptable justification for using short or abbreviated names. One-letter and short
        identifiers are often an indication of poor choice or laziness. Exceptions are well-recognized instances such
        as using E for the base of the natural logarithms, or Pi.
        Unfortunately, compilers and supporting tools, sometimes limit length of names; thus, care should be taken
        to ensure that long names do not differ only by their trailing characters: the differentiating characters may
        be truncated by these tools.

5.6.1   Example
         void set_color(color new_color)
         {
           ...
           the_color = new_color;

Confidential                                   AGH 343 Team, 2000                                                  15
DFTPS                                                                            Version:       1.1
C++ Programming Guidelines                                                       Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

             ...
          }
        is better than:
         void set_foreground_color(color fg);
        and:
          void set_foreground_color(color foreground){
             ...
             the_foreground_color = foreground;
             ...
          }
        The naming in the first example is superior to the other two: new_color is qualified and agrees with its
        type; thereby strengthening the semantics of the function.
        In the second case, the intuitive reader could infer that fg was intended to mean foreground; however, in
        any good programming style, nothing should be left to reader intuition or inference.
        In the third case, when the parameter foreground is used (away from its declaration), the reader is led to
        believe that foreground in fact means foreground color. It could conceivably; however, have been of any
        type that is implicitly convertible to a color.

5.6.2   Notes
        Forming names from nouns and adjectives, and ensuring that names agree with their types follows natural-
        language and enhances both code readability and semantics.

5.7     Use correct spelling in names
        Parts of names that are English words should be spelled correctly and conform to the project required form,
        i.e., consistently English or American, but not both. This is equally true for comments.
        An internal standard for AGH 343 Team is an American Language. So, we write color instead colour etc.

5.8     Use positive predicate clauses for Booleans
        For Boolean objects, functions and function arguments, use a predicate clause in the positive form, e.g.,
        found_it, is_available, but not is_not_available.

5.8.1   Rationale
        When negating predicates, double negatives are harder to understand.

5.9     Use namespaces to partition potential global names by subsystems or by libraries
        If a system is decomposed into subsystems, use the subsystem names as namespace names for partitioning
        and minimizing the system's global namespace. If the system is a library, use a single outer-most
        namespace for the whole library.
        Give each subsystem or library namespace a meaningful name; in addition give it an abbreviated or
        acronym alias. Choose abbreviated or acronym aliases that are unlikely to clash, e.g. the ANSI C++ draft
        standard library [Plauger, 95] defines std as the alias for iso_standard_library.
        If the compiler doesn't yet support the namespace construct, use name prefixes to simulate namespaces. For
        example, the public names in the interface of a system management subsystem could be prefixed with syma
        (short for System Management).




Confidential                                 AGH 343 Team, 2000                                                    16
DFTPS                                                                              Version:       1.1
C++ Programming Guidelines                                                         Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

5.9.1   Rationale
        Using namespaces to enclose potentially global names, helps to avoid name collisions when code is
        developed independently (by sub-project teams or vendors). A corollary is that only namespace names are
        global.

5.10    Use nouns or noun phrases for class names
        Use a common noun or noun phrase in singular form, to give a class a name that expresses its abstraction.
        Use more general names for base classes and more specialized names for derived classes.
          typedef ... reference; // From the standard library
          typedef ... pointer;               // From the standard library
          typedef ... iterator; // From the standard library
          class bank_account {...};
          class savings_account : public bank_account {...};
          class checking_account : public bank_account {...};
        When there is a conflict or shortage of suitable names for both objects and types; use the simple name for
        the object, and add a suffix such as mode, kind, code, and so on for the type name.
        Use a plural form when expressing an abstraction that represents a collection of objects.
          typedef some_container<...> yellow_pages;
        When additional semantics is required beyond just a collection of objects, use the following from the
        standard library as behavioral patterns and name suffixes:
                  vector-a randomly accessible sequence container;
                  list-an ordered sequence container;
                  queue-a first-in-first-out sequence container;
                  deque-a double-ended queue;
                  stack-a last-in-first-out sequence container;
                  set-a key-accessed (associative) container;
                  map-a key-accessed (associative) container;

5.11    Use verbs for procedure-type function names
        Use verbs or action phrases for functions that don't have return values (function declarations with a void
        return type), or functions that return values by pointer or reference parameters.
        Use nouns or substantives for functions that return only a single value by a non-void function return type.
        For classes with common operations (a pattern of behavior), use operation names drawn from a project list
        of choices. For example: begin, end, insert, erase (container operations from the standard library).
        Avoid "get" and "set" naming mentality (prefixing functions with the prefixes "get" and "set"), especially
        for public operations for getting and setting object attributes. Operation naming should stay at the class
        abstraction and provision of service level; getting and setting object attributes are low-level implementation
        details that weaken encapsulation if made public.
        Use adjectives (or past participles) for functions returning a Boolean (predicates). For predicates, it is often
        useful to add the prefix “is” or “has” before a noun to make the name read as a positive assertion. This is
        also useful when the simple name is already used for an object, type name, or an enumeration literal. Be
        accurate and consistent with respect to tense.




Confidential                                   AGH 343 Team, 2000                                                   17
DFTPS                                                                            Version:       1.1
C++ Programming Guidelines                                                       Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

5.11.1 Example
        void insert(...);
        void erase(...);

         Name first_name();
         bool has_first_name();
         bool is_found();
         bool is_available();
        Don't use negative names as this can result in expressions with double negations (e.g., !is_not_found);
        making the code more difficult to understand. In some cases, a negative predicate can also be made positive
        without changing its semantics by using an antonym, such as "is_invalid" instead of "is_not_valid".

5.11.2 Example
        bool is_not_valid(...);
        void find_client(name with_the_name, bool& not_found);
       Should be re-defined as:
          bool is_valid(...);
          void find_client(name with_the_name, bool& found);
5.12    Use function overloading when the same general meaning is intended
        When operations have the same intended purpose, use overloading rather than trying to find synonyms: this
        minimizes the number of concepts and variations of operations in the system, and thereby reduces its
        overall complexity.
        When overloading operators, ensure that the semantics of the operator are preserved; if the conventional
        meaning of an operator cannot be preserved, choose another name for the function rather than overload the
        operator.

5.13    Objects and Function Parameters - Augment names with grammatical elements to
        emphasize meaning
        To indicate uniqueness, or to show that this entity is the main focus of the action, prefix the object or
        parameter name with "the" or "this". To indicate a secondary, temporary, auxiliary object, prefix it with "a"
        or "current":

5.13.1 Example
        void change_name( subscriber& the_subscriber,
        const subscriber::name new_name)
        {
        ...
        the_subscriber.name = new_name;
        ...
        }
        void update(subscriber_list& the_list,
        const subscriber::identification with_id,
        structure& on_structure,
        const value for_value);
        void change( object& the_object,
        const object using_object);
5.14    Exceptions - Choose exception names with a negative meaning
        Since exceptions must be used only to handle error situations, use a noun or a noun phrase that clearly
        conveys a negative idea:



Confidential                                 AGH 343 Team, 2000                                                  18
DFTPS                                                                              Version:       1.1
C++ Programming Guidelines                                                         Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

          overflow, threshold_exceeded, bad_initial_value
5.15    Exceptions - Use project defined adjectives for exception names
        Use one of the words such as bad, incomplete, invalid, wrong, missing, or illegal from a project agreed list
        as part of the name rather than systematically using error or exception, which do not convey specific
        information.

5.16    Use capital letters for floating point exponent and hexadecimal digits.
        The letter 'E' in floating-point literals and the hexadecimal digits 'A' to 'F' should always be uppercase.

6.      Declarations
        This chapter provides guidance on the usage and form of various C++ declaration kinds.

6.1     Namespaces
        Prior to the existence of the namespace feature in the C++ language, there were only limited means to
        manage name scope; consequently, the global namespace became rather over-populated, leading to
        conflicts that prevented some libraries from being used together in the same program. The new namespace
        language feature solves the global namespace pollution problem.

6.2     Namespaces - Limit global declarations to just namespaces
        This means that only namespace names may be global; all other declarations should be within the scope of
        some namespace.
        Ignoring this rule may eventually lead to name collision.

6.3     Namespaces - Use a namespace to group non-class functionality
        For logical grouping of non-class functionality (such as a class category), or for functionality with much
        greater scope than a class, such as a library or a subsystem; use a namespace to logical unify the
        declarations (see "Use namespaces to partition potential global names by subsystems or by libraries").
        Express the logical grouping of functionality in the name.

6.3.1   Example
         namespace transport_layer_interface { /* ... */ };
         namespace math_definitions { /* ... */ };
6.4     Namespaces - Minimize the use of global and namespace scope data
        The use of global and namespace scope data is contrary to the encapsulation principle

6.5     Classes
        Classes are the fundamental design and implementation unit in C++. They should be used to capture
        domain and design abstractions, and as an encapsulation mechanism for implementing Abstract Data Types
        (ADT).

6.6     Classes - Use class rather than struct for implementing abstract data types
        Use the class class-key rather than struct for implementing a class-an abstract data type.
        Use the struct class-key for defining plain-old-data-structures (POD) as in C, especially when interfacing
        with C code.
        Although class and struct are equivalent and can be used interchangeably, class has the preferred default
        access control emphasis (private) for better encapsulation.


Confidential                                  AGH 343 Team, 2000                                                     19
DFTPS                                                                              Version:       1.1
C++ Programming Guidelines                                                         Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

6.6.1   Rationale
        Adopting a consistent practice for distinguishing between class and struct introduces a semantic distinction
        above and beyond the language rules: the class becomes the foremost construct for capturing abstractions
        and encapsulation; whilst the struct represents a pure data structure that can be exchanged in mixed
        programming language programs.

6.7     Classes - Declare class members in order of decreasing accessibility
        The access specifiers in a class declaration should appear in the order: public, protected, private.

6.7.1   Rationale
        The public, protected, private ordering of member declarations ensures that information of most interest to
        the class user is presented first, hence reducing the need for the class user to navigate through irrelevant, or
        implementation details.

6.8     Classes - Avoid declaring public or protected data members for abstract data types
        The use of public or protected data members reduces a class' encapsulation and affects a system's resilience
        to change: public data members expose a class' implementation to its users; protected data members expose
        a class' implementation to its derived classes. Any change to the class' public or protected data members
        will have consequences upon users and derived classes.

6.9     Classes - Use friends to preserve encapsulation
        This guideline appears counter-intuitive upon first encounter: friendship exposes one‟s private parts to
        friends, so how can it preserve encapsulation? In situations where classes are highly interdependent, and
        require internal knowledge of each other, it is better to grant friendship rather than exporting the internal
        details via the class interface.
        Exporting internal details as public members gives access to class clients, which is not desirable. Exporting
        protected members gives access to potential descendants, encouraging a hierarchical design which is also
        not desirable. Friendship grants selective private access without enforcing a subclassing constraint, thus
        preserving encapsulation from all but those requiring access.
        A good example of using friendship to preserve encapsulation is granting friendship to a friend test class.
        The friend test class, by seeing the class internals can implement the appropriate test code, but later on, the
        friend test class can be dropped from the delivered code. Thus, no encapsulation is lost nor is code added to
        the deliverable code.

6.10    Classes - Avoid providing function definitions in class declarations
        Class declarations should contain only function declarations and never function definitions
        (implementations).

6.10.1 Rationale
       Providing function definitions in a class declaration pollutes the class specification with implementation
       details; making the class interface less discernible and more difficult to read, increasing compilation
       dependencies as well.
        Function definitions in class declarations also reduce control over function inlining (see also "Use a
        No_Inline conditional compilation symbol to subvert inline compilation").

6.11    Classes - Always provide a default constructor for classes with explicitly-declared
        constructors
        To allow the use of a class in an array, or any of the STL containers; a class must provide a public default
        constructor, or allow the compiler to generate one.

Confidential                                  AGH 343 Team, 2000                                                       20
DFTPS                                                                              Version:       1.1
C++ Programming Guidelines                                                         Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

6.11.1 Notes
       An exception to the above rule exists when a class has a non-static data member of reference type, in this
       case it is often not possible to create a meaningful default constructor. It is questionable, therefore, to use a
       reference to an object data member.

6.12     Classes - Always declare copy constructors and assignment operators for classes with
         pointer type data members
         If needed, and not explicitly declared, the compiler will implicitly generate a copy constructor and an
         assignment operator for a class. The compiler defined copy constructor and assignment operator implement
         what is commonly referred to in Smalltalk terminology as "shallow-copy": explicitly, memberwise copy
         with bitwise copy for pointers. Use of the compiler generated copy constructor and default assignment
         operators is guaranteed to leak memory.

6.12.1 Example
         // Adapted from [Meyers, 92].
         void f()
         {
            String hello("Hello");// Assume String is implemented
                                           // with a pointer to a char
                                           // array.
            { // Enter new scope (block)
               String world("World");
                 world = hello;                  // Assignment loses world's
                                                 // original memory
            } // Destruct world upon exit from
                // block;
                // also indirectly hello
            String hello2 = hello; // Assign destructed hello to
                                              // hello2
         }
       In the above code, the memory holding the string "World" is lost after the assignment. Upon exiting the
       inner block, world is destroyed; thus, also losing the memory referenced by hello. The destructed hello
       is assigned to hello2.

6.12.2 Example
         // Adapted from [Meyers, 1992].
         void foo(String bar) {};
         void f()
         {
            String lost = "String that will be lost!";
            foo(lost);
         }
       In the above code, when foo is called with argument lost, lost will be copied into foo using the compiler
       defined copy constructor. Since lost is copied with a bitwise copy of the pointer to "String that
       will be lost!", upon exit from foo, the copy of lost will be destroyed (assuming the destructor is
       implemented correctly to free up memory) along with the memory holding "String that will be
       lost!"

6.13     Classes - Never re-declare constructor parameters to have a default value

6.13.1 Example
        // Example from [X3J16, 95; section 12.8]


Confidential                                   AGH 343 Team, 2000                                                   21
DFTPS                                                                              Version:       1.1
C++ Programming Guidelines                                                         Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

          class X {
            public:
            X(const X&, int);                 //   int parameter is not
                                              //   initialized
                                              //   No user-declared copy constructor, thus
                                              //   compiler implicitly declares one.
               };

          // Deferred initialization of the int parameter mutates
          // constructor into a copy constructor.
          //
          X::X(const X& x, int i = 0) { ... }
6.13.2 Rationale
       A compiler not seeing a "standard" copy constructor signature in a class declaration will implicitly declare
       a copy constructor. Deferred initialization of default parameters may however mutate a constructor into
       copy constructor: resulting in ambiguity when a copy constructor is used. Any use of a copy constructor is
       thus ill-formed because of the ambiguity [X3J16, 95; section 12.8].

6.14     Classes - Always declare destructors to be virtual
         Unless a class is explicitly designed to be non-derivable, its destructor should always be declared virtual.

6.14.1 Rationale
       Deletion of a derived class object via a pointer or reference to a base class type will result in undefined
       behavior unless the base class destructor has been declared virtual.

6.14.2 Example
        // Bad style used for brevity
        class B {
           public:
           B(size_t size) { tp = new T[size]; }
           ~B() { delete [] tp; tp = 0; }
           //...
           private:
           T* tp;
        };

          class D : public B {
             public:
             D(size_t size) : B(size) {}
             ~D() {}
             //...
          };

          void f()
          {
            B* bp = new D(10);
            delete bp;    // Undefined behavior due to
                         // non-virtual base class
                         // destructor
          }




Confidential                                  AGH 343 Team, 2000                                                    22
DFTPS                                                                             Version:       1.1
C++ Programming Guidelines                                                        Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

6.15    Classes - Avoid declaring too many conversion operators and single parameter
        constructors
        Single parameter constructors can also be prevented from being used for implicit conversion by declaring
        them with the explicit specifier.

6.16    Classes - Never redefine non-virtual functions
        Non-virtual functions implement invariant behavior and are not intended to be specialized by derived
        classes. Violating this guideline may produce unexpected behavior: the same object may exhibit different
        behavior at different times.
        Non-virtual functions are statically bound; thus, the function invoked upon an object is governed by the
        static type of the variable referencing the object-pointer-to-A and pointer-to-B respectively in the example
        below-and not the actual type of the object.

6.16.1 Example
        // Adapted from [Meyers, 92].
        class A {
           public:
           void f(); // Non-virtual: statically bound
        };

          class B : public A {
             public:
             void f(); // Non-virtual: statically bound
          };

          void g()
          {
            B x;
            A* pA = &x;        // Static type: pointer-to-A
            B* pB = &x;        // Static type: pointer-to-B
            pA->f(); //        Calls A::f
            pB->f(); //        Calls B::f
          }
6.17    Classes - Use non-virtual functions judiciously
        Since non-virtual functions constrain subclasses by restricting specialization and polymorphism, care
        should be taken to ensure that an operation is truly invariant for all subclasses before declaring it non-
        virtual.

6.18    Classes - Use constructor-initializers rather than assignments in constructors
        The initialization of an object's state during construction should be performed by a constructor initializer-a
        member initializer list-rather than with assignment operators within the constructor body.

6.18.1 Example
       Do this:
          class X
          {
             public:
             X();
             private
             Y the_y;
          };

Confidential                                  AGH 343 Team, 2000                                                    23
DFTPS                                                                              Version:       1.1
C++ Programming Guidelines                                                         Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc


         X::X() : the_y(some_y_expression) { }
         //
         // "the_y" initialized by a constructor-initializer
        Rather than this:
          X::X() { the_y = some_y_expression; }
          //
          // "the_y" initialized by an assignment operator.
6.18.2 Rationale
       Object construction involves the construction of all base classes and data members prior to the execution of
       the constructor body. Initialization of data members requires two operations (construction plus assignment)
       if performed in a constructor body as opposed to a single operation (construction with an initial value)
       when performed using a constructor-initializer.
        For large nested aggregate classes (classes containing classes containing classes...), the performance
        overheads of multiple operations-construction + member assignment-can be significant.

6.19    Classes - Never call member functions from a constructor initializer

6.19.1 Example
        class A
        {
           public:
           A(int an_int);
        };

          class B : public A
          {
             public:
             int f();
             B();
          };

          B::B() : A(f()) {}
          // undefined: calls member function but A has
          // not yet been initialized [X3J16, 95].
6.19.2 Rationale
       The result of an operation is undefined if a member function is called directly or indirectly from a
       constructor initializer before all the member initializers for base classes have completed [X3J16, 95].

6.20    Classes - Beware when calling member functions in constructors and destructors
        Care should be exercised when calling member functions in constructors; be aware that even if a virtual
        function is called, the one that is executed is the one defined in the constructor or destructor's class or one
        of its base's.

6.21    Classes - Use static const for integral class constants
        When defining integral (integer) class constants, use static const data members rather than #define's or
        global constants. If static const is not supported by the compiler, use enum's instead.

6.21.1 Example
       Do this:


Confidential                                  AGH 343 Team, 2000                                                    24
DFTPS                                                                           Version:       1.1
C++ Programming Guidelines                                                      Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

          class X {
             static const buffer_size = 100;
             char buffer[buffer_size];
          };

         static const buffer_size;
        Or this:
         class C {
            enum { buffer_size = 100 };
            char buffer[buffer_size];
         };
        But not this:
          #define BUFFER_SIZE 100
          class C {
             char buffer[BUFFER_SIZE];
          };
6.22    Functions – Always declare an explicit function return type
        This will prevent confusion when the compiler complains about a missing return type for functions
        declared without an explicit return type.

6.23    Functions – Always provide formal parameter names in function declarations
        Also use the same names in both function declarations and definitions; this minimizes surprises. Providing
        parameter names improves code documentation and readability.

6.24    Functions – Strive for functions with a single point of return
        Return statements sprinkled freely over a function body are akin to goto statements, making the code more
        difficult to read and to maintain.
        Multiple returns can be tolerated only in very small functions, when all returns can be seen simultaneously
        and when the code has a very regular structure:
         type_t foo()
         {
            if (this_condition)
               return this_value;
            else
               return some_other_value;
         }
        Functions with void return type should have no return statement.

6.25    Functions – Avoid creating function with global side-effects
        The creation of functions that produce global side-effects (change unadvertised data other than their
        internal object state: such as global and namespace data) should be minimized (see also "Minimize the use
        of global and namespace scope data"). But if unavoidable, then any side effects should be clearly
        documented as part of the function specification.
        Passing in the required objects as parameters makes code less context dependent, more robust, and easier to
        understand.

6.26    Functions – Declare function parameters in order of decreasing importance and volatility
        The order in which parameters are declared is important from the caller's point of view:



Confidential                                AGH 343 Team, 2000                                                 25
DFTPS                                                                              Version:       1.1
C++ Programming Guidelines                                                         Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

                  First define the non-defaulted parameters in order of decreasing importance;
                  Then define the parameters that have default values, with the most likely to be modified first.
                  This ordering permits taking advantage of defaults to reduce the number of arguments in function
                   calls.

6.27    Functions – Avoid declaring functions with a variable number of parameters
        Arguments for functions with a variable number of parameters cannot be type-checked.

6.28    Functions – Avoid re-declaring functions with default parameters
        Avoid adding defaults to functions in further re-declarations of the function: apart from forward
        declarations, a function should only be declared once. Otherwise this may cause confusion for readers who
        are not aware of subsequent declarations.

6.29    Functions – Maximize the use of const in function declarations
        Check whether functions have any constant behavior (return a constant value; accept constant arguments;
        or operate without side effect) and assert the behavior using the const specifier.

6.29.1 Example
        const T f(...); // Function returning a constant
                            // object.
        T f(T* const arg);     // Function taking a constant
                                   // pointer.
        // The pointed-to object can be
        // changed but not the pointer.
        T f(const T* arg);      // Function taking a pointer to, and
        T f(const T& arg);      // function taking a reference to a
                                // constant object. The pointer can
                                // change but not the pointed-to
                                // object.
        T f(const T* const arg); // Function taking a constant
                                  // pointer to a constant object.
                                  // Neither the pointer nor pointed-
                                  // to object may change.
        T f(...) const; // Function without side-effect:
                         // does not change its object state;
                         // so can be applied to constant
                         // objects.
6.30    Functions – Avoid passing objects by value
        Passing and returning objects by value may incur heavy constructor and destructor overhead. The
        constructor and destructor overhead can be avoided by passing and returning objects by reference.
        Const references can be used to specify that arguments passed by reference cannot be modified. Typical
        usage examples are copy constructors and assignment operators:
          C::C(const C& aC);
          C& C::operator=(const C& aC);
6.30.1 Example
       Consider the following:
          the_class the_class::return_by_value(the_class a_copy)
          {


Confidential                                   AGH 343 Team, 2000                                                   26
DFTPS                                                                             Version:       1.1
C++ Programming Guidelines                                                        Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

             return a_copy;
           }
           the_class an_object;
           return_by_value(an_object);
         When return_by_value is called with an_object as argument, the_class copy constructor is
         invoked to copy an_object to a_copy. the_class copy constructor is invoked again to copy
         a_copy to the function return temporary object. the_class destructor is invoked to destroy a_copy
         upon return from the function. Some time later the_class destructor will be invoked again to destroy the
         object returned by return_by_value. The overall cost of the above do-nothing function call is two
         constructors and two destructors.
         The situation is even worse if the_class was a derived class and contained member data of other
         classes; the constructors and destructors of base classes and contained classes would also be invoked, thus
         escalating the number of constructor and destructor calls incurred by the function call.

6.30.2 Notes
       The above guideline may appear to invite developers to always pass and return objects by reference,
       however care should be exercised not to return references to local objects or references when objects are
       required. Returning a reference to a local object is an invitation for disaster since upon function return, the
       returned reference is bound to a destroyed object!

6.31     Functions – Never return a reference to a local object
         Local objects are destroyed upon leaving function‟s scope; using destroyed objects invites disaster.

6.32     Functions – Never return a de-referenced pointer initialized by new
         Violation of this guideline will lead to memory leaks.

6.32.1 Example
        class C {
           public:
           ...
           friend C& operator+( const C& left,
           const C& right);
        };

          C& operator+(const C& left, const C& right)
          {
            C* new_c = new C(left..., right...);
            return *new_c;
          }

          C a, b, c, d;
          C sum;
          sum = a + b + c + d;
         Since the intermediate results of the operator+'s are not stored when computing sum, the intermediate
         objects cannot be deleted, leading to memory leaks.

6.33     Functions – Never return a non-const reference or pointer to member data
         Violation of this guideline violates data encapsulation and may lead to bad surprises.

6.34     Functions – Use inline functions in preference to #define for macro expansion
         But use inlining judiciously: only for very small functions; inlining large functions may cause code bloat.

Confidential                                  AGH 343 Team, 2000                                                   27
DFTPS                                                                            Version:       1.1
C++ Programming Guidelines                                                       Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

        Inline functions also increase the compilation dependencies between modules, as the implementation of the
        inline functions need to be made available for compilation of the client code.
        [Meyers, 1992] provides a detailed discussion of the following rather extreme example of bad macro usage:

6.34.1 Example
       Don't do this:
         #define MAX(a, b) ((a) > (b) ? (a) : (b))
        Rather, do this:
         inline int max(int a, int b) { return a > b ? a : b; }
        The macro MAX has a number of problems: it is not type-safe; and its behavior is non-deterministic:
          int a = 1, b = 0;
          MAX(a++, b);      // a is incremented twice
          MAX(a++, b+10); // a is incremented once
          MAX(a, "Hello"); // comparing ints and pointers
6.35    Functions – Use default parameters rather than function overloading
        Use default parameters rather than function overloading when a single algorithm can be exploited, and the
        algorithm can be parameterized by a small number of parameters.
        Using default parameters helps to reduce the number of overloaded functions, enhancing maintainability,
        and reduces the number of arguments required in function calls, improving code readability.

6.36    Functions – Use function overloading to express common semantics
        Use function overloading when multiple implementations are required for the same semantic operation, but
        with different argument types.
        Preserve conventional meaning when overloading operators. Don't forget to define related operators, e.g.,
        operator== and operator!=.

6.37    Functions – Avoid overloading functions taking pointers and integers
        Avoid overloading functions with a single pointer argument by functions with a single integer argument:
         void f(char* p);
         void f(int i);
        The following calls may cause surprises:
         f(NULL); f(0);
        Overload resolution resolves to f(int) and not f(char*).

6.38    Functions – Have operator= return a reference to *this
        C++ allows chaining of the assignment operators:
          String x, y, z;
          x = y = z = "A string";
        Since the assignment operator is right-associative, the string "A string" is assigned to z, z to y, and y to
        x. The operator= is effectively invoked once for each expression on the right side of the =, in a right to
        left order. This also means that the result of each operator= is an object, however a return choice of either
        the left hand or the right hand object is possible.
        Since good practice dictates that the signature of the assignment operator should always be of the form:
          C& C::operator=(const C&);



Confidential                                 AGH 343 Team, 2000                                                   28
DFTPS                                                                              Version:       1.1
C++ Programming Guidelines                                                         Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

        only the left-hand object is possible (rhs is const reference, lhs is non-const reference), thus *this should
        be returned. See [Meyers, 1992] for a detailed discussion.

6.39    Functions – Have operator= check for self-assignment
        There are two good reasons for performing the check: firstly, assignment of a derived class object involves
        calling the assignment operator of each base class up the inheritance hierarchy and skipping these
        operations may provide significant runtime savings. Secondly, assignment involves the destruction of the
        "lvalue" object prior to copying the "rvalue" object. In the case of a self-assignment, the rvalue object is
        destroyed before it is assigned, the result of the assignment is thus undefined.

6.40    Functions – Minimize complexity
        Do not write overly long functions, for example over 60 lines of code.
        Minimize the number of return statements, 1 is the ideal number.
        Strive for a Cyclomatic Complexity of less than 10 (sum of the decision statements + 1, for single exit
        statement functions).
        Strive for an Extended Cyclomatic Complexity of less than 15 (sum of the decision statements + logical
        operators + 1, for single exit statement functions).
        Minimize the mean maximum span of reference (distance in lines between the declaration of a local object
        and the first instance of its use).

6.41    Types - Define project-wide global system types
        In large projects there is usually a collection of types used frequently throughout the system; in this case it
        is sensible to collect together these types in one or more low-level global utility namespaces (see example
        for "Avoid the use of fundamental types").

6.42    Types - Avoid the use of fundamental types
        When a high degree of portability is the objective, or when control is needed over the memory space
        occupied by numeric objects, or when a specific range of values is required; then fundamental types should
        not be used. In these situations it is better to declare explicit type names with size constraints using the
        appropriate fundamental types.
        Make sure that fundamental types don't sneak back into the code through loop counters, array indices, and
        so on.

6.42.1 Example
        namespace        system_types {
          typedef        unsigned char byte;
          typedef        short int integer16; // 16-bit signed integer
          typedef        int integer32; // 32-bit signed integer
          typedef        unsigned short int natural16; // 16-bit unsigned integer
          typedef        unsigned int natural32; // 32-bit unsigned integer
          ...
        }
6.42.2 Rationale
       The representation of fundamental types is implementation dependent.

6.43    Types - Use typedef to create synonyms to strengthen local meaning
        Use typedef to create synonyms for existing names, to give more meaningful local names and improve
        legibility (there is no runtime penalty for doing so).

Confidential                                  AGH 343 Team, 2000                                                    29
DFTPS                                                                                 Version:       1.1
C++ Programming Guidelines                                                            Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

        typedef can also be used to provide shorthand for qualified names.

6.43.1 Example
         // vector declaration from standard library
         //
         namespace std {
            template <class T, class Alloc = allocator>
            class vector {
              public:
              typedef typename
              Alloc::types<T>reference reference;
              typedef typename
              Alloc::types<T>const_reference const_reference;
              typedef typename
              Alloc::types<T>pointer iterator;
              typedef typename
              Alloc::types<T>const_pointer const_iterator;
              ...
            }
         }
       When using names created by typedef, do not mix the use of the original name and the synonym in the
       same piece of code.

6.44    Constants and Objects – Avoid using the preprocessor #define directive for defining
        constants
        Use const or enum instead.
        Don't do this:
         #define LIGHT_SPEED 3E8
        Rather, do this:
         const int light_speed = 3E8;
        Or this for sizing arrays:
          enum { small_buffer_size = 100,
          large_buffer_size = 1000 };
6.44.1 Rationale
       Debugging is much harder because names introduced by #defines are replaced during compilation
       preprocessing, and do not appear in symbol tables.

6.45    Constants and Objects – Always initialize const objects at declaration
        const objects not declared extern have internal linkage, initializing these constant objects at declaration
        allows the initializers to be used at compilation time.

6.46    Constants and Objects – Never cast away the "constness" of a constant object
        Constant objects may exist in read-only memory.

6.47    Constants and Objects – Initialize objects at definition
        Specify initial values in object definitions, unless the object is self-initializing. If it is not possible to assign
        a meaningful initial value, then assign a "nil" value or consider declaring the object later.




Confidential                                    AGH 343 Team, 2000                                                       30
DFTPS                                                                               Version:       1.1
C++ Programming Guidelines                                                          Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

        For large objects, it is generally not advisable to construct the objects, and later initialize them using
        assignment as this can be very costly (see also "Use constructor initializers rather than assignments in
        constructors").
        If proper initialization of an object is not possible at the time of construction, then initialize the object using
        a conventional "nil" value that means "uninitialized". The nil value is to be used only for initialization to
        declare an "unusable but known value" that can be rejected in a controlled fashion by algorithms: to
        indicate an uninitialized variable error when the object is used before proper initialization.
        Note that it is not always possible to declare a nil value for all types, especially modulo types, such as an
        angle. In this case choose the least likely value.

7.      Expressions
7.1     Use redundant parentheses to make compound expressions clearer

7.2     Avoid nesting expressions too deeply
        The level of nesting of an expression is defined as the number of nested sets of parentheses required to
        evaluate an expression from left to right if the rules of operator precedence were ignored.
        Too many levels of nesting make expressions harder to comprehend.

7.3     Do not assume any particular expression evaluation order
        Unless evaluation order is specified by an operator (comma operator, ternary expression, and conjunctions
        and disjunctions); do not assume any particular evaluation order; assuming may lead to bad surprises and
        non-portability.
        For example, don't combine the use of a variable in the same statement as an increment or decrement of the
        variable.

7.3.1   Example
         foo(i, i++);
         array[i] = i--;
7.4     Use 0 for null pointers rather than NULL
        The use of 0 or NULL for null pointers is a highly controversial topic.
        Both C and C++ define any zero-valued constant expression to be interpretable as a null pointer. Because 0
        is difficult to read and the use of literals is highly discouraged, programmers have traditionally used the
        macro NULL as the null pointer. Unfortunately, there is no portable definition for NULL. Some ANSI C
        compilers have used (void *)0, but this turns out to be a poor choice for C++:
          char* cp = (void*)0; /* Legal C but not C++ */
        Thus any definition of NULL of the form (T*)0, rather than simply zero, requires a cast in C++.
        Historically, guidelines advocating the use of 0 for null pointers, attempted to alleviate the casting
        requirement and make code more portable. Many C++ developers however feel more comfortable using
        NULL rather than 0, and also argue that most compilers (more precisely, most header files) nowadays
        implement NULL as 0.
        This guideline rules in favor of 0, since 0 is guaranteed to work irrespective of the value of NULL, however,
        due to controversy, this point is demoted to the level of a tip, to be followed or ignored as seen fit.

7.5     Don't use old-style casting
        Use the new casting operators (dynamic_cast, static_cast, reinterpret_cast,
        const_cast) rather than old-style casting.


Confidential                                   AGH 343 Team, 2000                                                     31
DFTPS                                                                            Version:       1.1
C++ Programming Guidelines                                                       Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

        If you don't have the new cast operators; avoid casting altogether, especially downcasting (converting a
        base class object to a derived class object).
        Use the casting operators as follows:
                  dynamic_cast-to cast between members of the same class hierarchy (subtypes) using run-time
                   type information (run-time type information is available for classes with virtual functions).
                   Casting between such classes is guaranteed to be safe.
                  static_cast-to cast between members of the same class hierarchy without using run-time type
                   information; so is not guaranteed to be safe. If the programmer cannot guarantee type-safety, then
                   use dynamic_cast.
                  reinterpret_cast-to cast between unrelated pointer types and integral (integer) types; is
                   unsafe and should only be used between the types mentioned.
                  const_cast-to cast away the "constness" of a function argument specified as a const parameter.
                   Note const_cast is not intended to cast away the "constness" of an object truly defined as a const
                   object (it could be in read-only-memory).
        Don't use typeid to implement type-switching logic: let the casting operators perform the type checking
        and conversion atomically, see [Stroustrup, 1994] for an in-depth discussion.

7.5.1   Example
        Don't do the following:
          void foo (const base& b)
          {
            if (typeid(b) == typeid(derived1)) {
              do_derived1_stuff();
            else if (typeid(b) == typeid(derived2)) {
              do_derived2_stuff();
            else if () {
            }
          }
7.5.2   Rationale
        Old-style casting defeats the type system and can lead to hard-to-detect bugs that are not caught by the
        compiler: the memory management system can be corrupted, virtual function tables can get trampled on,
        and non-related objects can be damaged when the object is accessed as a derived class object. Note that the
        damage can be done even by a read access, as non-existent pointers or fields might be referenced.
        New-style casting operators make type conversion safer (in most cases) and more explicit.

7.6     Use the new bool type for Boolean expressions
        Don't use the old-style Boolean macros or constants: there is no standard Boolean value true; use the new
        bool type instead.

7.7     Never compare directly against the Boolean value true
        Since there was traditionally no standard value for true (1 or !0); comparisons of non-zero expressions to
        true could fail.
        Use Boolean expressions instead.

7.7.1   Example
        Avoid doing this:

Confidential                                    AGH 343 Team, 2000                                                32
DFTPS                                                                              Version:       1.1
C++ Programming Guidelines                                                         Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

         if (someNonZeroExpression == true)
         // May not evaluate to true
        Better to do this:
          if (someNonZeroExpression)
          // Always evaluates as a true condition.
7.8     Never compare pointers to objects not within the same array
        The results of such operations are nearly always meaningless.

7.9     Always assign a null pointer value to a deleted object pointer
        Avoid disaster by setting a pointer to a deleted object to null: repeated deletion of a non-null pointer is
        harmful, while repeated deletion of a null pointer is not.
        Always assign a null pointer value after deletion even before a function return, since new code may be
        added later.

8.      Statements
8.1     Use an if-statement when branching on Boolean expressions

8.2     Use a switch-statement when branching on discrete values
        Use a switch statement rather than a series of "else if" when the branching condition is a discrete value.

8.3     Always provide a default branch for switch-statements for catching errors
        A switch statement should always contain a default branch, which should be used for trapping errors.
        This policy ensures that when new switch values are introduced, and branches to handle the new values are
        omitted, the existing default branch will catch the error.

8.4     Use a for-statement or a while-statement when a pre-iteration test is required in a loop
        Use a for-statement in preference to a while statement when iteration and loop termination is based upon
        the loop counter.

8.5     Use a do-while-statement when a post-iteration test is required in a loop

8.6     Avoid the use of jump statements in loops
        Avoid exiting (using break, return or goto) from loops in other way than by the loop termination
        condition; and pre-maturely skipping to the next iteration with continue. This reduces the number of
        flow of control paths, making code easier to comprehend.

8.7     Don't use the goto-statement
        This seems to be a universal guideline.

8.8     Avoid the hiding of identifiers in nested scopes
        This may lead to confusion for the readers and potential risks in maintenance.

9.      Memory Management
9.1     Avoid mixing C and C++ memory operations
        The C library malloc, calloc and realloc functions should not be used for allocating object space: the C++
        operator new should be used for this purpose.


Confidential                                  AGH 343 Team, 2000                                                     33
DFTPS                                                                            Version:       1.1
C++ Programming Guidelines                                                       Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

        The only time memory should be allocated using the C functions is when memory is to be passed to a C
        library function for disposal.
        Don't use delete to free memory allocated by C functions, or free on objects created by new.

9.2     Always use delete[] when deleting array objects created by new
        Using delete on array objects without the empty brackets ("[]") notation will result in only the first array
        element being deleted, and thus memory leak.

10.     Error Handling and Exceptions
        Because not much experience has been gained using the C++ exception mechanism, the guidelines
        presented here may undergo significant future revision.
        The C++ draft standard defines two broad categories of errors: logic errors and runtime errors. Logic errors
        are preventable programming errors. Runtime errors are defined as those caused due to events beyond the
        scope of the program.
        The general rule for use of exceptions is that the system in normal condition and in the absence of overload
        or hardware failure should not raise any exceptions.

10.1    Use assertions liberally during development to detect errors
        Use function precondition and postcondition assertions during development to provide "drop-dead" error
        detection.
        Assertions provide a simple and useful provisional error detection mechanism until the final error handling
        code is implemented. Assertions have the added bonus of being able to be compiled away using the
        "NDEBUG" preprocessor symbol (see "Define the NDEBUG symbol with a specific value").
        The assert macro has traditionally been used for this purpose; however, reference [Stroustrup, 1994]
        provides a template alternative, see below.

10.1.1 Example
        template<class T, class Exception>
        inline void assert ( T a_boolean_expression,
                             Exception the_exception)
        {
          if (! NDEBUG)
          if (! a_boolean_expression)
          throw the_exception;
        }
10.2    Use exceptions only for truly exceptional conditions
        Do not use exceptions for frequent, anticipated events: exceptions cause disruptions in the normal flow of
        control of the code, making it more difficult to understand and maintain.
        Anticipated events should be handled in the normal flow of control of the code; use a function return value
        or "out" parameter status code as required.
        Exceptions should also not be used to implement control structures: this would be another form of "goto"
        statement.

10.3    Derive project exceptions from standard exceptions
        This ensures that all exceptions support a minimal set of common operations and can be handled by a small
        set of high level handlers.


Confidential                                 AGH 343 Team, 2000                                                  34
DFTPS                                                                             Version:       1.1
C++ Programming Guidelines                                                        Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

        Logic errors (domain error, invalid argument error, length error and out-of-range error) should be used to
        indicate application domain errors, invalid arguments passed to function calls, construction of objects
        beyond their permitted sizes, and argument values not within permitted ranges.
        Runtime errors (range error and overflow error) should be used to indicate arithmetic and configuration
        errors, corrupted data, or resource exhaustion errors only detectable at runtime.

10.4    Minimize the number of exceptions used by a given abstraction
        In large systems, having to handle a large number of exceptions at each level makes the code difficult to
        read and maintain. Exception processing may dwarf the normal processing.
        Ways to minimize the number of exceptions are:
        Share exceptions between abstractions by using a small number of exception categories.
        Throw specialized exceptions derived from the standard exceptions but handle more generalized
        exceptions.
        Add "exceptional" states to the objects, and provide primitives to check explicitly the validity of the
        objects.

10.5    Declare all exceptions thrown
        Functions originating exceptions (not just passing exceptions through) should declare all exceptions thrown
        in their exception specification: they should not silently generate exceptions without warning their clients.

10.6    Report exceptions at first occurrence
        During development, report exceptions by the appropriate logging mechanism as early as possible,
        including at the "throw-point".

10.7    Define exception handlers in most-derived, to most-base class order
        Exception handlers should be defined in the most-derived, to most-base class order in order to avoid coding
        unreachable handlers; see the how-not-to-it example, below. This also ensures that the most appropriate
        handler catches the exception since handlers are matched in a declaration order.

10.7.1 Example
       Don't do this:
          class base { ... };

          class derived : public base { ... };
          ...
          try {
            ...
            throw derived(...);
            //
            // Throw a derived class exception
          }catch (base& a_base_failure) {
            //
            // But base class handler "catches" because
            // it matches first!
            ...
          }catch (derived& a_derived_failure){
            //
            // This handler is unreachable!
            ...

Confidential                                 AGH 343 Team, 2000                                                    35
DFTPS                                                                               Version:       1.1
C++ Programming Guidelines                                                          Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

          }
10.8    Avoid catch-all exception handlers
        Avoid catch-all exception handlers, unless the exception is re-thrown.
        Catch-all handlers should only be used for local housekeeping, then the exception should be re-thrown to
        prevent masking of the fact that the exception cannot be handled at this level:
          try {
            ...
          }catch (...){
            if (io.is_open(local_file)){
              io.close(local_file);
            }
            throw;
          }
10.9    Make sure function status codes have an appropriate value
        When returning status code as a function parameter, always assign a value to the parameter as the first
        executable statement in the function body. Systematically make all statuses a success by default or a failure
        by default. Think of all possible exits from the function, including exception handlers.

10.10   Perform safety checks locally; do not expect your client to do so
        If a function might produce an erroneous output unless given proper input, install code in the function to
        detect and report invalid input in a controlled manner. Do not rely on a comment that tells the client to pass
        proper values. It is virtually guaranteed that sooner or later that comment will be ignored, resulting in hard-
        to-debug errors if the invalid parameters are not detected.

11.     Portability
        This chapter deals with language features that are a priori non-portable.

11.1    Never use hardcoded file pathnames
        Pathnames are not represented in a standard manner across operating systems. Using them will introduce
        platform dependencies.

11.1.1 Example
        #include "somePath/filename.hh" // Unix
        #include "somePath\filename.hh" // MSDOS
11.2    Do not assume the representation of a type
        The representation and alignment of types are highly machine architecture dependent. Assumptions made
        about representation and alignment may lead to bad surprises and reduced portability.
        In particular, never attempt to store a pointer in an int, a long or any other numeric type-this is highly non-
        portable.

11.3    Do not assume the alignment of a type

11.4    Do not depend on a particular underflow or overflow behavior

11.5    Use "stretchable" constants whenever possible
        Stretchable constants avoid problems with word-size variations.




Confidential                                  AGH 343 Team, 2000                                                   36
DFTPS                                                                              Version:       1.1
C++ Programming Guidelines                                                         Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

11.5.1 Example
        const int all_ones = ~0;
        const int last_3_bits = ~0x7;
11.6    Do not convert from a "shorter" type to a "longer" type
        Machine architectures may dictate the alignment of certain types. Converting from types with more relaxed
        alignment requirements to types with more stringent alignment requirements may lead to program failures.

12.     Reuse
12.1    Use standard library components whenever possible
        If the standard libraries are not available, then create classes based upon the standard library interfaces: this
        will facilitate future migration.

12.2    Use templates to reuse data independent behavior
        Use templates to reuse behavior, when behavior is not dependent upon a specific data type.

12.3    Use public inheritance to reuse class interfaces (subtyping)
        Use public inheritance to express the "isa" relationship and reuse base class interfaces, and optionally,
        their implementation.

12.4    Use containment rather than private inheritance to reuse class implementations
        Avoid private inheritance when reusing implementation or modeling "parts/whole" relationships. Reuse of
        implementation without redefinition is best achieved by containment rather than by private inheritance.
        Use private inheritance when redefinition of base class operations is needed.

12.5    Use multiple inheritance judiciously
        Multiple inheritance should be used judiciously as it brings much additional complexity. [Meyers, 1992]
        provides a detailed discussion on the complexities due to potential name ambiguities and repeated
        inheritance. Complexities arise from:
        Ambiguities, when the same names are used by multiple classes, any unqualified references to the names
        are inherently ambiguous. Ambiguity can be resolved by qualifying the member names with their class
        names. However, this has the unfortunate effect of defeating polymorphism and turning virtual functions
        into statically bound functions.
        Repeated inheritance (inheritance of a base class multiple times by a derived class via different paths in the
        inheritance hierarchy) of multiple sets of data members from the same base raises the problem of which of
        the multiple sets of data members should be used?
        Multiply inherited data members can be prevented by using virtual inheritance (inheritance of virtual base
        classes). Why not always use virtual inheritance then? Virtual inheritance has the negative effect of altering
        the underlying object representation and reducing access efficiency.
        Enacting a policy to require all inheritance to be virtual, at the same time imposing an all encompassing
        space and time penalty, would be too authoritarian.
        Multiple inheritance; therefore, requires class designers to be clairvoyant as to the future uses of their
        classes: in order to be able to make the decision to use virtual or non-virtual inheritance.




Confidential                                  AGH 343 Team, 2000                                                    37
DFTPS                                                                             Version:       1.1
C++ Programming Guidelines                                                        Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

13.     Compilation Issues
13.1    Minimize compilation dependencies
        Do not include in a module specification other header files that are only required by the module's
        implementation.
        Avoid including header files in a specification for the purpose of gaining visibility to other classes, when
        only pointer or reference visibility is required; use forward declarations instead.

13.1.1 Example
        // Module A specification, contained in file "A.hh"
        #include "B.hh" // Don't include when only required by
                         // the implementation.
        #include "C.hh" // Don't include when only required by
                         // reference; use a forward declaration instead.
        class C;
        class A
        {
           C* a_c_by_reference; // Has-a by reference.
        };
        // End of "A.hh"
13.1.2 Notes
       Minimizing compilation dependencies is the rationale for certain design idioms or patterns, variously
       named: Handle or Envelope [Meyers, 1992], or Bridge [Gamma] classes. By dividing the responsibility for
       a class abstraction across two associated classes, one providing the class interface, and the other the
       implementation; the dependencies between a class and its clients are minimized since any changes to the
       implementation (the implementation class) no longer cause recompilation of the clients.

13.1.3 Example
         // Module A specification, contained in file "A.hh"
         class A_implementation;
         class A
         {
            A_implementation* the_implementation;
         };
         // End of "A.hh"
       This approach also allows the interface class and implementation class to be specialized as two separate
       class hierarchies.

13.2    Define the NDEBUG symbol with a specific value
        The NDEBUG symbol was traditionally used to compile away assertion code implemented using the assert
        macro. The traditional usage paradigm was to define the symbol when it was desired to eliminate
        assertions; however, developers were often unaware of the presence of assertions, and therefore never
        defined the symbol.
        We advocate using the template version of the assert; in this case the NDEBUG symbol has to be given an
        explicit value: 0 if assertion code is desired; non-zero to eliminate. Any assertion code subsequently
        compiled without providing the NDEBUG symbol a specific value will generate compilation errors; thus,
        bringing the developer's attention to the existence of assertion code.

14.     Bibliography
         [Cargill, 92]        Cargill, Tom. 1992. C++ Programming Styles Addison-Wesley.

Confidential                                 AGH 343 Team, 2000                                                   38
DFTPS                                                                         Version:       1.1
C++ Programming Guidelines                                                    Date: 28/Jul/20113
411c7ae2-2941-4275-8ef4-74ef85a31de6.doc

         [Coplien, 92]      Coplien, James O. 1992. Advanced C++ Programming Styles and Idioms, Addison-
                            Wesley.
         [Ellemtel, 93]     Ellemtel Telecommunications Systems Laboratories. June 1993. Programming in
                            C++ Rules and Recommendations.
         [Ellis, 90]        Ellis, Margaret A. and Stroustrup, Bjarne.1990. The Annotated C++ Reference
                            Manual, Addison-Wesley.
         [Kruchten, 94]     Kruchten, P. May 1994. Ada Programming Guidelines for the Canadian Automated
                            Air Traffic System.
         [Lippman, 96]      Lippman, Stanley, B. 1996. Inside the C++ Object Model, Addison-Wesley.
         [Meyers, 92]       Meyers, Scott. 1992. Effective C++, Addison-Wesley.
         [Meyers, 96]       Meyers, Scott. 1996. More Effective C++, Addison-Wesley.
         [Plauger, 95]      Plauger, P.J. 1995. The Draft Standard C++ Library, Prentice Hall, Inc.
         [Plum, 91]         Plum, Thomas and Saks, Dan. 1991. C++ Programming Guidelines, Plum Hall Inc.
         [Stroustrup, 94]   Stroustrup, Bjarne. 1994. The Design and Evolution of C++, Addison-Wesley.
         [X3J16, 95]        X3J16/95-0087 | WG21/N0687. April 1995. Working Paper for Draft Proposed
                            International Standard for Information Systems-Programming Language C++.




Confidential                               AGH 343 Team, 2000                                            39

				
DOCUMENT INFO
Shared By:
Tags:
Stats:
views:3
posted:7/28/2011
language:
pages:45