Projects
Contact
Patrick Fox
Torrance, CA     90503
fox@patrickfox.org

Design & Coding Standards

Contents

1 - Introduction

This document formalizes and attempts to clarify the design, development, and coding standards and guidelines which I use on my own projects and which I would generally promote on other projects I lead.

The narratives provided herein are from the perspective of my own, personal experiences and research. I certainly would not presume to tell you how you should be running your own projects.

The underlying motivation for many of the standards is to try to make the code easier for team members to figure out - particularly, new team members, or team members who have not worked with a given piece/area of code before.

  1. 1.1 - Team Morale

    The standards should not adversely affect the morale of the team members. To that end, it is crucial that every individual standard include an explanation of how it relates to the goal of improving the product quality and reliability. Matters of personal preference or "style" should not be part of a team's coding standard. Doing so will surely stifle creativity and decrease team morale. Another crucial characteristic of a good coding standard is that it must always be open for review, debate, and reconsideration.

  2. 1.2 - The Need for Flexibility and Review

    A set of design and coding standards should not aim to be an absolute and inflexible list of rules which must absolutely be adhered to. There will always be the edge cases where it would make more sense to do something in a way which would conflict with the standards. When those cases arise, the engineer should do what would be best for the project, under the circumstances, and if that results in a design or in code which does not comply with the standards the engineer should document the fact, and the reason for it, in the code or design.

  3. 1.3 - Democratic "Standards" or Totalitarian "Rules"

    Some readers may feel an overwhelming compulsion to point out that some languages, like Java, enforce some of the standards and practices listed herein. And they may be inclined to use that as evidence that I should implicitly consider Java a better language. I believe that is a misguided presumption. Standards can be democratically decided by the team, based on what they believe is best for the team and the project(s). Standards are often based on the collective experiences of the team members. Policies "enforced" by a language are dictatorial - fascist, if you will. Some might even say arrogant on the part of the language designers to presume to know what is best for all projects. One needs only to look at how many "features" Java has had over the years which were later dropped or significantly modified because it was eventually realized they hindered good developers more than they "protected" bad developers.

  4. 1.4 - Applicability of the Standards

    These standards should be applied to all new design and development efforts and, when practical, to refactoring efforts of pre-existing code. Because maintaining the stability of the existing code is a significant concern for all parties, care must be taken to ensure that any such refactoring or improvement of that code does not introduce new bugs.

  5. 1.5 - Experience, Objectivity, and Independent Thought

    Much of these standards and guidelines have been developed over years of working on different projects, coming into contact with many different coding styles and practices. Those who know me know I am not the type to blindly follow what I read in books and publications, or whatever the latest trend happens to be. Instead, I usually take such information under advisement; consider it objectively; review and/or try to think of the possible flaws and criticisms of it; then decide for myself what I think the best thing to do will be. Each of the standards listed below are based on my own, personal, first hand experiences and what I believe has been reasonable, objective consideration.

    These standards do not aim to endorse or follow any commonly accepted industry standards or practices. Often, something which has become an industry standard has done so only out of default or only because our industry, like most industries, has a large percentage of people who would prefer to follow others' leads than to decide for themselves what the best thing to do is.

2 - Objectives

The objectives of these standards and guidelines are as follows:

  • improve the quality, maintainability, and consistency of the source code;
  • increase ongoing developer productivity;
  • increase developer confidence in the code, so they don't need to repeatedly verify their assumptions about it;
  • minimize the effort required by new team members to get familiar and productive with the existing code;
  • minimize the amount of effort that will be required to implement future changes which are likely to be inevitable.

To meet these objectives, every standard and requirement must be justified and include an explanation of the benefit it provides to the project(s) and/or the team.

One thing I wish to avoid is enforcing a list of coding standards which becomes oppressive and interferes with developer productivity. I believe that the use of excessive and pedantic standards does exactly that. Therefore, unless a given standard can be shown to provide an identifiable benefit to the team/project, it must be considered only a guideline, or not included in the standards at all.

When a given standard is found to not be beneficial, it should be mentioned in this document so that future team members can know that it has been considered and why it has been specifically excluded. Otherwise, the team may end up re-debating whether to include the same non-productive, already rejected standards every time there is a new generation of members.

The coding and design standards should be considered an ongoing process, subject to review and modification at any time. As the products and team progress we may find that certain standards no longer provide their original benefit and should be removed or adapted. Team members should be encouraged to voice their opinions on any of the standards in this document, especially those they disagree with – because being forced to comply with coding standards which don't seem to make sense and which adversely affect team productivity decreases developer morale, which leads to inferior quality of work. It is important to keep in mind, though, that the ongoing and future interests of the projects need to be weighed against the immediate interests of developer morale and productivity.

3 - Document Conventions

Within this document, the following conventions will be used.

  1. 3.1 - Rationale Field

    Each item MUST include a "Rationale" field, which explains the justification and/or reason for the standard/guideline. Almost every individual coding standard will have an immediate adverse affect on individual developer productivity – in exchange for long term positive effects on the projects and the team as a whole. So, a standard which cannot be satisfactorily justified should not be part of this document.

  2. 3.2 - Status Field

    Each item will include a "Status" field, which may be one of "Standard", "Guideline", "Rejected", or "Repealed".

    1. Standard

      "Standard" shall mean the given item has been accepted by the team and must be adhered to for code to be considered complete and correct.

    2. Guideline

      "Guideline" shall mean the given item has been accepted by the team and should (though it doesn't have to) be adhered to. Whether to adhere to a given guideline shall be at the individual developer's discretion. It is believed the item will generally improve the code quality, but if it will decrease individual developer morale and/or productivity it is not worth enforcing.

    3. Rejected

      "Rejected" shall mean the given standard or guideline was considered at some point but it was decided not to implement it. The reason for the decision should be provided in the rationale.

    4. Repealed

      "Repealed" shall mean the given standard/guideline had been in use at some point but has since been abandoned. The reason for discontinuing the standard/guideline should be included in the rationale.

    If the status is followed by a date that date indicates when the item was added to the document and became a standard, was rejected, et cetera. Items without a date should be interpreted as meaning they were part of the initial standard.

    Some may argue the inclusion of rejected and repealed standards just clutters the document and doesn't provide a real benefit. I disagree (at least with the part about not providing a benefit). Their inclusion helps to prevent wasted effort in the future when new team members propose standards which have already be considered and rejected.

  3. 3.3 - Comments Field

    Each item may optionally include a "Comments" field, which may provide additional comments regarding the standard.

  4. 3.4 - Todo Field

    Each item may contain one or more "Todo" fields, which list outstanding issues, relating to the item, which should be addressed.

4 - Refactoring Pre-Existing Code

This section discusses the procedures which should be used when refactoring pre-existing code to bring it into compliance with these standards.

Over time, it is expected that all pre-existing code, that is, code which was implemented before the adoption of these standards, will eventually be refactored to comply with these standards. When refactoring, we have to be very careful not to introduce new bugs.

  1. 4.1 - General

    1. 4.1.1 - When to Refactor

      Generally, when a developer needs to make a change within a pre-existing function or class, he should perform the refactoring of that function/class at that time, unless it would be so time consuming it would prevent him from completing his assigned task on time, or the pre-existing code within the function is so complex or unstable that the chance of introducing a new bug would be too great – in which case a Doxygen @todo comment should be added to function explaining such.

      1. Rationale

        This incremental and systematic approach ensures, over time, the entire pre-existing code base will be brought into compliance with the standards and thereby, generally improved. The alternative of attempting to refactor a large section of code at one time often results in widespread changes that end up requiring a large allocation of dedicated manpower – which can be risky. The other alternative of leaving pre-existing code as it is and only applying the standards to new code results in a patchwork code base which is sometimes difficult to comprehend and certainly difficult to maintain. It also results in clean, stable code being built on top of potentially questionable, unstable code.

      2. Status

        Standard

    2. 4.1.2 - Refactoring Public Interfaces

      Refactoring pre-existing interfaces (i.e. public members, class names, namespace containment) should be performed very cautiously. All consumer code should be recompiled and a search for any of the modified symbols should also be manually performed.

      1. Rationale

        Generally, the compiler will catch most of the errors for us, but we must practice diligence. This is particularly important in the case of common, shared interfaces in libraries, where the code is utilized by other modules. However, we should not rely only on the compiler because it will not locate occurrences of the changed symbols within, for example, comments or conditionally compiled code.

      2. Status

        Standard

5 - Design

This section lists the standards which should be followed during design tasks and when working on design documents. In this context, "design" shall refer to all work related to design, including generating design documentation, modeling (at all levels), and matters relating to the external interfaces of the systems and components (excluding the actual implementation of those interfaces).

  1. 5.1 - General

    1. 5.1.1 - Intended Audience

      All design documentation should be written with the intention of being useful to other developers who may not be familiar with the code, the design, the architecture, et cetera, as is relevant to the particular documentation.

      1. Rationale

        Documentation is most often used for the purposes of aiding others in understanding how something works and, in some cases (like code level documentation) WHY it was implemented the way it was. You cannot assume that just because something is obvious to you as you design it, it will be obvious to others when they review the documentation.

      2. Status

        Guideline

    2. 5.1.2 - Singleton Release Methods

      Singleton classes should not provide a Release() (or similar) type of function which can be used to delete the instance and release the resources it holds.

      1. Rationale

        A singleton object is expected by the code external to it to remain in existence and if one piece of code gets a pointer to that singleton, then another piece of code causes that singleton to be deleted then the first piece is now holding onto an invalid pointer. Even if the first piece of code calls the GetInstance() member of the singleton after it has been deleted by the second piece of code, the pointer it will get will refer to a new instance so it's state will be inconsistent.

      2. Status

        Standard

    3. 5.1.3 - UTF-8 Encoding for External Data

      All character data which is external to the application (whether written to a file or to the network) should be UTF-8 encoded.

      1. Rationale

        UTF-8 has become the standard for sending character data over a network and for storing character data to disk. It ensures that all Unicode code points (essentially, characters) are properly handled; eliminates byte ordering issues; and supports all languages universally.

      2. Status

        Standard

    4. 5.1.4 - Abstracting Platform Dependencies

      When platform specific features, concepts, or mechanisms (e.g. Windows registry, Windows services/Unix daemons, multithreading libraries) are used, they should be encapsulated within classes which provide platform independent interfaces.

      1. Rationale

        Porting existing code to support multiple platforms, or from one platform to another, is significantly easier when platform specific code is contained and isolated from program logic through platform independent interfaces. Otherwise, significant changes need to be made throughout the entire application.

      2. Status

        Standard

    5. 5.1.5 - Design Documentation File Formats

      All design documentation should be saved in a format which can be accessed universally by all project stakeholders, and can be easily searched. Authors should be able to use whichever tool they prefer (whether MS Word, OpenOffice, text editor, et cetera) while working on the file, but once it is published (made available to others) it should be in a standard, consistent format.

      1. Rationale

        A person interested in reading a given document should not have to install additional software to do so – especially if it would require proprietary, licensed software. The ideal solution is for all published documentation to be in HTML format, accessible on the company's intranet.

      2. Status

        Standard

    6. 5.1.6 - Design Documentation Version Control

      All design documentation should be kept in the version control system along with the project source code.

      1. Rationale

        Over time, the design documentation should be expected to change. Just as with source code, these changes should be tracked. Keeping the documentation in the version control system greatly simplifies this process.

      2. Comments

        For better version control integration, a file format which the version control system understands should be preferred (e.g. if using Subversion, then OpenOffice should be preferred over MS Word because SVN can interpret OpenOffice files).

        If the documentation is maintained in a system such as Confluence, MediaWiki, or Trac, then it may not be necessary to also keep it in version control because those systems also provide versioning features/histories.

      3. Status

        Standard

    7. 5.1.7 - Extensibility of Designs

      Designs should attempt to anticipate future changes in requirements as much as reasonable, and should be able to minimize the impact of such changes, should they occur.

      1. Rationale

        Designing components to only meet the immediate requirements (as advocated by Agile) results in designs and code which is very difficult to adapt to future changes, and which, over time, results in a hacky, patchwork code base which becomes increasingly difficult to maintain.

      2. Status

        Standard

  2. 5.2 - Modeling

    1. 5.2.1 - UML for Design Documentation

      UML 2.0 should be used for all design modeling, whether you are modeling the design before implementing the code, or documenting existing code.

      1. Rationale

        The exclusive use of UML will decrease the effort required to read and understand models created by others. UML has been accepted as the standard modeling and diagram language for the entire industry, since the late 1990's. It can be assumed that all software developers and engineers should have some degree of knowledge of UML and won't have to try to figure out what a given icon, line, arrow, or cardinality adornment mean. And even if they aren't that familiar with it they can easily pull up a reference on the Internet.

      2. Status

        Standard

    2. 5.2.2 - Visual Paradigm for UML Modeling

      All UML models and diagrams should be implemented using Visual Paradigm.

      1. Rationale

        Most UML modeling software still uses a proprietary file format, though many support exporting to a standard format, like XMI. If different developers are using different modeling tools it is difficult or impossible for them to quickly share the information. Visual Paradigm, in particular, was chosen because it is the most powerful and flexible UML modeling software I have encountered which is reasonably priced. It also has excellent support for, and integration with SVN.

      2. Comments

        Some teams may choose to use a product other than Visual Paradigm. That should be fine, provided the entire team is able to easily share and work on each other's models.

      3. Status

        Standard

    3. 5.2.3 - Use of Other UML Modeling Software

      Developers may use a UML modeling tool other than the one officially adopted by the team (see above), however it should be able to save and read files in such as way that they are interchangeable with the adopted tool.

      1. Rationale

        If a developer is more familiar with, and/or more comfortable with, a given UML tool then it would decrease their productivity and be distracting to have to use a tool they may find awkward to work with. When saving their files, however, we want to make sure that all other team members will be able to access those files and continue work on them, if needed.

      2. Comments

        Team members should never use a modeling tool which only allows them to save the file in it's own, proprietary format. Doing so will make it much more difficult for teammates to work with the file.

      3. Status

        Standard

6 - Development/Coding

This section lists the standards which should be followed when implementing and maintaining code of the organization's products. Two of the overall themes throughout these standards are:

  • simplicity: simple code is easier to understand, maintain and debug. We should avoid using tricky, obscure, "intellectually impressive" code, which does not provide a real benefit over a much simpler alternative; and
  • readability: the person that initially writes a piece of code will likely spend less time looking at that code, over it's lifetime, than other people will. Therefore, when writing code and comments, write them as though you're writing them for other people to work with – because you are!

By following a collection of standards which, overall, make the code as simple to understand as possible, we decrease the amount of effort required to become familiar with the code and we make the code easier to maintain. The less effort developers have to allocate to remembering unnecessary details, like which implementation file a given member function is declared in, the more they can concentrate on actually designing and implementing good, clean, stable code. Some of the standards listed herein do not provide a significant benefit on their own, but when combined with the other standards they should provide a significant, overall benefit.

Some of the standards in this section may seem to be mitigated by the capabilities of modern IDEs. Some may argue that they're not necessary because in the IDE you can simply click a symbol then press Ctrl+B (assuming you use Netbeans, otherwise whatever keystroke works for your IDE) and it will jump you to the definition/declaration of the symbol. I disagree with that way of thinking. I believe a developer should view their IDE as a tool to help them be more productive - not as a justification for being sloppy or lazy. The other issue I have with being too dependent on an IDE is that the IDE is not always available when you need to look at some code.

Another significant issue with developers who rely too much on the visual representations provided by an IDE is that it shows you what you want to see, very quickly, without bothering you with all the other surrounding code. On that one hand that's great - on the other hand, it means a lot of old, legacy, and bad code gets left as-is because nobody ever "stumbles across it". You may be surprised how many bugs, dead, lingering code, and how much bad code is "stumbled aacross accidentally when a developer was looking into an unrelated issue.

  1. 6.1 - General

    1. 6.1.1 - Approved Third Party Libraries

      Do not use any third party libraries or source code (including open source), in your projects, unless they/it has been approved by the company.

      1. Rationale

        There may be licensing restrictions or requirements which could cause complications for the company down the road. Particularly in the case of open source libraries licensed under the GPL. All third party libraries should be approved by the company before they are integrated into the code.

      2. Status

        Standard

    2. 6.1.2 - Use of typedefs

      Typedefs should be avoided, unless they really improve the readability of the code.

      1. Rationale

        Excessive use of typedefs makes it difficult to figure out and understand the code because the developer has to keep tracking down what the underlying type is. As an example, every C/C++ developer will immediately know what unsigned long nFoobar; means, however given SomeType nFoobar; will require tracking down the definition of SomeType.

      2. Comments

        Some might argue that having to lookup the actual type of a typedef'ed variable isn't that big of a deal, since the IDE can jump you straight to the definition with a couple of keystrokes. I disagree. An occasional typedef here and there is not too much of a problem, but when you're working with a code base that has, literally, hundreds of them it does get much more distracting.

      3. Status

        Guideline

    3. 6.1.3 - Smart Pointers

      Smart pointers and similar memory management techniques should be avoided unless there is some real justification for using them.

      1. Rationale

        While smart pointers may potentially decrease the chances of memory leaks (if implemented correctly), they make the rest of the code more difficult to understand and maintain (in part, because they are contrary to the C++ way of thinking about things). The best way to avoid bugs and memory leaks is to write clean, stable, well documented code.

      2. Status

        Standard

    4. 6.1.4 - Platform Specific APIs

      Whenever possible we should avoid using platform specific APIs and functionality, if there is a comparable, platform independent way of accomplishing the same result.

      1. Rationale

        The use of platform specific APIs makes the code much more difficult to port to other systems. Even if there are currently no plans to support other platforms, that may change in the future.

      2. Comments

        This point is concerned with the use of specific APIs (e.g. using GlobalAlloc() on Windows), not with utilizing features that may be specific to a given platform.

      3. Status

        Standard

    5. 6.1.5 - Dynamic Stack Allocation

      Dynamically allocating space from the stack should be avoided.

      1. Rationale

        Creating variable length arrays and calling alloca() will allocate the space on the stack rather than the heap. This can lead to stack overflows, running out of stack space, or other forms of stack corruption which can be very difficult to debug – especially in a multithreaded process.

      2. Status

        Standard

    6. 6.1.6 - Use of Friends

      The use of friend classes and functions should be avoided, if possible.

      1. Rationale

        The use of friend classes and functions is contrary to good encapsulation. Usually, when the need to make something a friend of another arises it's the result of poor design and trying to fix a problem quickly. If the problem can be solved without the use of making a class or function a friend of another then it should be done so.

      2. Status

        Standard

    7. 6.1.7 - File IO

      C++ iostreams should be used for file (esp. logging) IO, instead of fprintf() and C buffered/unbuffered IO.

      1. Rationale

        Using a consistent, standard approach to issues like file IO will make the code easier to maintain; make it easier to identify reusable pieces of code; and, make it easier to implement internationalization. With respect to internationalization, the C++ locale and facet classes provide a much more powerful interface than the standard C run-time library.

      2. Status

        Standard

    8. 6.1.8 - Pre/Post Increment/Decrement Operators

      The pre-increment/decrement operators should be used instead of the post-increment/decrement operators, whenever possible.

      1. Rationale

        The post-increment/decrement operator returns a copy of the resulting value which may increase overhead when used on objects. If the caller does not require the result then it is more efficient to call the pre-increment/decrement operator.

      2. Status

        Standard

    9. 6.1.9 - Precise Width Integer Types

      Use precise width integer types when possible. Do not use char to represent an 8bit int – use int8_t instead. Only use the char type if the actual data is actually a character (and only if it's guaranteed to be an ASCII character – otherwise, use a wchar_t).

      1. Rationale

        Improves code clarity. The original authors intention is better stated (e.g. Did he really want an 8bit integer? Or was he making assumptions that we'd always be using the ASCII table values? If he wanted an 8bit integer is it because the value is guaranteed to not exceed 255? If so, by whom is it guaranteed? Did he choose to use an 8bit int instead of a 32bit (default) int because he thought it would be faster?). Once again, as you can see from the questions that come up from such a seemingly simple and obvious issue: There IS no such thing as "self documenting code"!!! Use comments to tell us why you're doing things!!!

      2. Status

        Standard

    10. 6.1.10 - Preprocessor Macros

      Preprocessor macros should be avoided, unless absolutely necessary or if it would provide a definite, definable benefit.

      1. Rationale

        Macros make debugging and reading code much more difficult – especially when the same macro name is defined differently in different places (figuring out which one is actually being used can be a nightmare). There is also, usually, no real benefit to using a macro over using a function; static const data member; or whatever you're trying to accomplish with the macro.

      2. Comments

        I do acknowledge there are occasions when it makes sense to use a macro. But those instances should be very rare. If the same effect can be obtained using a static const data member (or some other means), then that approach should be used.

      3. Status

        Standard

    11. 6.1.11 - Magic Numbers

      Magic numbers should not be used. Instead, the appropriate class should contain a static const member, with a descriptive name, which is initialized to the given value.

      1. Rationale

        Using a literal, hard coded value in the code provides no indication of the meaning or purpose of the number. Also, if that value ends up being needed in more than one place we would have to remember to change it in every location it is referenced/used rather than just in one location. That increases the chances of introducing a bug.

      2. Status

        Standard

    12. 6.1.12 - Path Separators in Include Directives

      Include directives should use the slash character ("/") for the path separate, rather than the backslash.

      1. Rationale

        This will make the code more consistent and more portable – non-Windows platforms use the slash character, not the backslash. Windows source code supports either, so there is no drawback.

      2. Status

        Standard

    13. 6.1.13 - Use of String Literals

      String literals should be declared as static const data members of the appropriate class then referenced using that name, rather than embedding them directly into the code in the implementation files. The exception to this is log or printf() type output which will only appear one time.

      1. Rationale

        The contents of string literals is not checked by the compiler so putting them directly into the function implementation can result in duplication of string literals and cases where the same literal is used in multiple places and sometimes the multiple instances get out of sync.

      2. Status

        Standard

    14. 6.1.14 - Use of Wide Characters

      All character strings, internally, should be of the std::wstring or wchar_t types, rather than std::string or char types.

      1. Rationale

        All modern operating systems work, natively, with wide character strings now, so when using char types there are often implicit conversions which take place. That adds overhead. Also, all modern operating systems and C++ implementations no longer treat narrow strings as 8 bit arrays, but rather as multi-byte values, which means functions like strlen() return the number of bytes the string occupies, not the number of characters it contains. In the case of a string containing non-ASCII (multi-byte) characters, the number of characters will not correspond to the number of bytes. There is also more overhead in parsing a multi-byte string because each byte needs to be looked at individually to determine if it's a single-byte or multi-byte character and, if it's multi-byte it must be encoded/decoded accordingly. For these reasons, a (narrow) char string cannot be assumed to be an array of 8 bit characters and cannot be treated as a simple array. By using wide characters we can be sure that each element of the array (string) contains exactly one full character.

      2. Comments

        A potentially unfortunate consequence of using wide characters is that the space (memory) required to hold the strings becomes larger. On most (all?) Unix type systems wchar_t is 4 bytes; on Windows wchar_t is 2 bytes. in reality, though, this is not likely to be an issue worth worrying about in a server system.

      3. Status

        Standard

    15. 6.1.15 - Static Const String Members

      Strings which are declared as static const data members of a class should be std::wstring (or std::string) instead of C wchar_t (or char) pointers/arrays.

      1. Rationale

        There is more overhead to convert a C char string to an std::string (must allocate the internal buffer then copy the source char string) than to convert an std::string to a char string (calling std::string::c_str() to get a pointer to the underlying C char string).

      2. Status

        Standard

    16. 6.1.16 - Initialization of Enum Members

      Where the actual, numeric value of an enumeration member is significant, the value of each member should be specified explicitly, rather than relying on the compiler to automatically increment each member.

      1. Rationale

        If a member is added or removed from anywhere other than the end of the list it will change all of the values after it, potentially causing unexpected problems. This can be even more troublesome when the underlying values of the enum members are being serialized (over the network, or to a file). Specifying each value explicitly decreases the likelihood of such errors, and if a value does change it's easy to find in the version control history.

      2. Status

        Standard

    17. 6.1.17 - Correct Spelling of Names/Symbols

      All classes, functions, and class data members should be spelled correctly, according to the standard US English. When typographic and/or spelling errors are found in class, function, and/or data member names, they should be corrected as part of the overall refactoring efforts. If the discovery is made while working on another task and it can't be conveniently corrected at that time, a Doxygen @todo tag should be added to the code.

      1. Rationale

        Most programming languages are based on US English and so, they use spellings such as "color" instead of "colour", et cetera. Our code should be consistent with that. This makes it easier to find a given symbol within the code when you have to search for something. For example, imagine you are looking for a function which has to do with changing the color of something but it was incorrectly named "ChangeColer()". Adding the @todo tag ensure the issue will not be forgotten.

      2. Status

        Standard

    18. 6.1.18 - Use of Subscript Operator on STL Maps

      The subscript operator of the std::map template should not be used. Iterators should be used instead.

      1. Rationale

        There are numerous reasons for this: First, each time the subscript operator is called it is, internally, calling the find() function. This can lead to performance degradation by performing the exact same search repeatedly, if the subscript operator is used multiple times. Second, if the key does not already exist in the map then the call will automatically insert a new element with the key and with a default value. In almost all cases, this would not be the behavior we would want or expect. Third, when using the subscript operator to set, or insert a value, if the value is a pointer, then we will overwrite the existing pointer (value) with the new pointer - potentially causing a lost pointer (memory/resource leak). Fourth, the use of the subscript operator makes the code look like we're dealing with an array or vector rather than a map, which makes the code harder to debug and maintain. Fifth, because of the automatic insertion and implicit searching of the subscript operator it makes it impossible to perform proper error checking, reporting, and handling. This is particularly troublesome in cases where we have nested maps and we're using the subscript operator at multiple levels, for example foo[bar[foo1[x].size()]] = pUser;.

      2. Status

        Standard

    19. 6.1.19 - Commented-Out Code

      Whenever possible, commented-out code should be deleted, and should not be committed to the version control system.

      1. Rationale

        Commented out code raises a lot of questions for the other developers who are maintaining the code - namely: Why is it commented out? Is it no longer needed? Was there something wrong with it? Was it meant to be only temporarily commented? Should it be uncommented now? Since commented out code does not execute, it serves no purpose other than to confuse and raise questions.

        If some block or piece of code is intended to be commented out temporarily, for example because a temporary fix was applied and the original code is intended to eventually be fixed properly, then that should be clearly explained with a Doxygen @todo tag in a comment block immediately preceding the commented out code.

      2. Status

        Standard

  2. 6.2 - Files – General

    1. 6.2.1 - UTF-8 for Source Files

      Text based files, including header and implementation files, should be saved as UTF-8 encoded Unicode, without the byte order mark.

      1. Rationale

        The source files must be saved as a Unicode type because they may contain non-ASCII characters (namely, in the comments). The byte order mark should not be used because it is an obsolete requirement which is not applicable to UTF-8 encoding and causes problems from some programs/editors.

      2. Status

        Standard

  3. 6.3 - Files - Header

    1. 6.3.1 - Implementation/Header File Pairs

      Every implementation source file should have an associated header file. There are some common exceptions, such as unit/integration tests and small implementation source files containing only a main() function where no definitions are used outside the implementation source file.

      1. Rationale

        This practice makes the project more consistent, which makes it easier for developers to find and maintain specific code. For example, given a class named FooBar, it is immediately known to the developer, that the implementation of that class is in the file FooBar.cpp, and the definition is in FooBar.h.

      2. Status

        Standard

    2. 6.3.2 - Header File Name Extension

      Header files should use the file name extension .h.

      1. Rationale

        Being consistent in this respect makes it easier to find things in the code base and to use scripts to automate tasks.

      2. Status

        Guideline

    3. 6.3.3 - One Class Per Header File

      Each header file should contain, at most, one significant class definition. If the class uses small, insignificant, or supporting classes then they may also be contained within the same header file.

      1. Rationale

        This makes it easier to find a given class definition, makes the code more modular, and makes it easier to relocate a given class to another module or include the class in another module if the need arises. As development progresses, we will undoubtedly identify abstractions which may not have been obvious when the code was first implemented. Following this practice makes it easier to pull a single class, or a group of classes out of a given module and put them into a shared library.

      2. Status

        Standard

    4. 6.3.4 - Header File Named After Class

      Each header file containing a significant class definition should be named according to the name of the significant class it contains.

      1. Rationale

        This makes it easier to find the definition of a given class. Granted, in most modern IDEs you can usually right click on a class name to go to it's definition, but sometimes you may want to find a class definition when the IDE is not available.

      2. Status

        Standard

    5. 6.3.5 - Location of Shared Header Files

      Header files containing definitions used outside of the containing project/module should be located in a sub-directory of the project directory, named include.

      1. Rationale

        This makes it easier to create the external dependency on the given header file without inadvertently creating dependencies on other header files which we do not want to expose outside the given project. External projects should only reference header files in the include directory (separation of interface and implementation). If something in the exposed header file depends on a header file in the src directory then the compiler will generate an error (unless the src directory is specified in the include path – which it shouldn't be).

      2. Status

        Standard

    6. 6.3.6 - Location of Non-Shared Header Files

      Header files containing declarations which are only used within the project in which they are contained, should be located in the src sub-directory of the given project (i.e. the same directory the .cpp files are located in).

      1. Rationale

        This helps to separate the interface from the implementation. Since other projects may specify the include directory of another project in their include search path, then putting header files which contain definitions which should only be used internally, by the containing project, may inadvertently create dependencies on internal types (creating tight coupling).

      2. Status

        Standard

    7. 6.3.7 - Main Project Header File

      Each project (corresponding to a single executable or library)should have a main project header file, whose name should match the name of the project. This file should contain the declarations and similar effects which are general to the entire project but not to a particular class. Sometimes it makes sense to have an external project header file,which should be located in the include directory, and an internal project header file, which should be located in the src directory.

      1. Rationale

        This helps to centralize declarations and definitions which might otherwise be scattered amongst numerous header files, and decreases dependencies which may not be necessary. This also decreases ramp-up time because it provides developers a known, consistent, starting point when becoming familiar with the module.

      2. Status

        Guideline

    8. 6.3.8 - Include Guards

      All header files should have #define guards to prevent multiple inclusion. The format of the #define symbol should be [ORGANIZATION]_[PRODUCT]_[PROJECT]_[FILE]_H_.

      1. Rationale

        Decreases the chances of inadvertent clashes. As the projects grow there is an increasing likelihood that there will be multiple files with the same name in different modules, or even within different directories of the same module. If both header files use the same symbol for their include guard then the second file would be skipped due to the symbol conflict. This standard ensures that will not happen.

      2. Status

        Standard

    9. 6.3.9 - Define Directive for Include Guard

      Header files should use the #define include guards instead of the#pragma once directive.

      1. Rationale

        The #pragma once directive is not part of the C++ standard, though it is supported by most modern compilers. For that reason, it may be possible that there will be compatibility issues between different versions of compilers. Other than that there is not a compelling reason to use one type of guard over the other. However, to keep the header files consistent we should standardize on one.

      2. Status

        Guideline

    10. 6.3.10 - Use of Forward Declarations

      Whenever possible you should forward declare ordinary classes rather than using include directives to include the class's header file/definition.

      1. Rationale

        Minimizing the number of include directives in header files decreases the chances of inadvertently creating unwanted dependencies. If the required symbol is only referenced in a given header file by pointer or reference then the compiler doesn't need the actual definition (since a pointer/reference is just a memory address). This also forces the implementation file to have to explicitly contain the include directive, which makes it easier to tell exactly which header files are actually being included in any given implementation file.

      2. Status

        Standard

    11. 6.3.11 - Definition of Inline Functions

      Functions should only be defined within the class definition, in the header file if they are very small and do not contain complex logic.

      1. Rationale

        Defining functions within the class definition adds a lot of "noise" to the class definition, which makes it more difficult to focus on just the class definition. By avoiding putting function definitions inside the class definition, the structure of the class is easier to "see".

      2. Comments

        Some may argue that by placing the function definition within the class definition, in the header file, it improves the compiler optimizer's ability to inline the given function, which will improve runtime performance. I don't consider this a legitimate argument, though, because when performing a "release" (optimized) build, you should be enabling the compiler's link time optimization (LTO) feature, which will likely inline the given functions anyway.

        For those who are thinking this really sounds like a matter of person style - I am inclined to agree, to some extent. I, personally, don't like having function definitions in the header files because I find it distracts from the "overview" of the class which you get by being able to look only it the class definition.

      3. Status

        Standard

    12. 6.3.12 - Separate Headers for Inline Functions

      You may use file names with a -inl.h suffix to define complex inline functions when needed. However, the file should ONLY contain those inline functions.

      1. Rationale

        This approach should be preferred over putting the function definitions directly into the class definition. This approach provides the same benefits as defining the function within the class definition (i.e. potentially more effective inlining of the functions), without cluttering the class definition. In cases where LTO cannot be used, or would not be effective, inline functions should defined in header files with the -inl.h extension.

      2. Comments

        I have not encountered any evidence to show that defining inline functions within header files provides any performance benefit over using LTO. If the function is in a shared library then some compilers may not be able to inline them, even when using LTO. That's something you'd have to confirm with your given compiler(s).

        You also need to consider whether inlining a given function would really even make a performance difference. In many cases, for example when a function has only a small number of simple parameters, you may not even get a measurable benefit by inlining the function.

        And while we're on the topic of function inlining and runtime performance, don't forget that the compiler is also very likely going to apply return value optimization (RVO) to the function if it's not inlined.

        In closing, I'm not convinced that there is any benefit to explicitly declaring a function as inline, or to defining such functions in header files. All contemporary compilers are going to decide for themselves (based on the optimization flags you provide) whether or not to actually inline a given function - regardless of how you declare it. And, those compilers are going to do a fine job of optimizing such code - again, regardless of whether or not you declare the function as inline.

      3. Status

        Guideline

    13. 6.3.13 - Order of Function Parameters

      When declaring a function, input parameters should be listed first, then output parameters.

      1. Rationale

        This helps to make the code more consistent and, therefore, easier to become familiar with and to maintain. The more assumptions you can make about a piece of code, without having to verify them by actually checking the declarations/definitions, the less time you'll have to spend studying the code. If you have a group of functions, which all take similar parameters, but the ordering of the parameters is different in each case, then it is much harder to memorize the APIs and you'll have to double check the function signatures more frequently.

      2. Status

        Standard

    14. 6.3.14 - Order of Include Directives

      Include directives should be stated in the following order: C library; C++ library;third party/external libraries'; other internal (to your organization) libraries;the current project's headers.

      1. Rationale

        Helps to prevent dependency loops and makes managing include directives easier. If a given include directive is no longer necessary it is easier to identify and remove.

      2. Comments

        In my own projects, I also prefer to list the include directives alphabetically within each of the groupings stated above. In cases where there are a large number of include directives within a source file, this makes it easier to see whether a given header file is already included. Some people consider me too "uptight" on this issue.

      3. Status

        Standard

  4. 6.4 - Files - Implementation

    1. 6.4.1 - File Name Extensions

      All C and C++ source files should use the file name extension .cpp.

      1. Rationale

        Being consistent in this respect makes it easier to find things in the code base and to use scripts to automate some tasks.

      2. Status

        Guideline

    2. 6.4.2 - One Class Per Implementation File

      Each implementation source file should contain, at most, one significant class implementation. If the class uses small,insignificant, or supporting classes then they may also be contained within the same implementation file.

      1. Rationale

        When a given implementation file contains only a single class implementation it makes the code more modular and easier to split up, if needed. This makes it easier to move a given class to a different module, or to add a single class to another module without inadvertently introducing unwanted dependencies.

      2. Status

        Standard

    3. 6.4.3 - Entire Implementation In Single File

      A given implementation file should contain the entire implementation of the class after which it is named. The implementation of a class should not be spread across multiple implementation files.

      1. Rationale

        When a class implementation is spread across multiple implementation files it increases the likelihood of introducing a bug because the parts of the class which are located in other files might be missed when making changes to the class. It also increases the time required to become familiar with the code base because the developer must jump around to different files and try to remember which files contain which functions.

      2. Status

        Standard

    4. 6.4.4 - Implementation File Named After Class

      Each implementation file should be named according to the significant class implemented therein. Specifically, the file name should be the same as the class name, plus the.cpp extension.

      1. Rationale

        When the implementation file name matches the name of the class implemented therein, it is clear what the file contains and where a given class is implemented. This makes it easier to find the implementation of a given class.

      2. Status

        Standard

    5. 6.4.5 - Main Project Implementation File

      Each project (corresponding to a single executable or library)should have a main project implementation file, whose name should match the name of the project. This implementation file should contain the main() function and any other global functions and effects which are not specific to a given class.

      1. Rationale

        One of the first questions a developer has when they start working with a given code base should be "Where does the processing flow begin?" - in other words: "Where is the main() function?". If each module follows this standard then we always know where to find main().

      2. Status

        Standard

    6. 6.4.6 - Implementation/Header File Pairs

      A given implementation file and it's corresponding header file should have the same name (including case), differing only in their extension (.cpp and .h).

      1. Rationale

        Makes it obvious that the two files are a pair and should be treated as a single unit in certain respects. If, for example, we decide that a certain class should be moved to a common, shared library, then we can easily see that both files will need to be moved.

      2. Status

        Standard

    7. 6.4.7 - Supporting Classes Declared in Implementation File

      If an implementation file contains small, or supporting classes which are only used by the significant class of the implementation file they should be declared within the implementation rather than in the header file.

      1. Rationale

        This helps to keep the interface of the significant class, and it's containing package, clean. It also decreases the chances of introducing unnecessary dependencies on the significant class's internal implementation.

      2. Status

        Guideline

  5. 6.5 - Scoping

    1. 6.5.1 - Organization (Top/First) Level Namespace

      There should be a top level namespace, named for the organization, which should be the root of all other namespaces for all code developed internally. This should be referred to as the "company" or "organization" namespace.

      1. Rationale

        This helps to quickly and easily identify all types which are proprietary to the organization. It also eliminates the possibility of name clashes with any third party libraries or code.

      2. Status

        Standard

    2. 6.5.2 - Product (Second) Level Namespace

      There should be a second level namespace, within the organization namespace, which should be named according to the particular product (not project or module), for example realcomp for the "Realistic Server Comparison" product. This namespace should contain allcode which is specific to that product. Code which is not specific to the product, i.e.code which is shared amongst multiple products should not be contained in this namespace.This namespace should be referred to as the "product" namespace.

      1. Rationale

        Use of a second level, product specific namespace makes it clear that the code is part of, and is specific to, the given product. This will become particularly beneficial when the team will be working on code for multiple products. It is also important that code which is not specific to a given product be placed in a separate second level namespace, so it is clear that such code may be used across multiple products.

      2. Comments

        To help clarify the difference between a "product" and a "project" in this context, you can think of a "product" as being an overall system, which may be comprised of multiple separate, yet closely related, components (projects). For example, a collaboration system may be the product, which consists of a messaging server, an LDAP server, a calendar server, et cetera, which would be the projects.

        You may be thinking some products may not be large or distributed enough to warrant having separate product and project namespaces. In such cases you may be inclined to skip this standard. That may be acceptable to simplify the code, but there's really no conceivable drawback to having the additional namespace level and it does help to keep things more isolated right from the beginning should the product/project scope grow over time.

      3. Status

        Standard

    3. 6.5.3 - Project (Third) Level Namespace

      There should be a third level namespace, within the product namespace, which should be named according to the particular project, module, or component. For example: server,client, common, for the modules of the realcomp product. Each of these namespaces should contain only code which is part of the given module.

      1. Rationale

        Using project/module specific namespaces at the third level increases the clarity of the code by organizing it hierarchically. It also aides in identifying duplicate code and functionality, which should be moved to a common, shared library.

      2. Status

        Standard

    4. 6.5.4 - Declarations and Definitions Within Namespaces

      All declarations and definitions should be contained within appropriate namespaces. Whenever possible, there should not be any global types or variables.

      1. Rationale

        The use of global symbols and types increases the chances of name clashes and makes the code harder to extend over time due to potential unknown dependencies. By declaring all symbols and types within appropriate namespaces it helps to encapsulate the code and the functionality, which makes future changes easier.

      2. Status

        Standard

    5. 6.5.5 - "Using Namespace" Directives in Header Files

      using namespace directives should not be used in header files.

      1. Rationale

        By putting a using namespace directive in a header file you cause that directive to propagate to each other header file and implementation file which includes that header file. This may lead to inadvertent name clashes or symbol hiding when a given implementation file also contains another using namespace directive for another namespace which contains symbols or type names.

      2. Status

        Standard

    6. 6.5.6 - "Using Namespace" Directives in Implementation Files

      using namespace directives may be used in implementation files.

      1. Rationale

        using namespace directives help to improve the readability of the code by not requiring long namespace qualifiers when referencing contained symbols. But you must be careful not to introduce name clashes when using multiple using namespace directives. Unlike with header files, using namespace directives within implementation files will not be propagated to other source files.

      2. Status

        Standard

    7. 6.5.7 - Inline Namespaces

      Inline namespaces should not be used.

      1. Rationale

        Inline namespaces cause the declarations contained within them to be directly visible to the containing (parent) namespace. This can lead to ambiguities. It should be preferable to have to qualify symbols in nested namespaces because that is, after all, what hierarchical namespaces are for. If the developer does not want to have to qualify the symbol with the namespace name, they can always use a using namespace directive.

      2. Status

        Standard

    8. 6.5.8 - Global Functions

      Unless absolutely necessary, global functions should not be used.Instead, the function should be a static member (assuming it needs to be static) of the appropriate class.

      1. Rationale

        Global functions are contrary to good object oriented design and make an object oriented system more difficult to maintain/extend. Other than the main() function, I cannot think of any instances in which a function must be global.

      2. Comments

        Although the use of global functions is contrary to good object oriented design, one of the biggest benefits of C++ over other languages (like Java), is that it is a hybrid and does not force a developer to use an exclusively object oriented or an exclusively procedural paradigm. In line with that, this standard should be interpreted as meaning excessive use of global functions should be avoided. In real development there will always be times when a certain function is best implemented globally (usually because it wouldn't make sense to implement an entire class for the one function - a good example is a factory function).

      3. Status

        Guideline

    9. 6.5.9 - Local Variables in Narrowest Scope

      Local variables should be declared in the narrowest scope possible. In some cases it may increase readability and clarity of the code to even enclose a block of code within curly braces just to force the variable to come into scope and go out of scope explicitly. The additional curly braces will not adversely affect the code or performance.

      1. Rationale

        By creating and destroying local variables in the narrowest scope possible the code is easier to understand and maintain because it is clear that the variable is only required within that specific duration and for a specific purpose. When variables are declared at the start of a function (as they used to have to be in C), then the developer must check the entire function to be confident that the value of the variable is not being relevantly changed at any given point.

        This practice also discourages the reuse of a single local variable for multiple purposes (which is, in itself, a bad practice).

      2. Status

        Standard

    10. 6.5.10 - Global Variables

      Global variables should not be used. Instead, they should be private or protected, static members of the appropriate class.

      1. Rationale

        Global variables make the code much more difficult to debug and maintain because they could, potentially, be changed anywhere and at any time. The debugging difficulty is increased significantly in a multithreaded application because any thread could potentially be changing the value and while a problem may appear to be coming from a given thread it could actually be caused by a completely different thread modifying the value of the global variable.

        And, if the global variable is a pointer to some other variable the debugging difficulty increases much more. By making the variable a private member of a class and providing an accessor function then any changes to it's value are much easier to monitor.

      2. Comments

        Keep in mind, if an accessor function is used, the compiler optimizer is likely going to optimize away the entire function (usually by inlining it) so there usually would not be a performance hit. At the same time, when debugging, it is very easy to temporarily insert additional debug code into the accessor function to aid in determining exactly which thread is modifying the variable.

      3. Status

        Status

    11. 6.5.11 - Global Enumerations

      Defining global enumerators should be avoided. Whenever possible, enumerators should be defined within the scope of the class they most closely pertain to. If they are not logically related to a particular class then they should at least be defined within an appropriate namespace.

      1. Rationale

        This is partially to avoid name collisions, because the names used for the members of an enum are in the scope in which the enum itself is defined.

        This also improves encapsulation, which makes future modifications to the code easier.

      2. Status

        Standard

    12. 6.5.12 - Scoped Enumerations

      Scoped enumerations should be used, instead of unscoped enumerations, whenever possible.

      1. Rationale

        Using scoped enumerations decreases the chances of name conflicts. Often, the members of enumerations have very simple, common word names which makes name conflicts very likely. By declaring the enumeration as enum class or enum struct, it requires references to the enumeration's members to be qualified with the name of the enumeration.

        Although a scoped enumeration's members cannot be implicitly converted to an integral type, they can still be cast to such.

      2. Status

        Standard

  6. 6.6 - Classes

    1. 6.6.1 - Virtual Functions From Constructors

      Virtual functions should not be called from constructors.

      1. Rationale

        At the point that a constructor is invoked, we cannot be certain that the data accessed by the derived class has been created or initialized yet. This can lead to invalid data or crashes.

      2. Status

        Standard

    2. 6.6.2 - Failures in Constructors

      Operations which could cause a constructor to fail or return before it has completed all it's initialization (return early) should not be performed in constructors when exception handling is not being used.

      1. Rationale

        Since constructors do not return a value there is no reasonable way, other than throwing an exception, to notify the calling code that a constructor did not complete. Without the use of exceptions, the only way we can properly handle failed object construction/initialization is to either: have the class contain a "status" member which would be set to "invalid" if an error occurred in the constructor, and which the caller would have to check after the constructor returns; or, to only provide object initialization which may, potentially, fail, in an "Initialize()" member which would have to be called after the constructor returns. Neither approach is particularly attractive.

      2. Comments

        On projects which use exception handling it may be fine to throw an exception from a constructor. But keep in mind, this approach can sometimes lead to very complicated, very ugly code in the calling function(s). Generally, exception handling usually results unnecessarily complicated code anyway.

      3. Status

        Standard

    3. 6.6.3 - Compiler Generated Default Constructors

      Every class which contains non-static data members should not use a compiler generated default constructor.

      1. Rationale

        If a class does not provide at least one explicitly defined constructor then the compiler will automatically generate a simple default constructor. With some compilers, a generated default constructor will initialize it's members to a specific values when compiled in debug mode, but leave the members uninitialized when compiled in release mode (for performance reasons).

        The problem arises when the code is later maintained: Was the absence of an explicit default constructor deliberate or an oversight? If a default constructor was deliberately not provided then the reason should, at least, be stated in a comment.

      2. Comments

        There is some debate whether it is really better to use a compiler generated default constructor for simple classes, or to explicitly specify one. Some believe a compiler generated default constructor will provide better runtime performance. I've never seen any evidence of this in the real world. The few assembly instructions that might be saved or generally too insignificant to waste effort considering.

      3. Status

        Guideline

    4. 6.6.4 - Use of Explicit for Constructors

      Constructors with a single parameter should be declared "explicit".

      1. Rationale

        Without the explicit keyword, the compiler may perform implicit data type conversions on the parameter when calling such a constructor. This can lead to unexpected and/or undesirable behavior.

      2. Status

        Standard

    5. 6.6.5 - Copying/Assigning Classes Containing Pointers

      Classes containing data members which are pointers should either have their copy constructor and assignment operator disabled (declared private) or provide implementations which properly handle the data being pointed to.

      1. Rationale

        A conceptual copy of an object with pointed-to data can either have those copies share the pointed-to data, or require each copy to maintain it's own, distinct copy of the pointed-to data. This is a design issue which should be addressed before the class is implemented. If multiple copies of an object share pointed-to data then a reference counting scheme must be used to ensure one copy's destructor does not release data which is still being used by other copies. Also, for performance reasons, a copy-on-write scheme might be desirable to prevent unnecessary copying of buffers – another design time issue to address before implementing the class.

        Whichever approach is taken, the reason(s) should be documented, for the benefit of the poor fellow who will be maintaining it two years from now.

      2. Status

        Standard

    6. 6.6.6 - Prefer Classes Over Structs

      Classes should always be used instead of structs, except in cases where the type is completely passive (does not contain any member functions).

      1. Rationale

        This helps to improve the clarity of the implementor's intention. In C++ the only difference between a class and a struct is whether it's members are public or private, by default. Conceptually, though, if we think of a class as a complex object upon which we may wish to apply polymorphism and inheritance, and a struct as only a passive container, then the code may be easier to comprehend because when we encounter a struct we can assume it was only intended to be a simple container. If we later add methods to the struct or change it's intended purpose, we should change the type to a class.

      2. Status

        Guideline

    7. 6.6.7 - Private Members in Structs

      Structs should not contain any private data members. This is implied by the standard that structs should only be used for passive data encapsulation.

      1. Rationale

        If structs should only be used as passive containers then there would be no way to access a private data member, other than with a member function, at which point the struct should be changed to a class.

      2. Status

        Standard

    8. 6.6.8 - Interface Classes

      Interface classes should only contain public, pure virtual functions. The exception to this is the destructor. Since all interfaces are inherently polymorphic, they absolutely should contain an explicit virtual destructor.

      1. Rationale

        In C++ an interface is only a conceptual type, which is not enforced by the language through the use of a keyword (e.g. the interface keyword in Java). In order to remain consistent with the meaning of an interface, classes which are meant to be such should comply with the requirement that they only provide the contract, and not the implementation of that contract.

      2. Status

        Standard

    9. 6.6.9 - All Data Members Private

      All data members should be declared private and only be accessible through accessor functions. There may be some exceptions to this in cases where it would make sense for a derived class to be able to access a member directly. In those cases the data members would be declared protected.

      1. Rationale

        By strictly controlling access to data members, the code is easier to maintain and debug because the points at which the data can be changed are limited. This also makes it much easier to move and/or modify the class, if needed, because no external code can depend on the class's data members directly.

        This practice also decreases the complications which arise when a derived class contains a data member which happens to have the same name as a member in the base class. If it is private to the base class then no inadvertent conflict or name hiding will occur.

      2. Status

        Standard

    10. 6.6.10 - Order of Member Declarations

      Public members should be declared first, then protected members, then private members, within the class declaration.

      1. Rationale

        This improves the readability of the code by making the class declarations consistent.

      2. Comments

        This should be considered nothing more than a guideline because it is one of those cases where there is bound to be exceptions regularly. Sometimes, following this type of rule will actually make the code harder to maintain because adhering to this standard would mean having to split up members which would be more logically grouped by their functionality or their inheritance.

      3. Status

        Guideline

    11. 6.6.11 - Grouping of Overriding Interface Virtual Functions

      Declarations of overriding implementations of base interface virtual functions should be grouped together, within the class declaration. A comment stating the interface name should also be placed before the function declarations to make it easy to see that they are the realization of the given interface.

      1. Rationale

        I believe this improves the readability of the class definition by grouping functionally related members together. Since the members of an interface are all, presumably, public then this standard conflict with the standard about ordering members based on the visibility (see item 6.6.10).

      2. Comments

        There may be times when an interface function, declared in a base class (or "realized interface", if you prefer that term), should be redeclared as private or protected in the realizing class. When this happens, I believe it is preferable to still group the function declaration with the rest of the interface functions, but precede it with the protected or private keyword. This is one of the reasons item 6.6.10 should only be considered a guideline.

      3. Status

        Standard

    12. 6.6.12 - Member Grouping Based on Functionality

      When a class definition is sufficiently large or complex (in terms of number of members and/or functionality), members should be grouped within the definition based on their related functionality. In some cases it may be preferable to group based on functionality over grouping based on visibility.

      1. Rationale

        In cases of very large classes with many members providing diverse functionality it is difficult and time consuming to look through the entire definition to determine whether a given API already exists. This is compounded by potential poor naming or use of grammar. By grouping related members together a developer only has to look through a small subset of members. It also simplifies maintenance because if it is determined, for example, that a certain subset of functionality should be moved to a base class then it is easier to identify the relevant members.

        To improve the clarity of the code even more, each of the functional groupings should be preceded by a brief comment describing the particular functionality which relates the given members.

      2. Status

        Standard

  7. 6.7 - Functions

    1. 6.7.1 - Const Member Functions

      Member functions which do not modify the state of their object (by changing a data member) should be declared const.

      1. Rationale

        This increases the purpose and clarity of the given function by stating that it does not change it's object's state. It also makes the code easier to maintain and to extend - failing to declare a given function as const results in not being able to call the function on a const instance of the object.

      2. Comments

        Sometimes declaring a member function const will complicate things because a non-state related member, for example a mutex, may need to be modified. When this situation arises you would either have to not declare the function const or, preferably, declare the variable mutable. I agree, declaring the variable mutable might seem hacky, but it is better than not declaring the function const. Remember, the const-ness of the function should relate to the perceivable state of the object - if the variable that needs to be modified does not relate to the perceivable state of the object then it should be acceptable.

      3. Status

        Standard

    2. 6.7.2 - Passing by Pointer/Reference

      Passing by pointer should be preferred over passing by reference.

      1. Rationale

        Internally (meaning within the compiler) a pointer and a reference are the same – they are an address within memory. The difference is purely a matter of syntax. The problem with references, though, is that they create a false sense of security by leading the developer into thinking the entity referred to can not be null; they also make the appearance of the code misleading because syntactically, it looks like you're dealing with the actual object, not a reference/address of an object.

      2. Comments

        I mentioned in the Rationale, that using references deceives the developer into believing the entity referred to cannot be null. Many a C++ developer have argued this point with me, claiming that a reference in C++ can never be null - that is, after all, the main point of a reference over a pointer. They are, unfortunately, incorrect! There is nothing in C++ which would prevent a developer from initializing a reference by assigning it to a dereferenced null pointer. Consider the following code fragment:

        01:   std::wstring* pstrNull = nullptr;
        02:   std::wstring& rstrNull = *pstrNull;

        The above code fragment is perfectly legal C++, and the assignment which occurs on line 2 will not result in a runtime error - not even if you pass rstrNull to a function which expects a valid reference to a wstring. Until some piece of code actually attempts to dereference rstrNull everything will be fine.

        And when you write a function that takes a reference, how do you go about verifying that reference is, in fact, not null before you use it? You don't - that is, you can't! There is no way to verify a reference is not null, short of casting it back to a pointer then verify that pointer is not null.

        My advice: use references for copy constructors and overloaded operators, because you have to; otherwise, use pointers. They're open and honest about who they really are.

      3. Status

        Standard

    3. 6.7.3 - Passing Const Pointer/Reference

      When passing by pointer (or reference, if you insist on doing so) the parameter should be declared const, unless it is deliberately intended that the called function is meant to modify the original (pointed/referred to) object.

      1. Rationale

        This improves the clarity of the code, specifically with respect to the given function, by guaranteeing that the parameter will not be modified within the function.

        The compiler may also use that parameter's const-ness to aid in performing optimizations.

        And, if the given variable is const in the calling function then it cannot be passed as volatile (non-const) to another function.

      2. Status

        Standard

    4. 6.7.4 - Default Function Parameters

      Default function parameters should be avoided.

      1. Rationale

        The use of default parameters sometimes interferes with function overloading and leads to ambiguity and unclear code.

      2. Comments

        One of the benefits of being able to overload functions is that when requirements change and new use cases appear, new versions of a function can be added to existing code fairly easily. But, when you declare a function with default parameters you limit the ability to overload the function.

        Consider, for example, you create a function which takes a wchar_t* and two integers. But the two integers are declared with a default of zero (0). Over time, your function gets called from many places, often without specifying the int values, because the developers who called it were too lazy to bother typing the extra commas and zero characters.

        Sometime down the road, you're presented with a new, yet similar use case. This new version of the function only needs to take the wchar_t*, not the two integers. You can't simply write an overloaded implementation that just takes the wchar_t* because then the compiler can't determine which version of the function is being called when only the wchar_t* parameter is provided.

        So, either you create the new function with an incredibly similar, yet confusingly different name - even though they both do essentially the same thing; or you go through all the existing code and explicitly add the defaulted parameters to each of the function calls (which might not an option if your function is used outside your own team).

        Now consider, a year down the road when that new use case comes in, you probably won't be the person working on it. So some other developer is going to look at your handiwork (i.e. the use of default parameters) and curse you for making his job so much more difficult.

      3. Status

        Standard

    5. 6.7.5 - Returning Integers to Report Errors

      Any function which might potentially fail, for any reason, should return a numeric value (e.g. an int), which states whether it succeeded or failed. In the case of a failure, a different value should be used for each of the possible causes of a failure.

      1. Rationale

        In the absence of exception handling, a function which does not return a value has no way of notifying the caller that it failed. If the function only returns a boolean then there is no way for the caller to determine why the function failed, and therefore, no way to determine whether it can recover or how it should handle the failure.

      2. Comments

        This standard is more about not returning void or bool, than about using return values versus exception handling. The question of using return values or exception handling is more of a design issue than a coding issue.

        For my own projects, I generally a return value of 0 (zero) to indicate success, and a positive integer to identify exactly what error occurred.

      3. Status

        Standard

  8. 6.8 - Naming

    1. 6.8.1 - Descriptive Class Names

      Class names should be descriptive and consist, essentially, of nouns and, preferably also adjectives. Typically, the name of the absolute base class of some hierarchy should consist of a noun, then each derived class should be a combination of that noun with one or more adjectives that describe how it is more specific. For example, you may have an absolute base class named Socket, from which is derived ServerSocket and ClientSocket; from ServerSocket you might derive ServerTcpSocket and ServerUdpSocket.

      1. Rationale

        Using increasingly specific adjectives, together with the common noun (from the base class) makes it easier to determine that a given class is derived from, and is therefore, a more specific type of the base class. It also makes it easier to conceptually group classes together by type.

      2. Status

        Standard

    2. 6.8.2 - Class Name Prefixes

      Class names may be prefixed with an upper case "C" to indicate they are intended to be ordinary classes.

      1. Rationale

        Prefixing the letter "C" to class names may improve the clarity or the readability of the code, though I personally believe this is debatable.

        The important requirement for a standard as this is that if it's going to be used then it needs to be completely in order to provide any real benefit. That is, every class in your code base should follow the convention. If it is used selectively then it would not accomplish it's goal of improving the clarity of the code.

      2. Comments

        This practice has become fairly uncommon and it is not likely to be an issue with any new development. It is included herein because I have encountered the practice within pre-existing code on a number of projects over the years and I don't believe that prefixing a "C" to class names necessarily decreases the quality or clarity of the code.

        In my own projects, I would not favor prefixing class names with a character to clarify that they type is a class. In most C++ projects, most of the types other than primitive types are typically classes. Assuming that, a developer should generally be able to infer that any type he encounters which is not a primitive type is very likely a class. If anything, it would be more useful to prefix the types which are not classes.

      3. Status

        Guideline

    3. 6.8.3 - Interface Class Prefixes

      The names of classes which are intended to be interfaces should be prefixed with an upper-case letter "I" to indicate such. This applies only to those classes which are actually intended to be interfaces, not just because they contain at least one pure virtual function and are, therefore, abstract.

      1. Rationale

        Makes the interface class's intention and purpose more clear. Decreases the likelihood of a maintainer inadvertently adding non-interface members to the class, thereby breaking the contract.

        This practice has also become fairly standard throughout the industry, so we should be able to expect when a developer encounters a class whose name is prefixed with an upper case "I" he understands that the class is intended to be an interface.

      2. Status

        Standard

    4. 6.8.4 - Use of CamelCase

      For all names (whether variables, class, or function) each word (or token) within the name should begin with an upper-case letter, with the remainder of the word in lower-case letters. For acronyms within a name, the first letter of the acronym should be uppercase with the remaining letters lower-case.

      1. Rationale

        This helps to make the code more readable because the upper-case letter highlights the start of each token within the name. For example, the brain can tokenize the "ThisIsALongVariableName" much faster than "thisisalongvariablename", which helps the developer to be able to scan the code.

      2. Status

        Standard

    5. 6.8.5 - Use of Underscores in Class Names

      Case should be used to separate the words/tokens within a name, rather than underscores. This is more of an issue for class and function names. In the case of local variables it is not so critical and if the individual prefers one over the other he should be able to use his preference/discretion.

      1. Rationale

        This is, to some extent, a matter of individual preference – either approach (using case or underscore characters) helps to separate the tokens within a name. The reason for this standard is to promote uniformity amongst the interfaces within the code.

      2. Status

        Standard

    6. 6.8.6 - Thread Start Routines

      Thread start routine (entry point) functions, which are static members of a class should use a suffix, such as "_SR", to indicate such.

      1. Rationale

        Since a thread start routine which is a member of a class must be declared static, this practice enables the use of the same function name (but without the suffix) to be used as the instance member function which would be called from the static function.

        This also helps to make it obvious that the given function is merely a static wrapper function for the correspondingly named instance member function.

      2. Comments

        A non-static member function cannot be used as the start routine of a thread as passed to pthread_create() (or CreateThread(), on Windows), so it is a common practice to use a static member function and pass a pointer to the object instance as the thread start data; then within that static function, cast the pointer (which is passed as a void*) back to the actual type. Using that casted pointer, we would then call the member function with the same name as the static start routine (but without the suffix).

        It should be pointed out that, unlike Java, C++ does not impose a one-to-one relationship between a class and a thread. In C++, a given class can spawn and "own" any number of threads, each with their own start routine. Therefore, it is not practical to require that the static member function of a class, which is the start routine of a thread, simply use a common, well known name (e.g. Run()), as it does in Java (i.e. run()).

      3. Status

        Standard

    7. 6.8.7 - File Names Based on Class Names

      File names should correspond to the name of the significant class they contain. For example, the header file for a class named FooBarCounter should be FooBarCounter.h and the implementation file should be FooBarCounter.cpp.

      1. Rationale

        This makes it easier to find and to remember where a given class is defined and implemented. Otherwise, the developer either has to rely on the IDE to locate the class definition/implementation, or grep for it. The problem with relying too much on the IDE for such matters, is that the IDE is not always available – for example, when diagnosing a problem remotely.

      2. Status

        Standard

    8. 6.8.8 - Names of Main Project Source Files

      The name of the main header and implementation files for a given project should match the name of the project.

      1. Rationale

        This makes it easier to find the main() function and other global effects for the given project. Although it only takes a few moments to grep for main, it is one more thing to distract the developer for what he's really trying to accomplish. Also, the word "main" is very common and on most projects of any reasonable size, grepping for it will usually result in many, many hits.

      2. Status

        Standard

    9. 6.8.9 - Filesystem Independent File Names

      File names should be valid on Windows NTFS, OS X, and Unix file systems. There may, occasionally, be complications with this as Windows file systems are not case sensitive; OS X is optionally case sensitive; and Unix file systems are, generally, more flexible. In such cases, go with what will be the most compatible.

      1. Rationale

        When porting a project from a Unix-like system, which supports case sensitive file names, to Windows or OS X, which use case insensitive file names you will encounter complications if, for example, you have multiple files which differ only in their case, or contain characters not supported on the new target platforms.

      2. Comments

        I have worked on some projects where the policy was to use only lower case letters in file names, regardless of the casing of the contained class names. I believe this is a satisfactory approach - though not ideal. In my opinion, it was excessive because although Windows and OS X may consider two file names which differ only in case to be identical, they will, nevertheless, retain the case of the file names. Neither system would convert the case of the file names on their own.

        Some version control systems will be troublesome when used simultaneously with one platform which supports case sensitive file names and one which doesn't. If you're working in such an environment, be sure your version control system doesn't change the case of the file names inadvertently.

      3. Status

        Standard

    10. 6.8.10 - Variable Name Prefixing

      All variable names should use the agreed upon prefixing system to indicate certain data type information. The prefixing system is attached to this document as Appendix A.

      1. Rationale

        It is very counter-productive for developers to have to search through source code to determine what type of data a given variable is. And, it is very dangerous when developers have to assume what the type of data is by reading surrounding code. The brain is conditioned to look for and identify common or familiar patterns, so by consistently using naming prefixes which inform the developer of the type of data the variable contains the eye can scan through a bunch of code faster, rather than having to read it line by line.

        By consistently prefixing certain type information to the variable name, the developer can quickly and easily determine what type of data the variable is representing.

      2. Comments

        I should clarify, in this context, when we speak of the "type of the data", we are speaking of the conceptual type, not of the C++ "data type". That is, we're not so concerned with whether the variable is an int, a double, or a float; but rather, whether it is a "counter", an "index", or a general "number". We don't care whether it's an array of wchar_t, a pointer to a null terminated sequence of wchar_t, or a std::wstring; we care that it's a "character string".

        On a side note, this is probably the standard I have received the most resistance to over the years. It seems there is a huge number of developers who believe a variable named category conveys just as much useful information to another developer two years down the road as m_idCategory does. I tend to disagree. The "m_" tells us it is a member variable of the given class; the "id" tells us the value contained in the variable is the numeric identifier of a "category" (such identifier probably being the key within a std::map). The name category tells us it is something to do with a "category", but further investigation will certainly be required.

        Like it or not, consistent type prefixing saves a lot of maintenance time and effort.

      3. Status

        Standard

    11. 6.8.11 - Variable Name Word Parts

      Following the variable name type prefix, the remainder of the variable name should consist of English words, word parts, or abbreviations – as long as those word parts or abbreviations are commonly known, or are listed in Appendix B of this document. Each word (or word part, or abbreviation) in the name should begin with an upper-case letter. The rest of each word/word part/abbreviation should be lower-case.

      1. Rationale

        The use of English terms in the variable names makes it easier for English speaking developers to understand the code, and increases the consistency of the code (which also makes it easier to understand the code). English is chosen as the language of preference because it is the language which most programming languages (and, in this case, C++) are based on. The use of a single, common language, for variable names also makes it easier to locate specific code – it would unnecessarily complicate searches if we use nextUser in one place, prochainUtilisateur (French?) in another, then siguienteUsuario (Spanish?) in another.

      2. Status

        Standard

    12. 6.8.12 - Member Variables of Structs

      Data members of structs (as opposed to classes) should not be prefixed with m_, like class members are. Name conflicts won't arise because structs should only be used for simple, passive containment and should not contain member functions (other than, possibly, constructors/destructors).

      1. Rationale

        This mainly serves the purpose of keeping the code clean, and consistent with general, industry practices.

      2. Status

        Standard

    13. 6.8.13 - Const Variables

      Constants (e.g. static const data members) should be all upper-case, with a single underscore character between each word that makes up the name. The type prefixes, as used for other variables, should not be used.

      1. Rationale

        This helps to quickly identify a symbol as being static const data, likely used to represent some literal. This helps to save time and decreases distractions, because the developer does not have to look up the definition of the symbol.

      2. Status

        Standard

    14. 6.8.14 - Accessor Functions

      Accessor functions should be named based on the conceptual data they are accessing and should use the terms "Get" and "Set", for example for a data member which contains the name of something, use GetName(), and SetName().

      1. Rationale

        Improves the clarity of the code by including the relevant verb in the function name.

      2. Comments

        Some developers prefer to omit the term "Get" from the accessor function name because they believe it is redundant. I disagree. I find that a function with a name like Name() is too short and ambiguous, and ends up not looking like a function name at all. Since functions perform some action, I believe that action should be conveyed in the function name. Also, it seems inconsistent to use the verb on the setter, but not on the getter.

      3. Status

        Standard

    15. 6.8.15 - Namespaces

      Namespace names should be all lower-case, and based on the name of the hierarchical level they are encapsulating (e.g. the project, product, organization, et cetera). If the namespace name consists of multiple words then each word should be separated by an underscore.

      1. Rationale

        This helps to promote consistency amongst the code and to easily distinguish a namespace name from a class name.

      2. Comments

        To some extent, other than promoting consistency, the use of all lower case and underscores is a matter of personal preference. As long as a consistent pattern is used throughout, it should be acceptable.

      3. Status

        Standard

    16. 6.8.16 - Enumeration Members

      Enumeration members should be named using the typical camel-case (first letter of each word capitalized).

      1. Rationale

        The use of camel-case makes the code easier to read when a symbol consists of multiple tokens. It shouldn't be necessary to prefix the name of an enumeration member (for example, with a lower-case 'e') because the fact that it's an enum (as opposed to an int or a long) should not be relevant to it's use.

      2. Comments

        As an aside, an enum is, internally, implemented by the compiler as an int, anyway.

      3. Status

        Standard

    17. 6.8.17 - Macros

      Macro names should be all upper-case, with a single underscore separating each word of the name. However, unless it is absolutely necessary and there is absolutely no other way to accomplish what you're trying to accomplish, you should not use macros anyway.

      1. Rationale

        Making macros all upper-case helps to identify them as such.

      2. Comments

        Unfortunately, since the same convention is used for naming static const data members, there is the possibility for ambiguity – however, static const data members would usually have to be qualified with the owning class name and the scope resolution operator. Moreover, in C++, the use of macros should be discouraged and uncommon, so they should not be encountered very often.

        In some environments, the practice has been to name macros prefixed with an underscore character in order to better identify them as such. I believe this is a good practice if your code is going to contain a lot of macros.

      3. Status

        Standard

  9. 6.9 - Comments

    When writing comments and documentation, you should always consider who the intended audience will be:

    • other developers who may not be familiar with the given code;
    • other developers who may want or need to interface with the code;
    • new developers who are ramping up on the code; and
    • possibly yourself 18 months after your wrote the code.

    It is a fallacy that the best documentation is good, clean code. The cleanest, most well written, structured code in the world will never be able to tell you why the developer who wrote a given function 3 years ago and left the company 6 months ago chose to use a std::vector instead of an std::map (for example), or what range of values are valid for a given integer function argument. Also, figuring out an algorithm which uses extensive pointer arithmetic (again, for example), in your native (human) language is much faster than figuring it out by reading the code (especially if the only way to know what the pointer points to is by running the code in a debugger – hooray, polymorphism!).

    1. 6.9.1 - Syntax

      Comments may use either the C syntax (/* */) or the C++ syntax (//). However, for consistency, file, class, and function documentation/comment blocks should use the C syntax (though technically, they would use the Doxygen (/** */) syntax). Otherwise, either C or C++ style should be fine.

      1. Rationale

        The use of either comment syntax should not affect the quality of the code (beyond consistency) so it shouldn't matter which style a given developer chooses to use.

      2. Status

        Standard

    2. 6.9.2 - Limit Doxygen Syntax

      Only comments which should be included in the Doxygen documentation should use the Doxygen comment syntax. Comments which are not intended for documentation, for example a comment explaining what a loop is doing, should use the regular C or C++ comment syntax (so that they do not get included in the external documentation).

      1. Rationale

        The purpose of the Doxygen documentation is to document the system and component interfaces and to aid developers (and possibly others) in understanding the system and it's components (classes), not necessarily to explain the internal workings of the functions.

      2. Status

        Standard

    3. 6.9.3 - File Level Doxygen Block

      Every file should start with a Doxygen comment block which should include at least the @file tag; the @copyright tag; and a description of the file's contents.

      1. Rationale

        The description of the file contents might seem redundant because it will almost always just be a sentence stating that the file contains the definition (in the case of header files) or the implementation (in the case of implementation files) of such and such class (which should be able to be inferred from the file name), however the purpose of this comment block is for the auto-generated Doxygen documentation, not for developers to read when they open the file.

      2. Status

        Standard

    4. 6.9.4 - Doxygen @mainpage Block

      The main implementation file of each module/project should contain, near the top of the file, a Doxygen comment block containing the @mainpage tag, with a clear, descriptive explanation of the module, what it does, it's purpose, et cetera. This block should also include information about what dependencies the module has on other modules, which third party tools, utilities, or libraries it uses, and any other information which you may deem relevant or of interest to a new developer who has not worked with the module.

      1. Rationale

        When the Doxygen documentation is generated, the block containing the @mainpage tag goes onto the page which is displayed as the project's home page. This should be considered a starting point for someone not familiar with the code.

      2. Status

        Standard

    5. 6.9.5 - Class Level Doxygen Block

      Every class definition should have an accompanying Doxygen comment block which provides: a brief, one line description of the class and it's purpose; a detailed description which should include any and all information which might be relevant or helpful to a developer who has never worked with that class before; any restrictions, constraints, and pre/post conditions pertaining to the class.

      1. Rationale

        When a developer is trying to determine whether he should use, and possibly extend, an existing class; or whether it would be more appropriate to create a new class, derived from that one; or create a new class, altogether; reading the code of a class cannot help him make that determination because the code does not tell him the original developer's/designer's intentions or assumptions.

        It has become very common for maintainers to add inappropriate functionality to an existing class because they did not understand the original purpose of the class. Over time the class becomes a hodgepodge of unrelated, undocumented functionality.

      2. Status

        Standard

    6. 6.9.6 - Doxygen Commands Start with At-Sign

      All Doxygen commands should start with the at-sign, rather than the backslash.

      1. Rationale

        The backslash character is fairly commonly used for other purposes in C++ code, for example as the escape sequence. Whereas, the at-sign is very uncommon. Using the at-sign to specify Doxygen commands, therefore, makes it much easier to search for such commands.

      2. Status

        Standard

    7. 6.9.7 - Member Variable Doxygen Block

      Every member variable within a class definition should be preceded with a Doxygen comment block explaining, conceptually, what the variable is; it's purpose; any restrictions/constraints that apply to it; and any other information that may be relevant to another developer.

      1. Rationale

        This comment block will be put into the Doxygen documentation so it will be available without having to read through the code. This also frees other developers from having to make (possibly incorrectly) assumptions about constraints.

      2. Status

        Standard

    8. 6.9.8 - Function Level Doxygen Block

      Every function definition (not the declaration) should have an accompanying Doxygen comment block which provides: a brief, one line description of what the function does; a detailed description containing any information which may be relevant to another developer; a @return tag, containing any relevant information about the return value, including valid/expected values, error values, et cetera; a @param tag for each parameter, describing the parameter value, any constraints or restrictions that apply to it, what effect an invalid value would have, et cetera; a @throws tag for each exception the function may throw, including the type of the object thrown, and any relevant information about the exception; @pre and/or @post tags explaining any pre/post conditions which exist for the function.

      1. Rationale

        The goal of the function comment blocks is to provide other developers all the information they may require in order to effectively and reliably call a function without having to resort to reading the code. Reading the body of a function cannot tell him why it was implemented the way it was, or what values are valid for a given parameter. Reading the code also usually requires having to read other code in order to really understand what a function is doing, due to interactions with other classes/functions.

      2. Status

        Standard

    9. 6.9.9 - Overloaded Functions

      Overloaded functions should clearly explain, in the detailed description part of their Doxygen comment block, how they are different from the other implementations.

      1. Rationale

        This helps other developers to easily determine why the function was overloaded. Sometimes, over time, the need for a given overloaded implementation ceases to be beneficial, or it's usefulness diminishes entirely. Such a comment helps to identify those cases so the overload can be deprecated and eventually removed. It also helps other developers to determine whether they should modify an existing overloaded implementation or add a new overloaded implementation to provide some required functionality.

      2. Status

        Standard

    10. 6.9.10 - Non-Obvious Code

      Within a function implementation, any code which may not be immediately obvious to another developer, should be preceded with, at least, a simple sentence saying what it's doing and/or why. Generally, loops and pointer/iterator arithmetic/manipulation, should include such a comment.

      1. Rationale

        It only takes a few seconds to read a sentence in a comment, at the start of a for loop, in order to determine what the loop is doing – it takes a lot longer to infer what the loop is doing by reading the X lines of code that make it up, especially when there are break and/or continue statements within it.

      2. Status

        Standard

    11. 6.9.11 - Deliberate but Possibly Mistaken Code

      Any code which is deliberate but which could be mistaken for being a human error (forgetfulness, oversight), such as an infinite loop (for(;;) or while(true)), or a switch/case fall through should include a comment acknowledging that it is deliberate and why it is used.

      1. Rationale

        Without such a comment other developers will have to assume one of three things, either: the original developer had a good reason, which is beyond the scope of our understanding, in which case we better not mess with it; the original developer did it accidentally – perhaps he did it to test something then forgot to fix it before checking it into the version control system; or, the original developer had no idea what he was doing. Either way, it forces other developers to make unnecessary, and possibly harmful, assumptions about another developer's intentions.

      2. Status

        Standard

    12. 6.9.12 - Be Clear, Specific, and Complete

      Try to write your comments clearly and directly. Avoid using sentence fragments. Be specific.

      1. Rationale

        If the comment is vague or ambiguous it is not very helpful. If it consists of sentence fragments then the reader will have to fill in the missing words with assumptions.

      2. Status

        Standard

    13. 6.9.13 - Temporary Code and Fixes

      Any time a temporary fix or solution is implemented, clearly state so in a Doxygen @todo comment immediately preceding the relevant code. If possible, also try to state why it was done the way it was.

      1. Rationale

        This will help to make sure that the questionable code will eventually be addressed. Without the @todo tag, or some other way to keep track of such issues, they are often forgotten. Stating why the code was implemented in a less-than-ideal way will help to prioritize planning the fix.

      2. Status

        Standard

    14. 6.9.14 - Flagging Questionable Code

      Any time you notice code which seems questionable or less-than-ideal, which does not contain a Doxygen @todo tag, or a comment justifying it's inferiority, you should add a Doxygen @todo tag to it, explaining what you believe is wrong about it.

      1. Rationale

        Without some way to record inferior pieces of code, they will likely be forgotten and never get fixed/improved. The @todo tag makes sure that such code gets added to the list of outstanding issues.

      2. Comments

        Sometimes developers are afraid to flag other's code in this way because they think, maybe, they just don't understand the original developer's reasoning, and questioning it will make them look less competent. Don't fall into this trap! It is arrogant and foolish to try to pretend you know something you don't. A wise engineer (or person, in general), is not afraid to admit they don't know something. That is one of the ways they learn and better themselves - by asking questions of other people who do know.

      3. Status

        Standard

    15. 6.9.15 - Politeness When Flagging Code

      When adding Doxygen @todo tags to pieces of questionable code don't worry about being polite. Make the comment clear and direct.

      1. Rationale

        Being polite often results in statements which fail to express their intended meaning. Finding and fixing inferior code should be more important than worrying about hurting a team member's feelings. Certainly don't be rude about it – just say what needs to be said to express your concern about the code so everyone else is clear on what you're trying to say.

      2. Status

        Standard

    16. 6.9.16 - Identifying Author of Todo Tags

      Doxygen @todo comments should begin with the name of the person who is adding the comment. This should either be the person's first and last name or, at least, first initial and last name.

      1. Rationale

        It is often very helpful, when looking into a given issue which was marked with a @todo tag, to know who entered it. The reader may have further questions about the comment or the code. Having to track down the comment author by looking through version control logs is much more time consuming. Also, sometimes the person that committed the code is not the same person that entered the @todo tag.

      2. Status

        Standard

    17. 6.9.17 - Marking Deprecated Code

      Code which is identified as being deprecated should be marked as such with the Doxygen @deprecated tag. If possible, the comment should include who authorized the deprecation; when it is expected to be officially removed from the code (date or future version); and what, if any, alternatives there are to the deprecated functionality.

      1. Rationale

        This helps future maintainers to easily determine whether, and when, the code in question can be removed/deleted.

      2. Status

        Standard

    18. 6.9.18 - Proximity to Relevant Code

      Comments should be placed as close as possible to the code they are explaining. For example, a comment about a loop should be on the line immediately above the start of the loop; a comment about an expression within a loop should be on the line immediately above the expression.

      1. Rationale

        This decreases the likelihood that the comment will not be kept up to date as the code changes. It also makes it easier to identify what code the comment pertains to.

      2. Status

        Standard

    19. 6.9.19 - Keeping Comments Up To Date

      When you modify any code which has an accompanying comment, the relevant comment(s) should be checked and, if necessary, updated before the modification is considered complete.

      1. Rationale

        Without this practice, comments will become out of sync with the code. Over time, developers stop relying on the comments because they will consider them out of date.

      2. Status

        Standard

    20. 6.9.20 - Memory Management Responsibilities

      Any functions which return a pointer to memory which was allocated within that function, but which the caller is responsible for deleting/freeing, should include a Doxygen @warning message immediately preceding the @return comment.

      1. Rationale

        Without boldly drawing attention to the fact that a function is allocating resources which the caller is expected to release, it is very likely other developers are not going to realize this fact and leaks are going to result. A developer who is calling a given function should not be expected to have to read every line of the function implementation just to use that function.

      2. Comments

        It is generally considered bad practice for an object (or function) to allocate memory then expect the caller to be responsible for releasing it – especially when it is not indicated by the function name. As a rule, your code should not be doing this, anyway, but if it does then you absolutely MUST highlight that fact in the function documentation!

      3. Status

        Standard

  10. 6.10 - Formatting

    Code formatting tends to be a very individual matter and, for the most part, doesn't really affect other developers' productivity or the code quality very much.

    The biggest issue with respect to formatting and developer productivity is trying to get used to formatting your code a certain way as you type it. It can be very distracting.

    Thankfully, most decent, modern text editors are able to easily reformat whole sections of code, or even entire source files, according to very flexible criteria.

    For these reasons, I believe we should not be too stringent with respect to code formatting standards. Only those standards which provide a definite benefit or prevent a definite problem should be imposed on the developers.

    1. 6.10.1 - Line Length

      Generally, lines should not exceed 100 columns.

      1. Rationale

        This is only a general guideline. If a line goes a few characters over 100, or very occasionally there is a line that is way over that (say, 328 characters) it's fine – if you believe it improves readability or maintainability (e.g. a single, long string literal that you don't want to split across multiple lines). Generally, though, a developer shouldn't have to continuously scroll horizontally in his text editor.

      2. Status

        Guideline

    2. 6.10.2 - Non-ASCII Characters

      Non-ASCII characters should not be used in the code, other than in comments.

      1. Rationale

        Most likely, the compiler will not like a variable with a name like שם_משתנה_רע , anyway and compilation will fail. Nevertheless, I have encountered instances where developers have attempted to use non-ASCII characters in variable names.

      2. Comments

        Many development teams, nowadays, are spread across geographic and cultural regions, sometimes with different sub-teams natively speaking different languages. I believe it's preferable for all team members to speak (and write) a single, common language, but sometimes that's just not within our control.

      3. Status

        Standard

    3. 6.10.3 - Amounts of Indentation

      Either tabs or spaces may be used for indentation. But whichever is used, the indentation level should be set to 4 spaces/columns.

      1. Rationale

        As long as tabs are set to 4 columns or 4 spaces are used for each indent level then the code will line up properly in either setting.

      2. Comments

        The question of whether to use tabs of spaces seems to be an issue of heated debate. When you get right down to it, though, it is really nothing more than a matter of personal preference. And I don't believe personal preference should be forced on other developers. As long as the code lines up properly, so it's readable, then it shouldn't matter whether the line begins with 4 tabs or 16 spaces. Chances are, the developer is going to press Ctrl+Arrow, which will jump over all of the contiguous whitespace, anyway. If a developer is actually pressing the arrow key 16 times then you should probably consider replacing him with a developer who actually knows how to use his editor.

      3. Status

        Standard

    4. 6.10.4 - Whitespace

      Use of whitespace to improve readability should generally be left to the developers' discretion. The exceptions are, there should be at least one space: between function parameters in a function call; on either side of binary operators (e.g. +, -, *, /); between each of the parameters of a for loop; et cetera.

      1. Rationale

        Generally, the less whitespace is present, the harder it is to scan the code. When you're writing the code you are intimately familiar with what it's doing so the whitespace may not seem relevant. However, readability is more for the benefit of other developers who will have to read your code later. You should not be thinking in terms of whether or not YOU can read your code, but whether or not OTHER people can read your code. Also, I don't think there has ever been a case of more whitespace making code harder to read.

      2. Status

        Standard

    5. 6.10.5 - When Not to Use Whitespace

      There should not be any spaces around scope resolution or member access operators.

      1. Rationale

        This helps to keep the code consistent and makes it easier to search for scoped symbols in the code.

      2. Status

        Standard

    6. 6.10.6 - Parenthesis in Compound Expressions

      Conditionals consisting of multiple expressions should use additional parenthesis to clarify the grouping and/or order of evaluation.

      1. Rationale

        A developer will be much more productive if he can scan the code rather than having to look at each line closely. For example, if(i * j < k / l) is much more difficult to decipher by scanning, than the following if( (i * j) < (k / l) ). And, even if it is just as easy for you, it is not for most humans.

        Using additional parenthesis, even though it may be redundant, also reassures future maintainers that the order of precedence is deliberate. Otherwise, there is always the question: Did the original developer forget to use parenthesis to change the order of precedence or did he intend to use the default ordering?

      2. Status

        Standard

7 - Version Control

This section describes standards relating to the use of a version control system. This section does not attempt to prescribe a a particular version control system, nor does it attempt to address the pros and cons of various version control systems. The standards presented in this section should be applicable to all major version control systems.

In my own projects I use Subversion. I have used git fairly extensively on other, third party projects, and I did, at one point, migrate all of my personal projects from SVN to git. But after using git on my personal projects for a few months I made the very deliberate decision to migrate back to SVN. A discussion of why is, perhaps, a topic for a future blog post.

  1. 7.1 - General

    1. 7.1.1 - Commit By Same Developer

      Only the developer who actually made, and understands, a given change should commit that change to the repository. One developer should not perform a commit on behalf of another.

      1. Rationale

        It is important to be able determine exactly who committed changes, so that if we have questions about it we know who to address them to. It is also very helpful in the case of a developer who is no longer on the team – we then know there may be no way of getting additional information about the change. In the case when one developer does need to commit a change on behalf of another developer (for example, developer 'A' is out sick and the change needs to get committed as soon as possible) then that should be stated in the commit comment.

      2. Comments

        It may sound harsh, but one reality of management is that they do need to be able to track things such as the number of errors being introduced by each team member. Usually this statistic will not be critical, but if it is found that 80% of the errors are being introduced by 1 person then that is a "human resources" issue that would need to be addressed appropriately. And, the best way to be able to determine such as situation is to see who has been checking in the buggy code.

      3. Status

        Standard

    2. 7.1.2 - Clear, Complete Comments

      Every commit should include a comment which clearly describes the changes that were made and, if it is not immediately clear, why they were made.

      1. Rationale

        This is not intended to replace or duplicate the documentation/comments in the code – this should just be a short sentence or two so that we can find a given change by looking at the repository history logs rather than having to actually diff each revision of the file.

      2. Comments

        I have worked on some projects where the practice was to link each commit with a specific ticket in the issue tracking system. Then, from the commit, you could go to the ticket and get further information about the issue, the solution, et cetera. I believe that is a very good practice, though the commit comment should still contain sufficient information for a reviewer to determine what was changed and why.

      3. Status

        Standard

    3. 7.1.3 - One Issue Per Commit

      Each commit should be limited to an individual issue and should include all changes related to that issue.

      1. Rationale

        This makes it easier to find a specific change in the repository history, and to back-out a given change, if needed.

      2. Comments

        Sometimes we may accidentally forget to include a file or two in a given commit. It happens. When this occurs, the comment for the second commit should include an explanation that the files should have been included with the previous commit.

      3. Status

        Standard

    4. 7.1.4 - Refactoring Changes

      Changes related to refactoring, code cleanup, et cetera, should be committed separately from changes which are intended to change the logic of the code.

      1. Rationale

        Sometimes refactoring and general code improvement changes do introduce new errors. By keeping those changes separate from intended logic changes it makes them easier to locate and back-out without having to also back-out the logic changes (then having to redo those logic changes).

      2. Comments

        In reality, this is not always as easy as it might sound. Sometimes, when you're investigating a given issue you need to do some cleanup of the existing code simply because it is too sloppy or poorly implemented, which makes the code much more complicated than it needs to be. Sometimes, in the process of cleaning up the code before trying to figure out the problem, you inadvertently fix the problem. In such cases, it would not be practical to keep the fix and the refactoring separate.

        Further to the previous paragraph, it should be emphasized, even though cleaning up the code might have inadvertently resolved the problem, the developer should still take the time to understand what the problem was and how it was fixed. Lest, we find out three months down the road, that the code cleanup didn't actually fix the problem after all. As an example, let's say the problem was a synchronization issue - a race condition, perhaps - and let's say the cleanup of the code caused a specific few lines of code to take a few extra clock cycles to execute, which changed the timing of the sequence of events, which simply caused the problem to stop manifesting. The problem is still there - it's just not presenting itself in the same way.

      3. Status

        Standard

    5. 7.1.5 - Build Artifacts

      Build output files, such as .obj, build target files, and Doxygen generated documentation, should not be kept in the repository.

      1. Rationale

        The purpose of a version control system is to be able to keep track of, and rollback, if necessary, changes to the source files and other files used to generate the output files. If the output files are kept in the repository then they would have to be updated every time a developer builds.

      2. Status

        Standard

    6. 7.1.6 - Database Dumps

      7.1.6 Database dump files should not be stored in the repository.

      1. Rationale

        The purpose of a version control system is to track the changes to files over time and to be able to revert changes, if necessary. A database dump does not change over time - it is a one time snapshot of the state of the database at a specific point in time. Also, database dumps can get very large and they will unnecessarily bloat the size of the repository. It is better to store database bumps on a shared file server if the intent is to make the dump available to the team.

      2. Status

        Standard

8 - Database

This section describes the practices which should be followed with respect to database development, and administration in a development environment.

Note that this section is only concerned with development databases, not production or QA environments.

  1. 8.1 - General

    1. 8.1.1 - Standard Language

      Whenever possible, ANSI standard SQL should be used in scripts, instead of proprietary extensions.

      1. Rationale

        The use of proprietary or vendor-specific extensions will make the SQL less portable, in the event we decide to use or support a different database management system.

        Most systems are designed with a particular database management system in mind, but experience has taught that those types of decisions often change over time. We should not unnecessarily lock ourselves into a given database product.

      2. Comments

        I believe the acceptable exceptions to this rule are when the use of proprietary extensions will provide a specific and measurable benefit - particularly with respect to performance and/or scalability.

      3. Status

        Standard

    2. 8.1.2 - Schema Creation Scripts

      A SQL script, automating the creation of the database schema, should be kept up to date and available in the version control repository. When changes are made to the schema they should also, immediately, be made to the script.

      1. Rationale

        We should be able to quickly and easily recreate the database on any developer workstation or test server. Without an up to date schema creation script we would have to go to another database instance, perform an export of it's database, then import that on the other host. That is much more time consuming and error prone. Also, we can't be certain the schema on the host we're copying from is actually up to date.

        More importantly, by maintaining a schema creation script, we have a single, easily maintainable location for generating Doxygen documentation comments for the tables, columns, stored procedures, et cetera.

      2. Status

        Standard

    3. 8.1.3 - Designated Database Lead

      Specific team members should be designated as authorized to implement schema changes. Only specific, designated team members with sufficient database knowledge and experience should be authorized to approve changes to the database schema. This person would usually be the team lead or lead developer.

      1. Rationale

        Most developers do not have extensive knowledge of database schema design or optimization techniques. Allowing all developers to make changes to the schema can result in many small, unnoticed changes which gradually degrade the performance of the database, or worse, break the interfaces between the source code and the database.

        It is also an unfortunate reality that many developers are very undisciplined when it comes to documenting their work and the changes they make. Since changes to the database schema in a running database instance is not automatically updated in the version control system, it is critical for such changes to be recorded in the database creation script. My experience has been, we cannot rely on many developers to keep that script up to date.

      2. Comments

        I recently worked on a project where one of the team members was a very junior developer who had recently gotten his Masters Degree in Computer Science. He had written his thesis on something related to data mining. You would assume, from that, that he had a good understanding of relational database technologies. That would be an incredibly wrong assumption! That developer frequently made bad changes to the database without letting anyone know and without recording them. He is the inspiration for this standard.

      3. Status

        Standard

9 - Other Practices

This section shall contain other, miscellaneous standards and guidelines which do not fit into any of the previous categories. Currently, no such miscellaneous standards exist.

Appendix A – Variable Name Prefix System

This section lists the prefixes which should be used in variable names, to aid developers in reading and understanding the code without having to jump around, trying to determine what type of data the variable represents or holds. The use of consistent type prefixing also makes it easier for developers to scan through code when they're looking for something, rather than having to read it line by line.

A variable name prefix should have three parts. The first part is used to indicate whether the variable is a member of a class, static, global, or local. The second part indicates whether the variable is a pointer, reference, or value. The third part indicates the type of data the variable refers to or holds. The parts must be prefixed in that order, otherwise ambiguity may arise.

Since the intention of the variable prefixes is to aid developers in quickly understanding the code, their use may not be considered beneficial by some. You should consider, though, when you're reading common text, such as you're doing right now, when you look at a common, well known word - for example, the word "the" - your brain immediately recognizes it and moves on to the next word; you don't need to contemplate it or give it a second thought. That is the same intention behind the motivation for using common, consistent variable name prefixes. As soon as the developer's brain see's a symbol which begins with "m_str" it will immediately identify it as a string which is a member of the current object; when it see's "g_idx" it immediately identifies it as a global variable which is an index (and since it is a global variable it also alerts the developer that someone has written some bad code!).

  1. Scope

    The scope indicator consists of a single character, followed by an underscore. The underscore is used to separate the indicator from the rest of the prefix.

    g_ Global variable. This should not be used very often because ideally, there will not be any global variables.
    m_ Member variable. The variable is a member of the current class. Since all data members should be private (or occasionally protected) it would not be possible for it to be a member of another class. Part of the purpose of this prefix is also to decrease the chance of a name collision (resulting in name hiding) with a local variable.
    s_ Static variable. In this case static means static to the compilation unit – not a static class member. It is unlikely there will be many static global variables because they are not commonly used in C++ (as opposed to C). There is no special indicator necessary for a static data member of a class – it's still a member (so it uses the m_, regardless of the fact that there's a single instance shared by all of the class instances).
    blank Local variable. A local variable should not have any scope prefix. So, if we encounter a variable without a scope prefix we can assume it is local to the current function (including function arguments).
  2. Variable Type

    The variable type indicator consists of a single character which immediately follows the underscore of the scope indicator; or is the first character, in the case of local variables. Note that these indicators refer to the variable type, not the data type. That is, whether it's a pointer, reference, or otherwise (plain old variable).

    a Array. The variable refers to an array. This is used in conjunction with the data type indicator to show that it's an array of some type of data. Also, arrays are unique in that in some ways they are treated like pointers and in other ways they are treated like regular variables.
    p Pointer. The variable is a pointer to some other, actual variable. Since we can have pointers to pointers, ad infinitum, then we should use a p character for each level of indirection. The pointer indicator is important to use, because it easily informs the developer that they must remember to do null checks and other error handling for each appropriate level of indirection.
    r Reference. The variable is a reference to some other, actual variable. It's important to use the reference indicator to make it clear within a function that the data referred to is not necessarily local to that function and changing it may have affects outside the function. Also, null checks will not be required.
    blank Regular (non-pointer, non-reference) variable. A variable name not prefixed with the r or p (as it's first or third character) can be safely assumed to be a regular variable and changing the value of it's data will be limited to that single instance.
  3. Data Type

    The following prefixes are used to indicate the type of data the variable contains or refers to. The goal here is not necessarily to state the C/C++ type, but rather, the higher level, conceptual type. For example, by prefixing str we're saying the variable is a string, whether it's an STL std::string, a char array, or a char* (though, technically, that should be a pstr); by prefixing n we're saying it's a general purpose whole number, whether it be an int, a long, an int8_t, et cetera. The data type prefixes are generally one to three characters in length.

    If there are certain classes, or types of data are used frequently, within the project we can come up with project specific data type prefixes for those. It is preferable such prefixes be discovered early on, though, so that a lot of refactoring will not be required.

    A note about wide and narrow characters/strings: Generally, I don't use name prefixes to distinguish between wide and narrow characters. In part because I believe all strings should be implemented as wide character strings, internally and should be converted to/from UTF-8 when exiting/entering the process. On some projects, however, which are in the process of moving from narrow strings to wide strings, or where both types are being used internally, it might make sense to use a different prefix for wide and for narrow strings.

    b Byte. This is usually used to indicate that a buffer contains arbitrary data. For example, when reading data from a socket. The data might be structured or not, but at this point it's specific structure is either not known or not relevant.
    bl Boolean.
    db Database connection or database object.
    c Character. Just an individual character – not a null terminated string.
    cln Collection. This can include any of the standard STL container types. Usually, the exact container type (e.g. std::map, std::list, std::vector) is not relevant or significant to the variable use, and so cln should be used.
    cnt Counter. Typically an integer/long type, used for performing some type of monitoring.
    e Enumeration.
    idx Index. It can be assumed that an index is some integral type, whether short, int, long, et cetera. This is usually an index into an array, or some similar thing.
    id Identifier. Usually, though not always, a numeric value which uniquely identifies something.
    it Iterator. Typically, an iterator for an STL collection.
    lst List. Used instead of cln when the fact that the collection is implemented as a list is significant.
    map Map. Not necessarily a map, proper, but any collection ordered on a key. Used instead of cln when the fact that the collection is implemented as a map is significant.
    msg A structured message object. Typically for the messages which are used to communicate between different processes, namely between a client and a server.
    mtx Mutual exclusion lock – whether mutex, semaphore, critical section, et cetera.
    n General purpose numeric data.
    qry Database query or query related object.
    row Row from a result/record set (database).
    rst Result set or record set (from the database).
    soc Socket. Whether a simple file descriptor associated with a socket or a complex class representing a socket.
    str Character string. Whether a wchar_t[]/char[] or a std::wstring/std::string.
    stm Any of the stream related objects. Typically an STL stream.
    thr Thread. Usually used to indicate a Thread or Thread related object, whether a simple pthread_t, or a higher level thread class.
    tm Timer or time related value.
    usr User related information/object. Many systems will have some type of abstraction to represent a user, or a real world person.

Appendix B – Accepted Abbreviations

This section lists the abbreviations and acronyms which should be acceptable for use in the code, and their respective definitions. New abbreviations may be added to this list if they would help to improve the code.

addr Address; typically meaning a network or IP address, though it may also mean a street address.
msg Message; typically a message sent between processes.
req Request; typically a request message.
resp Response; typically a response message.