Coding Style, Standards and Best Practices
CORRECT VERSION NOTICE
This document is subject to frequent detailed changes critical to correct engineering.
Any hard copy may be out of date. Readers are responsible to ensure they are reading the most current document. Confirm the correct version by checking the on-line version of the document, the on-line master list or the project authority.

Table of Contents
  1. Introduction
  2. General Rules
  3. Notes
  4. General Layout
    1. Source File Layout
    2. Header File Layout
    3. Namespace Layout
    4. Class Layout
  5. Formatting
    1. Indentations
    2. Braces
    3. Parenthesis
    4. Spaces
    5. Declarations
    6. Statements
      1. Compound statements
    7. Scope references and the 'this' operator
      1. Scope
      2. The 'this' operator
    8. Examples
  6. Hierarchical Types
  7. Functions
    1. Use of goto()
  8. Comments
    1. Function Comments
  9. Naming
    1. File Names
  10. Continuation Lines
  11. Variable Initialization
    1. Constructor Logic
  12. Macros, Enums and Constants
  13. Error handling and Exceptions
  14. Optimizations
  15. Appendix A: Other Considerations

1. Introduction
This document describes the coding guidelines for the various code files that are written by the Software Engineering Team at Zeriph. The goal of these guidelines is to increase portability, reduce maintenance, and above all improve clarity. Some of the content as well as the coding style presented here is a mixed derivative of styles presented in the References section below.

The secondary purpose of this document is to describe the layout of the code throughout the Omni library in the event one wishes to fix, add, or create a derivative work from the full source.
2. General Rules
  • Indentations shall be 4 spaces long. Do not replace spaces with tabs.
  • All source code should use Unix end-of-line convention (\n instead of \r\n or \r).
  • All source code should use lower-case names for files and directories where applicable, and shall replace white space with an underscore (e.g. some_file.cpp).
  • All source files will have 1 empty line at the end of the file.
  • There should be no revision control tags in source files. Use the CRM tools instead.
  • If there are source files that do not adhere to this document, follow that source file's coding style as best you can, refactor when permitted. A mixed coding style can be just as hard, or harder, to maintain than a bad coding style.
  • 3. Notes
    Any coding styles not mentioned in this file should be brought up to the team to ensure the style meets any specifications that might be required. NOTE: this only applies to those working internally on the library and does not need to apply to a general use case of this library.

    In general the code presented for any examples will try to be as technically correct as possible, however it is not the technicalities of the actual code, but the idea presented (syntax layout/etc.) that should be paid attention to throughout this style document.
    4. General Layout

    4a. Source File Layout
    Program/implementation (.c/.cpp/etc.) files should be organized in the following manner:
    • A comment with the copyright notice (unless it is 3rd party code that does not allow copyright). Be sure to include any other copyright notices that may be required (i.e. the GPL if using code that comes direct from GPL copyrighted code).
    • The omni/global.hpp include if necessary.
    • Library imported functionality (e.g. #include <omni/...>).
    • System imported functionality (e.g. #include <map>).
    • #define statements.
    • File local members and functions.
    • File local externally linked function or member declarations.
    • Class member variable or property instantiations and any forward declarations that may be needed.
    • Static or externally linked class member variables or function implementations.
    • Public class function implementations.
    • Protected class functions implementations.
    • Private class function implementations.
    Additionally:
    • If implementing code from a header file, follow the layout of the header file. For instance, if a class is defined with 4 functions, those 4 functions will be implemented in the same order in the program file.
    • Only function implementation should be in the program files itself, everything else (interface declarations, enum types, global/local variables, class or struct definitions, etc.) will be in the header file.
    • It is OK to put forward declarations in program files that might be needed within the file.
    • If any member functions are defined within a namespace only, they will be treated as a static member of a class and will be put in the global/static member area of the file.
    • Any implementation in a header file (such as the case with templates and inline functions) will adhere to the standards put forth in this document.

    See Naming for more information on how to name items within your source file.
    4b. Header File Layout
    Interface files (.h/.hpp/etc.) should be organized in the following manner:
    • A comment with the copyright notice (unless it is 3rd party code that does not allow copyright). Be sure to include any other copyright notices that may be required (i.e. the GPL if using code that comes direct from GPL copyrighted code).
    • A brief comment describing the purpose of the file if it is for something other than class or function definition. The comment should not contain revision information or revision control tags.
    • The single inclusion define (e.g. #if !defined(OMNI_HEADER_HPP)) that prevents accidental double inclusion.
    • Local #include's, if needed (e.g. #include <omni/...>).
    • System #include's, if needed (e.g. #include <map>).
    • Macro's and other #define constants.
    • Opening brace of C++ guard (#ifdef __cplusplus), if needed.
    • Global variable declarations (using extern where needed).
    • Global function declarations (using extern where needed).
    • Namespaces, classes and data structures.
    • Closing brace of C++ guard if opening brace exists.
    • The complimenting #endif for preventing accidental double inclusion.
    Notes on header file implementation:
    • Try and keep any actual implementation details (code) out of the header file, unless it cannot be avoided (as with templated types).
    • Try and keep the definitions in alphabetical order unless it cannot be avoided; for example, a static templated function named foo might use a static templated function named more_foo, so more_foo must be defined and declared before foo.
    • Alphabetical ordering can be ignored if using a type within a declaration that uses another type below it and a forward declaration is not possible or would convolute the code (as can be the case with templated types).
    • There may be instances where it is unavoidable to put implementation (code) in the header file (for instance, when making use of templates); this is OK, but try and keep as much code as possible in the actual implementation file (the .c or .cpp file) for cleaner separation of implementation, declaration and problem domains.
    See Naming for more information on how to name items within your header file.
    4c. Namespace Layout
    Namespaces within the library should be organized in the following manner:
    • typedef declarations that do not require forward declarations.
    • Constants.
    • Child namespaces.
      • Inner classes/namespaces (ad infinitum).
      • Inner functions (ad infinitum).
    • Classes and structures, to include enum class types.
    • Namespace functions.
    • typedef declarations that utilizes classes within the namespace.
    Note that these standards do not strictly need to be adhered to in the case that types or functions need to be used within a later declared/defined type or function. In these instances, typedef declartions, functions and classes can be placed where they need to be for the code to build, but should be grouped in the code as closely to these standards as possible.

    See Naming for more information on how to name items within a namespace.
    4d. Class Layout
    Classes and structures within the library should be organized in the following manner:
    • typedef declarations
    • Rule of 3/5 constructors.
    • Additional class constructors.
    • The class destructor.
    • Public members.
    • Public functions.
    • Public operators.
    • The OMNI_MEMBERS_FW macro if applicable (should be in most classes).
    • The OMNI_OSTREAM_FW macro if applicable (should be in most classes).
    • Public friend operators.
    • Public static members.
    • Public static functions.
    • Protected members.
    • Protected functions.
    • Protected operators.
    • Protected friend operators.
    • Protected static members.
    • Protected static functions.
    • Private members.
    • Private functions.
    • Private operators.
    • Private friend operators.
    • Private static members.
    • Private static functions.
    Note: this does not apply to the enum class types throughout Omni as they have a template that is followed for ease of reading the class code.

    All functions should try and be arranged in alphabetical order when feasible.

    Any member properties/variables should also be arranged alphabetical unless doing so will cause additional padding. In this case, the member variables should be ordered in such a way as to reduce the padding as much as possible, and initialized in the constructor member initialization list appropriately.

    Note that these standards do not strictly need to be adhered to in the case that types or functions need to be used within a later declared/defined type or function. In these instances, typedef declartions, functions and classes can be placed where they need to be for the code to build, but should be grouped in the code as closely to these standards as possible.


    See Naming for more information on how to name items within the class.
    5. Formatting

    5a. Indentations
    Indentions will be 4 spaces each and follow a tree structure. In other words, following a definition of some type, if there is code that supports the definition (for instance as in an if statement, there is code that follows the if) this code shall be 1 indent and any subsequent areas shall be indent count +1.
    Example:
    if (...) { // 0 indent
        // 1 indent
        if (x == y) {
            // 2 indents
        } else {
            if (z > 0) {
                // 3 indents
            }
        }
    }
    
    Other notations on indentions in regards to placement of braces or function names, etc. are noted in the following sub paragraphs.
    5b. Braces
    Though there is no technical reason for a different placement and position of braces, it is preferred (for readability) that the opening brace be put on the same line at the end, and the closing brace as the first and only (in most cases) character on a line.

    Example:
    if (...) {
         // do x,y,z
    }
    // single lines are acceptable if doing small computations:
    if (...) { /* do x */ }
    
    This guideline does not apply to functions. Functions shall have the opening brace at the beginning of a new line immediately following the function definition.

    Example:
    int function_name(int x)
    {
        // function body
        return value;
    }
    
    Other notations on brace placement can be found in the below sections.
    5c. Parenthesis
    It is generally a good idea to use parenthesis in expressions involving mixed operators.
    If there is an area of code that will have multiple parameters in between the parenthesis, you should treat the parenthesis as they were braces of an if statement in regards to placement.

    Example:
    // mixed operators
    int i = (((a * b) / (c * d)) * 10) + 1;
    
    // expansive parenthesis
    object* obj = new object(
        param1, // comment on param1
        param2, // other comment
        param3,
        param4,
        param5,
        param6
    );
    
    5d. Spaces
    The use of spaces depends (mostly) on function-versus-keyword usage. Use a space after (most) keywords. The notable exceptions are sizeof, typeof, exit and any other reserved keywords that can look/act somewhat like a function.

    Example:
    if (x == 1) { // space following control statement 'if'
        if (y > sizeof(object)) { // no space after 'sizeof'
            exit(1); // no space after 'exit'
        }
    }
    

    There should be 1 space immediately following the last closing code (the last parenthesis before the opening brace in the above if statements for instance).

    There will be no spacing immediately following an opening parenthesis or immediately before a closing parenthesis.

    Example:
    s = sizeof( object ); // WRONG
    s = sizeof(object); // RIGHT
    

    If declaring a pointer type, the pointer operator (the asterisks *) will be immediately before the data type.

    Example:
    char * value; // WRONG
    char *value; // WRONG
    char* value; // RIGHT
    

    Use 1 space on either side of a most binary/ternary operators (<, =, +, -, *, etc.).

    Example:
    a = (((1+1)*2)/4); // WRONG
    a = (((1 + 1) * 2) / 4); // RIGHT
    a+=5; // WRONG
    a += 5; // RIGHT
    

    There will be no space after unary operators (&, *, !, ~, sizeof, etc.).

    Example:
    char* a = & b; // WRONG
    char* a = &b; // RIGHT
    int x = ~ (y + 1); // WRONG
    int x = ~(y + 1); // RIGHT
    

    No space before or after the increment and decrement unary operators, ++, --.

    Example:
    x ++; // WRONG
    x++; // RIGHT
    

    No space before or after the scope resolution operators (., ->, ::).

    Example:
    x . get_ref() -> base_class :: some_func(); // WRONG
    x.get_ref()->base_class::some_func(); // RIGHT
    

    Any initialization lists will be separated by a space before and after the colon (:) and after each initializer.

    Example:
    class object {
        public:
            object(const char* name, int count) : m_name(name), m_count(count) {}
        private:
            const char* m_name;
            int m_count;
    };
    

    An initialization list can be on a single line, or multiple lines if there are multiple entries. Be sure to line up the entries accordingly.

    Example:
    object(const char* name, int count) :
        m_name(name),
        m_count(count) {} // Braces can be here if nothing is to be done
            // or here on next line
    
    Lastly, do not leave trailing whitespace at the ends of lines.
    5e. Declarations
    One declaration per line is recommended since it encourages commenting, though it is acceptable to put all declarations on a single line (especially when multiple are needed).

    Example:
    int level = 0; // comment on level
    int size = 100; // comment on size
    int level = 0, size = 100, tmp, x, y, z; // OK, just be sure to comment
    

    When declaring multiple variables of the same base type, do not mix the variable types (reference, pointer, etc.) on the same line.

    Example:
    int x = 0, y[SIZE] = { 0 }; // WRONG
    int x = 0; // RIGHT
    int y[SIZE] = { 0 }; // RIGHT
    

    Another alternative is to line up a group of declarations (with spaces, not tabs), while acceptable, it is preferred to use the above method mentioned.

    Example:
    int   level        = 0; 
    int   size         = 0;
    float current_data = 0.0;
    

    Function level declarations will be only at the beginning of blocks of code (a block is anything surrounded by braces { and }). This can be ignored if initializing an object or type that will consume resources and only needs to be declared if something is to happen.

    Example:
    void set_state(int x, int y)
    {
        // initialize variables here
        std::vector group;
        object tmp();
        int counter = 0;
        if (x == 0 && y == 0) {
            // alert or something else
        } else {
            // instantiate memory/CPU intensive object/resource
            resource* resrc = new resource();
            // do something with resource
            delete resrc;
        }
    }
    

    Avoid local declarations that hide higher level declarations at a function level, in other words do not declare the same variable name in an inner block, while syntactically correct, it can lead to confusion in code.

    Example:
    void set_state(int x, int y)
    {
        int counter = 0;
        // other code that uses the 'counter' variable
        for (int counter = 0; counter < 10; ++counter) {
            // this could lead to errors in this section of code
            // remedy is to rename this 'inner' variable to something else
        }
    }
    

    Function variables should be initialized, when feasible, when they are declared.

    Example:
    std::list group; // no need to 'initialize' to anything
    int x = 0; // initialize x to zero
    object* obj = NULL; // pointer types can be NULL if allowable by the type
    object* obj = new object(); // initializing tmp to a new instance
    
    5f. Statements
    In general, each line should contain at most one statement. If putting multiple statements on a single line, try and group them such that they preform similar functionality. The break statement is OK to put on the same line as another statement as long as it is easy to read.

    Example:
    ++i; ++x; // OK
    ++i; break; // OK
    some_func(); break; // OK
    do_something_with_var(x); ++x; ++i; // NOT OK
    

    Do not nest the ternary conditional operator (... ? ... : ...).

    Example:
    int num = ((count < x) ? ((x > 10) ? x : 0) : 1); // WRONG
    int num = ((count < x) ? 10 : x); // RIGHT
    

    Functions that do not return a value (void) should not have a return statement as the last statement in the function.

    Example:
    void set_state(int x)
    {
        // do something
        return; // WRONG
    }
    

    If returning a value, the return statement should not enclose its return value in parenthesis, unless it is a compound return statement.

    Example:
    return (val); // WRONG
    return val; // RIGHT
    return (val + 10 - some_func()); // OK
    
    5f1. Compound statements
    Compound statements are statements that contain lists of statements enclosed in braces. The enclosed list should be indented one more level than the compound statement itself. The opening left brace should be at the end of the line beginning the compound statement and the closing right brace should be alone on a line, positioned under the beginning of the compound statement (see examples below). Note that the left brace that begins a function body is the only occurrence of a left brace which should be alone on a line.

    Braces are also used around a single statement when it is part of a control structure, such as an if-else or for statement.

    Example:
    if (condition) {
        if (other_condition) {
            statement;
        }
    }
    

    All statements and control blocks that allow for optional braces are to include braces. This is for clarity of code.

    Example:
    /* The following is valid, but could potentially be confusing 
    to read depending on context of the code */
    if (condition)
        do_something();
    
    // This is preferred
    if (condition) {
        do_something();
    }
    

    Any empty statements can have the opening and closing braces immediately following it.

    Example:
    for (;;) { }
    
    5g. Scope references and the 'this' operator

    5g1. Scope
    When referencing a variable or function, the scope of that variable must be included as well. This is to ensure the code is immediately readable and understandable by anyone not familiar with the structure of the code.

    Take the following code as an example:
    void some_function(int newidx)
    {
        // ... other code
        do_something_with_updated_index(newidx);
    }
    

    When looking at the above code, it is not immediately clear as to where the function do_something_with_updated_index resides, in other words, the scope of the function is unclear and could potentially lead to confusion by other developers. It can also lead to naming conflicts.

    Take the following code as an example:
    class base_class
    {
        public:
            static bool some_function(double val)
            {
                return (val > 0.0d);
            }
    };
    
    class some_class : base_class
    { 
        public:
            bool some_function(long val)
            {
                return (val > 100L);
            }
    
            void do_something()
            {
                // some other code
                some_function(get_input());
                // which one are we intending to call?
            }
    };
    

    To this affect all variables and functions will have at least 1 level of scope applied for reference. To illustrate this point, take the above code as an example applied with the appropriate scope:
    class base_class
    {
        public:
            static bool some_function(double val)
            {
                return (val > 0.0d);
            }
    };
    
    class some_class : base_class
    { 
        public:
            bool some_function(long val)
            {
                return (val > 100L);
            }
    
            void do_something()
            {
                // some other code
                this->some_function(get_input());
                base_class::some_function(static_cast(get_input()));
            }
    };
    

    By explicitly defining the scope of the variable or function, you not only make the code more readable, but also remove any possibilities of inadvertently calling the wrong function or having the compiler assume which function you meant to call; while it might lead to more verbosity throughout the library's internal code, it removes ambiguity which leads to cleaner, less buggy code.

    Higher levels of scope may be applied where more clarity is needed, but typically 1 level of scope is sufficient. As an example, the following C# code illustrates how adding higher levels of scope can add clarity; this example uses part of the .NET Framework in C# to illustrate this point due to the lack of examples across the library and C++ standards:
    Thread.Sleep(1000);
    Thread.AddUserRequest(someObj);
    
    For the first two functions above, their first level up is the Thread class, however it is not immediately clear if Thread is the same class for each function, or if it is the same name within two separate namespaces. For this you can use 1 more level up in scope, or you can elect to use the full scope, for example:
    System.Threading.Thread.Sleep(1000);
    CustomFramework.Utils.Threading.Thread.AddUserRequest(someObj);
    

    If the single level of scope is the current class, then the this operator will be used for clarity.
    5g2. The 'this' operator
    The this operator is a special operator that returns a reference to the current instance of the object you are operating in (the class you are in). If no this operator is explicitly applied to the variable or function, one is implicitly applied by the compiler (scope permitting).

    The this operator has 0 impact on performance since it is simply referring to a scope, not an operation.

    For clarity the this operator will be put on all member variables and functions for immediate understanding of what scope the variable or method is a part of.

    If the variable or function you are referencing within a child class is from its parent class, it is still preferred to reference it via the this operator. It is ok, in certain instances, to reference parent members and functions via the parent class; examples would be specific language rules requiring the use of the parent keyword or in an event when using the parent class would add clarity to code.
    5h. Examples
    if, if-else, if-else-if statements:
    if (condition) {
        statements;
    }
    
    if (condition) {
        statements;
    } else {
        statements;
    }
    
    if (condition) {
        statements;
    } else if (condition) {
        statements;
    }
    

    Note that the right brace before the else and the right brace before the while of a do-while statement (see below) are the only places where a right brace appears that is not alone on a line.

    for statements:
    for (initialization; condition; update) {
        statements;
    }
    

    When using the comma operator in the initialization or update clauses of a for statement, it is suggested that no more than three variables should be updated. More than this tends to make the expression too complex or confusing. In this case it is generally better to use separate statements outside the for loop (for the initialization clause), or at the end of the loop (for the update clause).

    The infinite loop is written using a while or for loop. While infinite loop's are useful tools it's best to be sure there is some rudimentary mechanism to allow the loop to exit (on user input for instance, or the program shutting down).

    while (!exit_condition) {
        statements;
        if (exit_condition) { break; }
        statements;
    }
    
    for ( ;!exit_condition; ) {
        statements;
        if (exit_condition) { break; }
        statements;
    }
    

    while statements:
    while (condition) {
        statements;
    }
    

    do-while statements:
    do {
        statements;
    } while (condition);
    

    switch statements:
    switch (condition) {
        case ABC:
        case DEF:
            statements;
            break;
        case XYZ:
            statements;
            break;
        default:
            statements;
            break;
    }
    

    The last break is, strictly speaking, redundant, but it is recommended form nonetheless because it prevents a fall-through error if another case is added later after the last one. In general, the fall-through feature of the switch statement should rarely, if ever, be used (except for multiple case labels as shown in the example). If it is, it should be commented for future maintenance.

    All switch statements should include a default case. Don't assume that the list of cases covers all possible cases.

    New, unanticipated, cases may be added later, or bugs elsewhere in the program may cause variables to take on unexpected values.

    Notes on switch statements:
    • The case statement should be indented one level more than the switch statement.
    • The case statement should be on a line separate from the statements within the case.
    • The break statement should be indented to the same level as the code within the case.
    The following shows the format that should be used for a switch whenever the blocks of statements contain more than a couple of lines.

    Example:
    switch (condition) {
        case ABC:
        case DEF:
            statement1;
            .
            .
            statement2;
            break;
        case XYZ:
            statement1;
            .
            .
            statement2;
            break;
        default:
            statements;
            break;
    }
    
    6. Hierarchical Types
    Hierarchical types (class, struct, enum, etc.) shall be laid out in such a manner that best suits the design needs of that specific type. This document will not cover design specifications, only general guidelines to certain idioms.

    Inner types should be specific in nature to its parent type.

    Avoid too many inner types or too deep a tree of inner types. The tree depth excludes namespaces.

    Generic types should still be contained within a namespace, static/abstract class, or some other container mechanism. This is for clarity of code and to split out any generic code that could potentially be reused by other code.

    See Naming for more information on how to name hierarchical types.
    7. Functions
    Treat reserved keywords that can act like functions as such (except for the exit and return statements).

    Example:
    sizeof x // Valid, but not preferred method
    sizeof(x) // Preferred
    

    In program files, functions should be separated by one blank line.

    Example:
    int get_value()
    {
        return value;
    }
    
    void set_value(int val)
    {
        value = val;
    }
    

    When declaring function prototypes with parameters (which should be in the header file), include the parameter name as well as its type.

    Example:
    int get_value(); // no parameters
    void set_value(int new_value); // parameter is 'new_value' and is of type 'int'
    

    It is not required to give a parameter name in C/C++ when defining the method; however, it does give more clarity as to what the parameter should potentially do. This is especially helpful when only looking at the header file, or when using an IDE that has some sort of 'IntelliSense' engine.

    That being said; functions should be simple, as short as possible, do one thing and do it well. It is OK to have longer line count functions if it cannot be avoided, for instance, a conceptually simple function that consists of a really long, but simple, case statement is OK to be long, however, an overly complex function that is only 3 lines (for example) might need to be rewritten for clarities sake (unless there is a reason for the complexity, like speed or efficiency for instance).

    Try to limit the number of local variables used within a function to in between 5 and 10. Anything more could add to the complexity of the code and thus be more error prone or not as easy to understand; this also helps your stack be generally smaller and thus more efficient.

    An idea that is to be followed with functions:

    If the function looks like it might not be easily understood by a high school freshman with a good beginners programming book, chances are you might not understand either in a couple of weeks.

    To this affect, in any function you write, be sure it can be understood; either through the syntax and semantics of the code itself or through commenting, preferably through commenting.
    7a. Use of goto()
    The goto statement should be used extremely sparingly. While the goto statement is a completely valid tool, it can be error prone if not used correctly (similar to a case statement without a break). Any code that uses a goto can be restructured to not use the goto, typically with the addition of an if or some other control flow statement.

    Since the goto statement is syntactically equivalent to a jmp statement in assembler, a goto can be valuable in instances where memory and CPU cycles are in a very limited supply. Even in these cases, boolean values in conjunction with proper if statements could avoid the goto with no added overhead.

    Again, while the goto statement is a valid and potentially useful tool, it is best to avoid them and use well-structured code instead.
    8. Comments
    Commenting is vitally important to quickly and easily understanding a programs control structure and flow. It is because of this that commenting can be good and bad.

    To avoid excess commenting, it is typically best to leave comments out of the function bodies themselves. It is OK to comment areas of a function to warn about something in the code or to better explain why something is the way it is (as per the paragraph below). It is easier, however, to read the code if the function itself is commented (see the section below).

    Typically you do not want to try and explain how your code works, more so, what the code is supposed to do and potentially why you did something the way you did (if it's not overtly clear by the code).

    Commenting should be limited in scope to the particular area you are commenting on, it should not include author names, bug tracking information, or other items that do not directly reflect or refer to that specific area of code. In other words, a comment should reflect and clarify the code it is being written for.

    Both C89 /*...*/ and C99 //... style commenting is acceptable.

    There are three styles of comments: block (multi-line), single-line, and trailing.

    For single-line comments, the C89 or C99 style is OK to use. Both styles are also OK for a trailing comment, if the trailing comment fits on the same line. However, it should be noted that the C99 style is preferred.

    For block (multi-line) comments, if the comment is more than 2 lines, do no use the C99 style commenting, only use C89 style. This is true for trailing comments as well.

    C89 style block comments should be in the form of having the opening /* followed by a new line, then either a single space followed by *, or 4 spaces, followed by the comment text, and the closing */ should be on a new line and should align with the previous lines * if asterisks are used, otherwise, it should be the only text on the line. Note: the "single space" is meant to indicate the indentation, so if the comment is double tabbed over (e.g. 8 spaces), the "single space" would be the 9th space.

    Example:
    123 <- column
    /*
     * Here is a block comment.
     * The comment text should be tabbed or spaced over uniformly.
     * The opening slash-star and closing star-slash are alone on a line.
     * The closing star-slash is aligned with the previous lines star.
     */
    
    Or the following:
    1234 <- column
    /*
        Here is a block comment.
        The comment text should be tabbed or spaced over uniformly.
        The opening slash-star and closing star-slash are alone on a line.
        The closing star-slash is not tabbed or spaced over.
    */
    

    One-line comments alone on a line should be indented to that of the code that follows.

    Example:
    if (argc < 3) {
        // Argument count is not the number we need, show the usage
        show_usage();
        exit(1);
    }
    

    Trailing comments should be short. They can be tabbed uniformly or left at your discretion. They must be at least 1 space from the code. They can also be either C89 or C99 style commenting.

    Example:
    if (a == EXCEPTION) {
        b = true;          /* C89 comment */
    } else {
        b = isprime(a);    // C99 comment
    }
    
    As mentioned above in the General Rules, if a file does not adhere to these guidelines, stick as closely as possible to that file's way of commenting. Also, whatever style you choose for commenting, stick with it (don't mix and match).

    Example:
    if (a == 10) {
        b = true; // Don't start with C99 style single line comments
    } else {
        b = (a < 5 && a > 2); /* then switch to C89 style in other areas */
    }
    

    This does not preclude the use of other commenting types, it is OK to mix and match commenting types within a file.

    Example:
    if (a == 10) {
        b = true; // Single line C99 style comment
        /*
         * Mixed with longer C89 block style comments
         * is completely OK, just don't overly use
         * mix-and-match style.
         */
    }
    
    As stated above however, if single line comments are C99 style and multi line comments are C89 style, do not then switch to a C99 multi line comment and C89 single line comment. This is to avoid the jarring effect that mixed style commenting can have visually while reading the code.

    It is also important to comment your data (variables) when it is not obvious as to what they do or in the event you are declaring a global variable or defining a member variable within a class.

    Lastly, always remember, you never know who will be looking at the comments, so try to avoid any asinine comments or comments that are insulting or degrading.
    8a. Function Comments
    Since various tools have the capability to parse JavaDoc/Doxygen[4] style comments, as well, the custom Omni documentation tool expects the function comments to be in this style, all functions will utilize the JavaDoc style of commenting to document the function.

    Each public facing function that can be utilized by user code will use the following template for commenting:
    /**
     * @brief          Brief description.
     * 
     * @details        A more detailed description of the function. This should describe what the function
     *                 is supposed to do in more detail; it may not always be a longer description as some
     *                 functions can be explained easy enough in the @brief. 
     * 
     * @return         [optional] A return value if any.
     * 
     * @param [name]   [optional] Each function parameter should be marked with this; this is the description
     *                            of the input value, its range, and any other info. The [name] is the variable
     *                            name in the function.
     *
     * @tparam [name]  [optional] Each template parameter should be marked with this; this is the description of
     *                            the template parameter and what it represents in the function. The [name] is
     *                            the template parameter name in the template declaration.
     * 
     * @exception      [optional] Any errors (or error conditions) specific to the context of the function. This
     *                            should explain what error conditions can occur (e.g. division by 0, invalid
     *                            range, etc.). This should also detail what Omni specific exceptions can be
     *                            thrown in the code.
     * 
     * @warning        [optional] Any extra considerations to be aware of. This should explain any pre/post
     *                            conditions to be aware of, especially if the conditions should cause
     *                            undefined behavior. 
     * 
     * @attention      [optional] Any platform specific notes. This should explain any notes to be aware of
     *                            if there are differences between certain architectures or platforms, like
     *                            the differences that can arise between Windows and Linux, or for systems
     *                            that might be big-endian versus little-endian.
     * 
     * @note           [optional] Any notes to be aware of (like system calls, order of operations, etc.).
     *                            This section can be used for any addendum's specific to the function that
     *                            other sections might not adequately cover or be appropriate in that section.
     * 
     * @invariant      [optional] This is the complexity of this function (e.g. O(1) for X conditions, etc.).
     */
    
    9. Naming
    Naming of variables and functions can sometimes lead to easier to understand code than any amount of documentation might provide. As such, naming is more important to code use than documentation is.

    Names should be descriptive and as short as possible. Local variables (those within function bodies only) can be even shorter and less descriptive if they are extremely limited in use. For instance, in a for loop, one could simply declare for (int i = 0; i < 10; ++i) versus some long name convention for (int loop_counter; loop_counter < 10; ++loop_counter). In contrast, public variables should be as descriptive and short as possible. For instance int current_thread_count is well more descriptive than something like int tcount.

    The following is the naming convention to be used with this document:
    • Do not use Hungarian notation (types can change)
    • Names with leading and/or trailing underscores are reserved for system purposes and should not be used for any user-created names (does not apply to private members).
    • #define constants and variable constants should be in all CAPS.
    • Enum constants are CAPITALIZED.
    • Functions, typedef values, and variable names, as well as struct, union, and enum type names should be lower case with an underscore for any spaces, example: get_current_pid, or set_user_name
    • Many macro functions are in all CAPS. Some macros (such as getchar and putchar) are in lower case since they may also exist as functions. Lower-case macro names are only acceptable if the macros behave like a function call, that is, they evaluate their parameters exactly once and do not assign values to named parameters. Sometimes it is impossible to write a macro that behaves like a function even though the arguments are evaluated exactly once.
    • Avoid names that differ only slightly, like foobar and foo_bar. The potential for confusion is considerable.
    • Avoid names that can look like each other. On many terminals and printers, 'l' (lowercase ell), '1' (number one) and 'I' (capital eye) look quite similar. A variable named 'l' is particularly bad because it looks so much like the constant '1' (due to font variations).
    Global names, in general, should have a common prefix identifying the module (file) that they belong with (unless they are within a class or namespace of some type). Globals may alternatively be grouped in a global structure.

    Private member variables (not functions) should start with m_ then the variable name to identify it is a member level variable.

    Private member functions should start with _ then the function name to identify it is a private member level function.

    File level variables or functions (those only declared within a single program file) will follow the naming convention based upon what level of access that variable or function is supposed to have.

    Hierarchical types (class, struct, enum, etc.) should be named according to what they will contain within them and to some extent what they will do. The naming of the type shall follow the standards as listed above regardless of scope.

    Avoid names that might conflict with various standard library names (like calling a class cout or variable stdin).
    9a. File Names
    File names should be all lowercase with valid characters being 0-9, a-z, and the underscore "_".

    The following extensions will be used for the specific types:
    .hpp - header file for C++
    .cpp - C++ based source file
    .hxx - C++ based source file internal to the library

    The file names should reflect the functionality defined/implemented in that file.

    Files with logical connections (e.g. pairs of header and source files) should reflect that connection in their names wherever possible.

    Files belonging to the same module should reflect that dependency by a short unique prefix to the filename, followed by an underscore. The following illustrates this convention:
    Makefile            io_base.cpp
    server.c            io_base.h
    server_ioplug.h     io_file.h
    server_ioplug.c     io_file.cpp
    io_cwrapper.h       io_stream.h
    io_cwrapper.c       io_stream.cpp
    
    10. Continuation Lines
    Sometimes an expression will not easily fit on a single line, such occurrences are especially likely when blocks are nested deeply or long identifiers are used. If this happens, the expression should be broken after the last comma in the case of a function call (never in the middle of a parameter expression), or after the last operator that fits on the line. The next line should be further indented by 2 spaces or lined up with the first parameter on the above line. If they are needed, subsequent continuation lines should be broken in the same manner, and aligned with each other. The next line of a continuation line should only ever start with a parameter name, not any type of operator or punctuation (except for a closing punctuation like a closing parenthesis or brace).

    Example:
    if (long_logical_test_1 || long_logical_test_2 ||
        long_logical_test_3) {
        statements;
    }
    
    // long_identifier_term3 is 5 spaces out instead of 2 to showcase
    // alignment of parameters with each other
    a = (long_identifier_term1 - long_identifier_term2) *
         long_identifier_term3;
    
    // showcase putting closing parenthesis on single line
    function(
        long_complicated_expression1,
        long_complicated_expression2,
        long_complicated_expression3,
        long_complicated_expression4,
        long_complicated_expression5,
        long_complicated_expression6
    );
    
    11. Variable Initialization
    Variables should be initialized when defined, unless initialization will cause additional resource usage when unnecessary.

    Example:
    int x = 0;
    some_struct s; 
    if (someval > 0) {
        // Causes resources to be consumed and is only needed if someval > 0
        object o;
    }
    

    Or consider the following:
    object* s = null; // declare but nullify
    for (int i = 0; i < some_var; ++i) {
        // object s(i); // WRONG: (named resource in tight loop)
        s = new object(i); // OK: named vars take resources vs. memory allocation
        s->some_function();
        s->some_other_function();
        delete s;
    }
    

    Multiple assignments are OK as well in the case when doing so does not sacrifice readability.
    Example:
    int x, y, z, i, a, b, c; // temporary variables
    x = y = z = i = a = b = c = 0; // OK
    
    int rad, max, min, a, b, c;
    rad = max = min = a = b = c = 0; // Valid but unclear about rad, max and min are
    

    In the last portion, it would be better to have declared rad, max, and min on separate lines with comments explaining what each is.

    The variables that are being multiply assigned should all be of the same type (or all pointers being initialized to NULL).

    Do not use multiple assignments for complex expressions.

    Example:
    foo_bar.fb_name.firstchar = bar_foo.fb_name.lastchar = 'c'; // Not easy to read!
    
    11a. Constructor Logic
    Class members should be instantiated in the member initialization list when feasible and in the order they are declared in your class to clarify this is the order in which they will be initialized by the compiler (actual order does not matter but this increases clarity in the event a member is a class/struct type and thus might have more initialization code that needs to be considered).
    12. Macros, Enums and Constants
    Names of macros defining constants are to be all CAPS, example: #define CONSTANT 0x12345

    CAPITALIZED macro names are appreciated but macros resembling functions may be named in lower case. Generally, inline functions are preferable to macros resembling functions.

    Things to avoid when using macros:
    1. Macros that affect control flow, example:
      #define FOO(x)       \
          if (blah(x) < 0) \
          return -EBUGGERED;
      
    2. Macros that depend on having a local variable with a magic name, example: #define FOO(val) bar(index, val)
    3. Macros with arguments that are used as l-values, example: FOO(x) = y;
    4. Forgetting about precedence: macros defining constants using expressions must enclose the expression in parentheses. Beware of similar issues with macros using parameters, example:
      #define CONSTANT 0x4000
      #define CONSTEXP (CONSTANT | 3)
      
    5. Forgetting about macro expansion, example:
      #define FOO(x) std::cout << "X = " <<
      
      FOO(10); // ERROR
      
      In this FOO(10); would expand out to std::cout << "X = " << ;.
    13. Error handling and Exceptions
    Error handling and catching exceptions can be extremely useful when debugging applications to ensure stable code. As such, they can have a tendency to be used as a way of control flow versus their original intent (to alert of a critical error within the application and possibly recover from it). Error handling should not be used as a way of controlling program flow, but instead a way of being notified when an area of code has done something it should not have and as a way of cleaning up after oneself.
    14. Optimizations
    There are many optimizations that a compiler can, and should, make in ones code. It is not advisable, however, to trust and expect the compiler to do what you want the code to do when you can explicitly tell the compiler what to do via your code.

    As an example, most of the time the compiler should optimize the i++ post increment operation to be a pre increment operation (i.e. ++i) which is more efficient. However, this is a simple optimization that can be done by the human coder to explicitly define how the code should operate without relying on the optimizations of the compiler; this ensures that no matter the compiler, the same level of optimization is done across all platforms and architectures.

    Additionally, it should be the goal to have code that is as CPU and memory efficient as possible; this means ensuring that the code is as optimized to the C++ standard as possible, thus ensuring that any compiler will adhere to the micro optimizations in the code.

    Ways to achieve these standardized optimizations include, but are not limited to, preferring the switch statement to a large nested if..else branch where feasible, using the pre-increment operator where applicable, specifying inline for single line and simple functions, and using idioms like return value optimization (a.k.a. copy elision).
    15. Appendix A: Other Considerations
    Please note that while there are many ideas and concepts covered in this outline, not everything can be taken into account. This document is meant as a general guideline and not as a must. If you're making a 10 line program to do a very simple task, following these guidelines may be more time consuming than is needed to understand the simple program. Use your best judgment when creating new code.

    Try and avoid 'non-standard' standards. For example, as of 1995, C++ adds #define values of and, or, not and a couple of others (as defined in the header ciso646) that are literal translations (definitions) of their respective values. In other words in the ciso646 header file you would have the following:
    #define and &&
    #define or ||
    #define not ! 
    #define bit_and &
    #define bit_or |
    
    Along with some other definitions for some comparison and bitwise operators. So one could technically do the following:
    int x = 10;
    int y = 10;
    bool z = false;
    if (x == 10 and y == 10) {
        if (not z) {
            // do something
        } else {
            // do something else
        }
    }
    

    Notice the and in between the x and y comparison statements? This is the same as saying x == 10 && y == 20. So even though the macro definitions of these value or actually considered a 'standard' within the C++ standards, it might confuse those less privy to such values. We say this because the actual and operator itself (the double ampersand &&) is pretty common among many languages, making it easier to fully understand that this is an actual comparison operator, while the name and itself might be a reserved keyword to function as the same as the && operator, or could it could potentially function as a bitwise and operator (&). The implementation of the actual and keyword or name is language dependent. Hence why we say 'non-standard' standard, because though it is an actual 'standard' of the language itself, it's not as widely used as other nomenclature, as such it's potential for ambiguity is much higher. To this effect we advise against these constructs unless absolutely necessary.

    References
    1. The C Programming Language, Second Edition
    2. The C++ Programming Language, Third Edition
    3. Linux kernel coding style
    4. Microsoft Coding Techniques
    5. Microsoft Design Guidelines for Developing Class Libraries
    6. Code conventions for the Java Programming Language (Oracle)
    7. Doxygen