Skip to content
Vladlen edited this page Feb 16, 2021 · 5 revisions

Table Of Content

    1. General Naming Conventions
    2. File Names
    3. Type Names
    4. Variable Names
    5. Constant Names
    6. Function Names
    7. Namespace Names
    8. Macro Names
    1. Visual Studio IDE 2019 Settings
    2. Non Auto Format Cases
    1. Order of Includes
    2. Namespaces
    3. Functions Definitions
    4. Separation Rules
    1. Include Guard
    2. Namespaces
    3. Class Declaration
    1. Order of Includes
    2. Namespaces
    1. Variable Comments
    2. Implementation Comments
    3. Function Argument Comments
    4. TODO Comments

  1. Naming Conventions

    1. General Naming Conventions

      1. All names should be written in English.
      2. Use names that describe the purpose or intent of the object
      3. Name lengths should be proportional to the length of the name's scope
      4. Optimize for readability using names that would be clear even to people on a different team
      5. Minimize the use of abbreviations that would likely be unknown to someone outside your project
      6. As a rule of thumb, an abbreviation is probably OK if it's listed in Wikipedia
      7. Do not abbreviate by deleting letters within a word
      8. Do not worry about saving horizontal space as it is far more important to make your code immediately understandable by a new reader
    2. File Names

      1. Source Files

        • Start with a capital letter and have a capital letter for each new word, with no underscores PascalCase
        • Header files should have the extension .h
        • Source files should have the extension .cpp
          MyUsefulClass.h
          MyUsefulClass.cpp
          
      2. Other Files

        • Should be all lowercase with underscores between words snake_case
        • Can be exceptions like CMakeLists.txt etc
          downloads.bat
          build_depends_msvc16_debug.bat
          CMakeLists.txt
          README.md
          
    3. Type Names

      1. Start with a capital letter and have a capital letter for each new word, with no underscores PascalCase
      2. The names of all types — classes, structs, type aliases, and template parameters — have the same naming convention.
        class SomeClass;
        struct SomeStruct;
        typedef TypedefSomeClass SomeClass;
      3. Type enum must be named with a leading E
        enum class EHotSpotEventFlag;
      4. Type struct that describe the data in themselves must be named with Desc postfix
        struct FontDesc
        {
            ConstString fontName;
            uint32_t fontSize;
            Color fontColor;
        };
    4. Variable Names

      1. Common Variable names

        • Must be in mixed case starting with lower case camelCase
        String tableName;
      2. Class Data Members

        • Prefix m_ for non-static members
        • Prefix s_ for static nonconstant members
          class SomeClass
          {
          public:
              static const int32_t MAX_COUNTER;
          
          private:
              static int32_t s_staticCounter;
          
          private:
              int32_t m_someCounter;
          };
      3. Struct Data Members

        • Must be named like ordinary nonmember variables
      4. Function Arguments

        • Must be prefixed by single underscore
          int32_t setNumber( int32_t _number );
    5. Constant Names

      1. In function scope must be named like ordinary variables
        const int32_t daysInAWeek = 7;
      2. In other scope, must be named with all uppercase using underscore to separate words
        const int32_t MIN_COUNTER = 0;
        static const int32_t MAX_COUNTER = 10;
    6. Function Names

      1. Must be verbs and written in mixed case starting with lower case camelCase
      2. Accessors and mutators must be named with prefix get/set following by variable name
        class SomeClass
        {
        public:
            int32_t setNumber( int32_t _number );
            void getNumber() const;
        
        private:
            int32_t m_number;
        }
      3. Class internal callbacks must be prefixed by single underscore (mvi pattern)
        class SomeClass
        {
        public:
            void initialize();
        
        protected:
            virtual void _onInitialize();
        };
      4. Class non virtual protected/private methods must be sufixed by single underscore
        class SomeWindow
        {
        public:
            void setupSomeWindow();
        
        protected:
            void setupLabels_();
            void setupButtons_();
        };
    7. Namespace Names

      • Start with a capital letter and have a capital letter for each new word, with no underscores PascalCase
        namespace Mengine
        {
        
        };
    8. Macro Names

      • They should be named with all capitals and underscores
        #define ROUND(x) ...
        #define PI_ROUNDED 3.0
        #define MY_MACRO_THAT_SCARES_SMALL_CHILDREN_AND_ADULTS

  1. Formatting

    1. Visual Studio IDE 2019 Settings

      • Download environment settings, or copy from Mengine folder
      • Import in Tools -> Import and Export Settings...
      • Before merger request, please, do Ctrl + A -> Alt + R in your modified files for auto format purpose
    2. Non Auto Format Cases

      1. Do not add spaces inside <...> brackets
        // bad
        typedef Vector< String > VectorExample;
        // good
        typedef Vector<String> VectorExample;

  1. General For Source Files

    1. Order of Includes

      1. Include statements must be located at the top of a file only
      2. Include statements should be grouped and sorted in this order:
        #include "Interface/..."
        #include "Interface/..."
        
        #include "Plugins/..."
        #include "Plugins/..."
        
        #include "SomeClass.h"
        #include "SomeService.h"
        
        #include "Engine/..."
        
        #include "Kernel/..."
        #include "Kernel/..."
        
        #include "Config/..."
        
        #include <dependencies>
        
        #include <stl>
        
        #include <cstyle.h>
    2. Namespaces

      • Use namespace Mengine by default
        namespace Mengine
        {
        
        };
    3. Functions Definitions

      1. Follow the fast return rule
        void SomeClass::computeHeavyEquation()
        {
            if( isSomethingWentWrong == true )
            {
                // fast return
                return;
            }
        
            /*
                ...
            */
        }
      2. Compare boolean explicitly with true/false
        void SomeClass::computeHeavyEquation()
        {
            // boolean explicitly compared with 'true'
            if( isSomethingWentWrong == true )
            {
                return;
            }
        
            /*
                ...
            */
        }
      3. Add explicit this pointer for member function calling
        void SomeClass::computeHeavyEquation()
        {
            this->computeFirstEquation();
            this->computeSecondEquation();
            this->computeThirdEquation();
        }
      4. Add parent class namespace without this pointer for overridden functions calling
        void SomeClass::_onIntialize()
        {
            // 'Super' is only example, use real parent class name instead
            Super::_onIntialize();
        
            /*
                ....
            */
        }
      5. Use local constants for all not tunable magic numbers
      6. Move tunable constans to .cpp file scope as constexpr
        namespace Mengine
        {
            //////////////////////////////////////////////////////////////////////////
            // tunable constant in .cpp scope
            constexpr float WINDOW_RELATIVE_WIDTH = 0.3f;
            //////////////////////////////////////////////////////////////////////////
            void Window::setupWindow()
            {
                // not tunable constant in method scope
                const Char * clearBtnLabel = "Clear"; 
        
                /*
                ....
                */
            }
        };
      7. Remove complex calculations and function calls from function arguments
        void Window::setupWindow()
        {
            // bad
            m_window->setButtonLabel( this->getButtonLabelFromCongfig() );
        
            // good 
            const String & btnLabel = this->getButtonLabelFromCongfig(); 
            m_window->setButtonLabel( btnLabel );
        
            /*
            ....
            */
        }
        };
      8. Use only nullptr for pointers instead of NULL
      9. Define type alias for templates before using of templates only through typedef specifier
        //////////////////////////////////////////////////////////////////////////
        typedef Map<String, class SomeClass> MapSomeClasses;
        typedef Vector<class SomeClass> VectorSomeClasses;
        //////////////////////////////////////////////////////////////////////////
      10. Define alias template only through using specifier
        //////////////////////////////////////////////////////////////////////////
        template<class CharT>
        using MyString = std::basic_string<CharT, std::char_traits<CharT>>;
        //////////////////////////////////////////////////////////////////////////
        void someFunc()
        {
            MyString<char> str;
        
            /*
            ...
            */
        };
      11. Use auto only with local lambdas and in range-based for loop
        void someFunc()
        {
            auto someLambda = [](){
                /*
                    ...
                */
            };
        
            for( auto && [key, value] : someMap )
            {
        
            }
        };
      12. If func need to return some large object as result pass it as _out parameter by const pointer
        void someFunc( VectorLargeData * const _out )
        {
            for( ... )
            {
                _out->...
            }
        };
    4. Separation Rules

      1. Use separator:
        //////////////////////////////////////////////////////////////////////////
        
      2. Put a separator just before some entity.
        // SomeClass.h
        namespace Mengine
        {
            //////////////////////////////////////////////////////////////////////////
            class SomeClass
            {
            public:
                SomeClass();
                ~SomeClass();
            };
        };
        // SomeClass.cpp
        namespace Mengine
        {
            //////////////////////////////////////////////////////////////////////////
            namespace Detail
            {
                //////////////////////////////////////////////////////////////////////////
                static void serviceMethod()
                {
                };
            };
            //////////////////////////////////////////////////////////////////////////
            SomeClass::SomeClass()
            {
            };
            //////////////////////////////////////////////////////////////////////////
            SomeClass::~SomeClass()
            {
        
            };
            //////////////////////////////////////////////////////////////////////////
        };
      3. Wrap typedef/define or related typedefs/defines groups with a separator.
        namespace Mengine
        {
            //////////////////////////////////////////////////////////////////////////
            typedef Map<String, class SomeClass> MapSomeClasses;
            typedef Vector<class SomeClass> VectorSomeClasses;
            //////////////////////////////////////////////////////////////////////////
            class SomeClass
                : public Factorable
            {
            public:
                SomeClass();
                ~SomeClass() override;
        
            public:
                static SomeClass * getInstance();
            };
            //////////////////////////////////////////////////////////////////////////
            typedef IntrusivePtr<SomeClass> SomeClassPtr;
            //////////////////////////////////////////////////////////////////////////
        };
        //////////////////////////////////////////////////////////////////////////
        #define SOME_SINGLETON (Mengine::SomeClass::getInstance())
        //////////////////////////////////////////////////////////////////////////

  1. Header Files

    1. Include Guard

      #pragma once
    2. Namespaces

      • Use namespace Helper in top of parent namespace for class service functions declarations
        namespace Mengine
        {
            //////////////////////////////////////////////////////////////////////////
            typedef IntrusivePtr<class Sprite> SpritePtr;
            //////////////////////////////////////////////////////////////////////////
            namespace Helper
            {
                //////////////////////////////////////////////////////////////////////////
                SpritePtr createSpriteFromFile(...);
                //////////////////////////////////////////////////////////////////////////
                SpritePtr createSpriteFromTexture(...);
            };
            //////////////////////////////////////////////////////////////////////////
            class Sprite
            {
                /*
                    ...
                */
            };
        };
    3. Class Declaration

      1. Should be declared in a header file where the name of the files match the name of the class
      2. All declarations should be in this order:
        • constructors
        • destructor
        • setters|getters methods
        • initializing methods
        • public virtual/non-virtual methods (class interface)
        • protected virtual/non-virtual methods
        • private virtual/non-virtual methods
        • attributes
      3. Class must contains constructor/destructor declarations without = default or = delete
      4. All definitions should reside in source files
      5. Visibility modifier should be used like separator for class member groups
        class SomeClass
        {
        public:
            SomeClass();
            ~SomeClass();
        
        public:
            int32_t setNumber( int32_t _number );
            void getNumber() const;
        
        public:
            void initialize();
        
        protected:
            virtual _onIntialize();
        
        private:
            int32_t m_number;
        };
      6. One header - one class declaration

  1. Source Files

    1. Order of Includes

      • The first #include must contains related header file of this source file
      • The rest is exactly the same as in General For Source Files
    2. Namespaces

      • Use namespace Detail in top of parent namespace for service entities/functions which don't go beyond class definition
        • In namespace Detail all functions must be static
        namespace Mengine
        {
            //////////////////////////////////////////////////////////////////////////
            namespace Detail
            {
                //////////////////////////////////////////////////////////////////////////
                static ComputeResult computeSomething()
                {
        
                };
            };
        };

  1. Comments

    1. Variable Comments

      • In general the actual name of the variable should be descriptive enough to give a good idea of what the variable is used for
    2. Implementation Comments

      • In your implementation you should have comments in tricky, non-obvious, interesting, or important parts of your code
    3. Function Argument Comments

      • When the meaning of a function argument is nonobvious, consider one of the following remedies:
        1. If the argument is a literal constant, and the same constant is used in multiple function calls in a way that tacitly assumes they're the same, you should use a named constant
        2. For functions that have several configuration options, consider defining a single struct to hold all the options, and pass an instance of that
        3. Replace large or complex nested expressions with named variables
    4. TODO Comments

      1. Use TODO comments for code that is temporary, a short-term solution, or good-enough but not perfect
      2. TODOs should include the string TODO in all caps
      3. TODOs must followed by the name, bug ID, jira task or other identifier of the person or issue with the best context about the problem referenced by the TODO
      // TODO(Jira task) some 'to do' description.