Skip to content

NamingConventions

David Jewsbury edited this page Jan 27, 2015 · 2 revisions

#NamingConventions

Prefer to follow the following rules for naming identifiers.

Identifier type Example Naming
Functions and methods DoSomething() Camel case, begin with a capital. This applies equally to methods, global "free" functions and file scope static functions
Classes, structs and namespaces class FooBar Camel case, begin with a capital
Typedefs and template renames typedef ... RenamedType Camel case, begin with a capital
Member variables _myName Begin with an underscore, and then a lower case letter
Function-scope variables and function parameters someVariable Camel case, begin with a lower case letter
Static member variables s_instance Begin with a lower case 's', then an underscore, and then a lower case letter
Global-scope variables and file-scope static variables g_theWorldManager Begin with a lower case 'g', then an underscore, and then a lower case letter.

##Examples

The following is well formatted code:

#include <algorithm>

namespace Examples { namespace Naming
{
  /// <summary>Typical XLE class</summary>
  /// This is an example of what a typical XLE class
  /// might look like
  /// <example>
  ///     <code>\code
  ///         XWing wingLeader;
  ///         for (auto i=0u; i < XWing::GetFoilCount(); ++i) {
  ///             wingLeader.OpenFoils(i);
  ///         }
  ///         wingLeader.CommenceStrafingRun();
  ///     \endcode</code>
  /// </example>
  class XWing
  {
  public:
    const std::string& GetSquadName() const never_throws { return _squadName; }
    void OpenFoils(unsigned foilIndex);
    void CommenceStrafingRun();

    static unsigned GetFoilCount() { return _foilCount; }

    XWing(const char squadName[]);
    ~XWing();

  private:
    std::string _squadName;

    struct FoilState
    {
      enum Enum { Closed, Open };
    };
    static const unsigned s_foilCount = 4;
    FoilState::Enum _foilState[s_foilCount];
  };

  inline XWing::XWing(const char squadName[])
  : _squadName(squadName)
  {
    std::fill(_foilState, &_foilState[dimof(_foilState)], FoilState::Closed);
  }
}}

The following example contains mistakes and incorrect formatting:

namespace BadExamples
{
    // Use a capital letter for a class identifier
  class something {};   // BAD!

    // Avoid identifiers that look like they are something else.
    // This is a class, but it's using the naming convention for a member viable
  class _itLooksLikeAMember {};  // BAD!

    // Global scope variable should begin with "g_"
    // Readers will be expecting us to follow the standards,
    // so violations are particularly confusing.
  _itLooksLikeAMember whatsGoingOn;  // BAD!

  class SomeClass // OK
  {
  public:
      // Underscore followed by a capital letter is reserved by the language standard.
      // Use a lower case letter after the underscore
    int _BadlyFormed;  // BAD
  };
}

##Rationale

Generally I prefer to avoid too many rules and restrictions for naming and layout conventions. However, there are some good advantages to this scheme.

###Clear member variable distinction

It's handy to distinguish between member variables and local function-scope variables. It can often make a complex method much easier to understand.

This is particularly useful in constructors, where often we want to initialise a member with the same name as a parameter.

Prepending a underscore is the least intrusive way to clearly distinguish member variables. However, if we begin an identifier with an underscore, the next letter should be a lower case letter.

More specifically, the second character must not be a upper case letter, or another underscore. Both these cases are reserved by the language standard.

###Clearly identify static member variables and globals

I find it useful to be particularly explicit when using static member variables, globals and file scope static variables. Usually we don't want to confuse these with local scope variables and normal member variables. We don't use them frequently, so let's just be explicit about it.

###Exceptional cases

In rare cases, some exceptions are made

  • some classes and functions are designed to replace, or "feel like" similar classes in the standard library
  • An example is class intrusive_ptr. This is designed to match the behaviour of std::shared_ptr and std::unique_ptr. Therefore, it uses similar naming conventions
  • There are also some macro #defines that are intended to behave like language keywords
  • Some examples are: never_throws, dll_export, thread_local, dimof
  • These follow the naming conventions of C++ keywords to better fit in with their purposes. Syntax highlighting and our knowledge of the language should make it adequately clear that they extensions to the language
  • In some cases (eg, thread_local) these can be macros under some compilers, but built in keywords under others
  • Some classes may need to meet the template requirements for compatibility with standard library types. A custom allocator is a good example. In these cases, we're forced to use the standard library naming conventions (eg, for a value_type typedef, or allocate() method)
  • Sometimes we need to make exceptions in order build certain meta-programming constructs, or some reusable template interface.

##Other cases

Normally template parameters should use the same formatting as classes and typedefs (both for typename and scalar type parameters).

template <typename Type, int Count>
  Type SomeFunction();