C/C++ Style GuideVersion: 1.1

C++ Style Guide

1. Introduction 

    This document contains the coding standard of Graphisoft, which mainly sets the formal requirements for writing source code. It also describes a few rules and recommendations for the content. You have to obey these rules; by default every declarative or imperative sentence is a rule, except where 'recommendation' (or avoidable, optional, etc.) is explicitly stated.
     
     

2. Naming conventions
 

    Using prefixes besides the ones allowed below is forbidden.
    Using underscores (_) is not forbidden, but please avoid it if possible. It cannot be used as the first or last character of a name.
    Standard C function names (e.g. floor, abs) shouldn't be used as names.
     
  1. #define names
      Should be written in capitals; names consisting of more than one word should be separated by underscores (_).
      E.g.: NULL, PLATFORM, DEBUG_LEVEL_2, REFERENCE_CHECK
      NULL should be used to denote 0 pointers.
       
  2. Type names (struct, enum, typedef, class, namespace, template parameter)
      Starts with an uppercase letter; every new word should also start with an uppercase letter. For interface names the "I" prefix is compulsory.
      E.g.: Vector2D, Array, SymbolSet, Char, IComparable.
       
  3. Variable names
      Starts with a lowercase letter; every new word should also start with an uppercase letter. For member variables the "m" prefix (e.g.: mLength), for globals the "g" prefix (e.g.: gWindowCount) is allowed. Globals can be also indicated with the :: global scope.
      E.g.: size, isDeletable, newFont.
       
  4. Constants (const, enum)
      Starts with an uppercase letter; every new word should also start with an uppercase letter. Names of enums defined in the common scope should differ, so the use of prefixes is allowed here.
      E.g.: Underscore, DefaultSize.
       _
  5. Function names
      Starts with an uppercase letter; every new word should also start with an uppercase letter.
      E.g.: Print, SetPort.
       
  6. Method names
      Starts with an uppercase letter; every new word should also start with an uppercase letter.
      Preferably you should use verbs or structures containing a verb for names (e.g. verb + object). E.g.: Move, FindLast, AppendName, DeleteIcon.
      The verbs Set, Get, and Has should be used in method names setting and retrieving attribute information. E.g. SetSize, GetSize, IsEmpty, HasIcon.
      Conversion methods should have names starting with the 'To' prefix, and without verbs. E.g.: ToString, ToDouble.
       
  7. Parameter names
      For input, output or input/output parameters it is allowed to use the in/out/io prefixes (e.g.: inString, outText, ioLength).
       
    Acronyms are an exception to the capitalization rule, e.g. ISO_Standard, where ISO is written in all caps.
     

3. Expressions
 

  • Space should be used in front of and behind the following binary operators:
    • arithmetical: *, /, %, +, -, <<, >>
    • logical: &&, ||, ==, !=, <, <=, >, >=
    • bit manipulating: &, ^, |
    • assignment: =, *=, /=, %=, +=, -=, <<=, >>=, &=, |=, ^=
       
    E.g.:
      a = b + c % d;
      x += y;
      double r = 25 * b / 2 + 4;
       
  • No spaces are allowed in front of and behind the following binary operators:
    • Scope resolution: className::member
    • Member selection: object.member, pointer->member, object.*member, pointer->*member
     
  • No spaces are allowed in front of and behind the following unary operators:
    • pre, post / increment, decrement: ++i, --i, i++, i--
    • subscripting: array[25]
    • complement: ~x
    • logical not: !x
    • unary minus, unary plus: -x, +x
    • address of: &var
    • dereference: *p
    • global scope: ::globVar
       
  • Space should be used in front of and behind the following (special) unary operators:
    • function calls: SetItem (25, item)
    • casting: (char*) p
    • run-time type identification: typeid (Vector)
    • size of object, size of type: sizeof (*v), sizeof (Vector)
    • allocation, deallocation: new type, delete p, delete [] p
    • exception throwing: throw Exception
     
  • Function calls should have a space in front of the opening parenthesis of the parameter list, and behind every comma separating the parameters:
     
      sin (PI / 2);
      double average = Average (i, 25 * j, 45);
      Stop ();
     
  • When comparing with constants (e.g. i == 5) the constant should be the second operand.
     
  • In conditional expressions the condition should be parenthesized, placing a space in front of and behind the '?' and the ':' operators:
     
      long val = (a < 5) ? 0 : a * 2;
       
    Avoid embedding such conditional statements if possible.
     
  • Put a space behind the comma (sequencing) operator:
     
      for (int i = 0; i < 10; i++, j++)
       
    Please don't overuse the comma operator.
     
  • Casting in C++:
     
      p = static_cast<Vector*> (pVec);

      Vector& v = dynamic_cast<Vector&> (*pVec);
       

    You are not allowed to use C-style casting in C++ code.
     
  • C-style casting (can be used only in C code, or C code compiled as C++):
     
      pc = (char*) p;
      pc = (int*) (q->c + 3);
     
  • When assigning values to bool variables the logical expression should be parenthesized:
     
      bool b = (i > j);
       
  • Don't use automatic bool conversion; write the conditions explicitly in the code:
     
      unsigned int i = f ();
      if (i != 0) {    // instead of: if (i)
          ...
      }

      char* p = g ();
      if (p != NULL) {    // instead of: if (p)
          ...
      }
       

      Also, do not use the bool result of logical negation. E.g. instead of !i please use i == 0, and instead of !p please use the p == NULL condition.
      (Of course, bool variables and expressions can be negated, e.g. !b).

     
  • Do not compare bool variables and expressions to true or false; use the value of bool or its negated value:
     
      bool b = true;
      if (b) {    // instead of: if (b == true)
          ...
      }

      if (!b) {    // instead of: if (b == false)
          ...
      }

     
  • Logical expressions consisting of many parts should be placed on multiple lines, and you should also align the sub-expressions or the logical operators. Also leave an empty line in front of the body:
     
      if ((a > 2 && b < 1)   ||
          (c > 10 && d == 0) ||
          (e == -5)          ||
          (f < 5 && f > 1))
      {
          // body
      }
       
    Complex expressions (e.g. where && and || are both present) should be parenthesized, so that the precedence should always be clear.
     
  • You can use chain assignment only for closely related variables of the same type (e.g.: i = j = 0).
     
  • Put parentheses around rarely used operator combinations.
     
     

4. Control flow statements
 

  1. if-else
     
    • if (condition)
          statement;
       
    • if (condition) {
          // body
      }
       
    • if (condition)
          statement1;
      else
          statement2;
       
    • if (condition) {
          // body
      } else {
          // body
      }
       
    • if (condition1) {
          // body
      } else if (condition2) {
          // body
      } else if (condition3) {
          // body
      } else {
          // body
      }
       
    Don't write embedded "simple" (without { }) conditions; only the innermost condition can be "simple" (even if there isn't any else):
     
      if (condition1) {
          if (condition2)
              statement1;
          else
              statement2;
      }
       
    If there is any complex branch in the conditional if statement then all should be complex.
    The condition shouldn't contain any assignment.
  2. switch
     
      switch (key) {
          case Value1:
              // body
              break;

          case Value2:
              // body
              // no break

          case Value3:
          case Value4:
              // body
              break;

          case Value5:
              }
                  // body with local variables
              }
              break;

          default:
              // body
              break;
      }
       

    It is obligatory to have a default branch (error handling!). The default should always be the last.
    Adding a "no break" comment is compulsory if after executing the body of a non-empty case branch execution will continue at the next case branch.
    You shouldn't add break after unconditional return and throw statements.

    For short-body similar case branches (e.g. conversion) you can use the more readable compact switch form:

        switch (code) {
            case 'I':  return 1;
            case 'R':  return 2;
            case 'B':  return 3;
            case '\n': return 4;
     
            default:   return 0;
        }

  3. for, while, do-while
     
    • for (long i = 0; i < 100; i++)
          statement;
       
    • for (long i = 0; i < 100; i++) {
          // body
      }
       
    • for (long i = 0; i < 100; i++)
          ;    // empty body
       
    You shouldn't write embedded "simple" (without { }, Example 1) loops, only the innermost loop can be "simple".

    If the loop variable is only needed inside the loop, then you can define it in the header of the for loop as in the examples above.

    If you leave out any of the statements from the head of the for loop, then you should leave a space between the '(', ';', or ')' characters. E.g.: for ( ; ; )
     

       
    • while (condition)
          statement;
       
    • while (condition) {
          // body
      }
       
    You shouldn't write embedded "simple" (without { }, Example 1) loops, only the innermost loop can be "simple".
    You shouldn't use assignments in the condition.
     
       
    • do
          statement;
      while (condition);
       
    • do {
          // body
      } while (condition);
       
    You shouldn't write embedded "simple" (without { }, Example 1) loops, only the innermost loop can be "simple".
    You shouldn't use assignments in the condition.
     
  4. try-catch-throw
     
      try {
          // body
      }
      catch (Type1 exception) {
          // body
      }
      catch (Type2) {
          // body
      }
      catch (...) {
          // body
          throw;    // rethrow
      }
       
    If it doesn't cause any problems, the recommended way of writing the type and parameter name accepted by catch is the following:

        catch (const Type& exception)  or
        catch (const Type& e)          or
        catch (const Type&)

    (if Type is not a basic type).


5. Variable declarations
 

  • Declare all variables in the tightest scope. E.g. if you have a utility variable which is needed only in an if branch, then declare it there. Static variables of functions should be declared at the beginning the function. Do not use variables with the same name within the same function.
    Caution: the constructor of variables declared in the scope of a (for, while, do) loop is called for each iteration. Sometimes this is what you need, but you may run into performance problems.
     
  • Class-type variables (objects) should be declared where they can be properly initialized (the default constructor + an assignment later is not as efficient as a proper initialization using the appropriate constructor.) The basic type should be declared at the beginning of the tightest scope. If a (local) variable cannot be properly initialized when declared, then don't give it a dummy value (e.g. 0), so that the compiler can shout if you forgot to initialize the variable in one of the branches. If the compiler gives a false warning then indicate this in a comment at the place of the unnecessary initialization.
     
  • All variables should be declared (and initialized) on separate lines. This is obligatory for global (static) and class data member variables, pointers and references. An exception to this rule can be made for tightly related local variables. E.g.:
      double x, y, z;
    If you comply with the rules above (and don't write 20-page functions), there shouldn't be too many variable declarations in one place.
     
  • Variables shouldn't be defined in line with the opening brace ('{') of the scope.
     
  • Declaring and initializing local and global (static) variables:
     
      long index = 3;
       
    Place a space between the type and the name, and in front of and behind the =. This way one declaration fits nicely with the statements. If you place more than one declaration line align the names and the equal signs if possible. You can add empty lines in front of, between, or after the declaration(s) if that seems logical.

    The '*' for pointers and the '&' for references should be bound to the type:
     

      char* ptr   = NULL;
      long& index = oldIndex;
       
    You should never declare more than one pointer or reference variable on one line.

    Don't leave a space between the array variable name and the '[' character:
     

      char  array[20];
      char* numbers[] = { "One", "Two", NULL };
       
  • The declaration of class data members is similar to that of local variables, though the initialization is (clearly) done somewhere else, and alignment is stricter because there are more members. See the chapter Data member declaration about data member declaration, and chapter Implementing methods of embedded classes about implementation issues.
     
  • Global variables and constants should always always be accompanied by comments describing their usage, and give great care when choosing their name (it shouldn't be k, myDrw or newTKDef). If only a longer name can describe the role of the variable, then use that. Don't use (many) global variables; put everything into namespaces.
     
  • If a "variable" is constant then declare it const:
     
      const Vector& v = translation;
       
  • Do not use "naked" values (literals) in the code; assign a constant to each of them.
     
  • Naming conventions: => see chapters Variable names and Constants.

6. Functions
 

  1. Declaration
     
    • Place one (or two if the first gives only one space distance) tab between the type of the return value and the name of the function. Also leave a space in front of the opening parenthesis of the parameter list and behind each parameter-separating comma. E.g.:
       
        void    DrawPixel (long x, long y);
        short   GetVersion (void);
         
      Also leave a space between the type and the name of the parameter.
      If you have more than one modifying keyword, the function name can be moved to a new line:
       
        static int cdecl
        SetPointer (char* p);
         
    • If you have default parameters, put a space in front of and behind the '=':
       
        void    PrintNumber (long n, short radix = 10);
       
    • If you have many parameters and the function declaration doesn't fit into one line, every parameter should be put into a new line and the names and types should be aligned. E.g.:
       
        String  ConcatenateName (const String* part1,
                                 const String* part2,
                                 const String* part3,
                                 const String* part4,
                                 bool          appendNewLine = false);
         
    • Use const whenever it is possible.
       
    • You should always write void if the function doesn't have any parameter. Except: default constructor, destructor and the operators which don't have parameters by definition.
       
    • Do not use references to pass values back and to initialize variables. Use pointers instead. On the other hand references may be used for value or object modification (input/ouput parameters).
       
    • Pass bool parameters only in the simplest cases, use enums instead.
       
    • Operator overloading can be used only for natural conversions. The operator should immediately follow the operator keyword.
       
    • Naming conventions: => see Function names
       
  2. Implementation
     
    • Style:
       
        void    PrintNumber (long n, short radix)
        {
            // body
        }
         
    • The function header should follow the rules described above for function declarations.
       
    • You should write the body of the function indented by one tabulator field to the right.
       
    • You should leave two empty lines behind the closing '}' of the function.
       
    • You should write one statement per line.
       
    • Functions shouldn't be longer than 80 lines.
       
    • Try to avoid too compact structures, very complex expressions and magical C tricks.
       
    • Conditional expressions should be kept maximum 3 levels deep; try to separate the code to separate (inline) functions if more depth is needed.
       
    • Try to avoid using many (more than one) return statements. Also, avoid return statements in the middle of the function body, except the conditional returns at the beginning of the function.
       
    • Use Asserts to check incoming parameters (preconditions) at the beginning of the function body.
       
    • Declaration of local (or static) variables is discussed in the Variable definitions chapter.
       

7. Type definitions
 

  1. struct
     
        struct Vector {
            double x;    // x coordinate of the Vector
            double y;    // y coordinate of the Vector
            double z;    // z coordinate of the Vector

            bool   isNormalized;    // if it is true, then the vector is normalized
        };
         

      Do not write the default public keyword for structs, and do not use protected or private sections. Write a class instead if you need such sections. Similarly, do not use static data members and inheritance.
      Other rules concerning data member declarations are the same as what you can find in the Data member declaration chapter for classes.

      Place an empty line between the data members and constructors if you need constructors. E.g.:
       

        struct Point {
            long x;    // x coordinate of the Point
            long y;    // y coordinate of the Point

            Point ();
            Point (long xc, long yc);
        };
         

      Structures can be used only in the sense you got accustomed to in C programming. Structures may have constructors and constructor-like (Set) methods beside the data members.
      Other rules concerning method declarations are the same as what you can find in the Method declaration chapter for classes.
      See the chapter Embedded classes and structures for information on embedding structures into classes.
       
  2. union
     
      Unions should comply with the same rules as srtucts.
      Use unions only for optimization for space.
       
  3. enum
     
        enum Status { OK, Error };    // return value of many methods

        enum DataType {    // possible data types of the interpreter
            Int,
            Real,
            Bool,
            String,
            Object,

            Void
        };
         

      In case of value assignment align both the '=' signs and the values:
       
        enum Token    // tokenizer constants
        {
            Integer   = 25,
            Real      = 48,
            Character = 701
        };
         
      Please add comments to enums describing their role.

      Take into consideration that the physical size of an enum can be different on each platform. Because of that e.g. the length of a structure containing an enum can vary, causing problem if you write it out in binary form. To avoid this set the size of the enum to the size of int before compilation.
      Give value to the enumerators if the enum will be written into a file.
       

  4. typedef
     
        typedef long Offset;

        typedef unsigned long  Index;

        typedef VectorFS<double, 3>  Vector3D;
         

      Try to avoid to typedef pointers.
       
       

8. Class definitions
 

  1. Base structure
    1. Visibility - allowed is the developer's viewpoint (private, protected, public), and the user's viewpoint (public, protected, private). There may be only one of each section. E.g.:
       
        class Vector {
        private:
            // private part

        protected:
            // protected part

        public:
            // public part
        };
         

      The opening bracket '{' of the class body should go to a new line if the header of the class spans multiple lines (see inheritance).
      In some rare cases the public (or protected) parts contain declarations (mainly user types, e.g. enum), which are used in the private (or the protected) section. These declarations should appear at the beginning of the class:
       
        class Vector {
        public:    // predefinitions
            typedef double Coord;
            // other public definitions also used by the private (protected) part

        private:
            // private part

        protected:
            // protected part

        public:
            // public part
        };

    2. Template classes
       
        template <class Type, int BufferSize = 16>
        class Array {
            // body
        };

        Template arguments should be treated similarly to function arguments.
         

    3. Inheritance
       
        class Derived: public Base1,
                       public Base2,
                       protected Base3,
                       private Base4
        {
            // body
        };
         
      You should always write in case of inheritance the default private keyword.
      Use the public virtual Base ordering for virtual inheritance.
      The opening brace in case of single inheritance should go to the end of the header line (class ...).
    4. Naming conventions
  2. Body
    1. private section - typical layout, recommendations, rules
       
        class String {
        private:
                // definitions

                typedef short Symbol;

            enum InternalStatus { OK, Error };
         
                // static (class) data members

            static const long  MaxSize;    // short description
            static char        defChar;    // short description

                // normal (instance) data members

            char*  str;    // short description
            long   size;   // short description

                        // static (class) functions

             static unsigned char*  ToPascalString (const char* cStr);

                // normal (instance) member functions (methods)

                void          Clear      (void);
            long          SkipSign   (long from) const;
            inline long   SkipDigits (long from) const;
            virtual void  Print      (void) const;
         
        protected:
            // protected part

        public:
            // public part
        };
         

      For better readability the default private keyword should always appear at the beginning of the class.

      Put disabled methods (e.g. constructors) to the beginning of the normal method list with a // disabled comment. E.g.:
       

        String (const String& source);               // disabled
        String& operator= (const String& source);    // disabled
         
      If you use friend classes or functions, it should appear at the beginning of the private section if it needs to know about all private data and methods:

          private:
              friend class Menu;    // must be friend because ...

      If the friend class only needs a few private methods or data, then you should put the friend declaration close to those, so that the user would see what the friend class uses from our class. (This ruins the immediate visibility of those classes which can access our class.)
      You should explain in a short comment the reason behind the friend declaration (in the same line).
      (The exceptions to this rule are those public friend operator functions, which are not normal methods so that the compiler could assure the symmetry of the operator. E.g. operator+ (const Vector& v1, const Vector& v2)friend function of the Vector class).
    2. protected section - typical layout, recommendations, rules
       
        There aren't any special recommendations and rules for the protected section, except the following recommendation:
        Use protected data members sparingly; try protected (even inline) methods instead.
         
    3. public section - typical layout, recommendations, rules
       
        class String {
        private:
            // private part

        protected:
            // protected part

        public:
                // definitions

                typedef char* CStr;

            enum SymbolType { ASCII, WideChar };

                // declaration of optional exception classes (types)
         
                // static const (class) data members

            static const char  NewLine = '\n';     // short description
            static const char  MaxLength = 256;    // short description

                        // static (class) functions

             static char*  GetClassName (void);

                // Constructors

            String ();
            String (const char* cStr);
            String (const String& source);
           ~String ();
            String&  operator= (const char* cStr);
            String&  operator= (const String& source);

                // normal (instance) member functions (methods)

            void          Delete     (long idx);
            void          Delete     (long from, long range);
            void          DeleteLast (void);

            inline void   Clear      (void);

            void          Replace    (long from, long range, char ch);

            inline charoperator[] (long idx);
            inline char   operator[] (long idx) const;

            virtual long  Read       (IChannel& ic);
        };

        Do not use public non-const data members in classes.

       
    4. Data member declaration
       
      • Data members (one per line) should appear below each other, and put a short description after each member.
      • If you have many data members, align them. Leave at least two spaces between the type and the name. E.g.:
         
          char*          str;    // points to the allocated string (if not NULL)
          unsigned long  size;   // size of the string
           
      • If you have many data members, leave an empty line between the logical groups.
      • If a member has many modifying keywords (static, const, mutable), this is the correct order:
        • static const
        • mutable const
    5. Method declaration
       
      • You should not put comments behind the method declarations (the public and protected methods should be described in the documentation, the private methods in the implementation to make the class easily understandable).
      • If you group the methods, align the names of the methods, and preferably leave a tab (or two if one yields to only one space) between the return type and the name. Also align the parameters except when the name is very long. E.g.:

                 /* IO methods */
         

          virtual long  Read     (IChannel& ic);
          long          ReadQuotedString (IChannel& ic, bool skipLeadingWhiteSpace = true);
          long          ReadLine (IChannel& ic);
          virtual long  Write    (OChannel& oc) const;
           
        Leave at least one space between the name of the method and the opening parenthesis of the parameter list.
      • If a method has many parameters, and the declaration doesn't fit into one line, every parameter should appear in a new line, the types and the names of the parameters should be aligned. E.g.:
         
          virtual Vector3D  ComputePosition (const Matrix& rot,
                                             Mode          mode,
                                             double        x,
                                             double        y,
                                             double        z) const;

          In this case you can leave an empty line after the method declaration if you feel the class would be more perspicuous.

         
      • Style of template method declarations:
         
          template <class U, class V>
          inline Pair&  operator= (const Pair<U, V>& p);
      • If you group the methods logically, you can place a short group description in front of or an empty line between the method groups. Place an empty line in front of and behind the group description, and also indent it with one tab. E.g.:
         
              /* Compare methods */
           
          bool         operator== (const char* cStr) const;
          bool         operator!= (const char* cStr) const;

              /* Conversion methods */

          const char*  ToCStr (void) const;
          const char*  ToCStr (long from, long range) const;

      • Place other constructor-like methods (such as operator=) in the group of constructors. Other examples for these methods are (with their recommended names):
         
        • static String*   NewInstance (void);  - creates a new String, but you can take its address (opposed to the constructor)
        • String&  Assign (const char* charPtr, long charCount);  - like operator=, but it can have many parameters
        • virtual String*  Clone (void) const- copies the String
         
      • Write the default constructor and destructor with an empty - no void - () parameter list, so that it can be found easily and emphasizes that these don't have any parameters by definition. (You should write the same way operator functions without parameters.) For all other methods and functions which don't have parameters you should always write the void keyword.
         
      • Always align the names of constructors, even if you use the explicit or inline keywords. E.g.:
         
                   String ();
          explicit String (char c);
                   String (const char* cStr);
          inline   String (const char* charPtr, long charCount);
          explicit String (unsigned char* pStr);
                   String (const String& source);
           
        You should always put the explicit keyword in front of the constructors which have one parameter (except the copy constructor), unless you need automatic conversion.
         
      • The compulsory parameter name for the copy constructor and the assignment operator is source:
         
          String (const String& source);
          String&  operator= (const String& source);
           
        You should always write const, except in special cases.
         
      • You should write the virtual keyword in front of all such methods for better navigation, even if the method inherited this attribute.
         
      • Style of abstract virtual methods:
         
          virtual void  Draw (void) const = 0;
           
        Leave a space in front of and behind the = sign.
         
      • In case of many modifier keywords (static, virtual, inline, explicit, friend) the order should be:
        • static inline
        • virtual inline
        • explicit inline
        • friend inline
         
      • You should not write the method implementation into the class declaration (except the short methods of embedded classes).
         
      • In all other points (placement of parameters, default parameters, etc.) the declaration of methods follow the rules of function declarations.
       
    6. Embedded classes and structures
       
        You should place embedded (utility) classes and structures in the declaration part of the class, and indent them with one tab. E.g.:
         
          class Vector {
          private:
                  // definitions

              struct RealPair {
                  double  r1;    // first value
                  double  r2;    // second value
           
                  RealPair (): r1 (0), r2 (0) {}
                  RealPair (double val1, double val2) r1 (val1), r2 (val2) {}
              };

          ...

          };
           

        For such small utility classes you can put the method implementation into the class.
        Avoid writing large embedded classes, create a well hidden namespace instead (e.g. for a String class with the name StringPrivate), and put the embedded class there.
       
    7. Naming conventions
       

9. Class implementation
 

  1. Static (class) members
     
      long          Array::defaultInitialCapacity = 100;
      const long    Array::MaxInitialCapacity     = 1000000000;
      const double  Array::DefaultAllocationUnit  = 100.5;
       
    Try to align data member names, the '=' signs, and the initializer values.
    Declare static data members at beginning of the source (.cpp) file.
  2. Methods
     
    • Place two empty lines between the method implementation functions. Put the public inline functions three empty lines behind the end of the class in the header file.
       
    • The order of methods in the implementation file should be the same as in the class declaration. You should write the constructors first, then the other public methods (as in the class declaration). It is practical to implement the private methods after the public methods, and the protected methods where they fit in most logically.
       
    • Style of constructors
       
        Array::Array (long initialCapacity, long maxSize):
            base1 (initialCapacity),
            base2 (0, 25),
            member1 ("", 40),
            member2 (1)
        {
            // body
        }
         
      You should align the initializers of the ancestor classes and the data members, with the initializers of the ancestor classes coming first, and then the data members in the order they appear in the inheritance and in the data member declaration. Data members can also be initialized in the body if it is more practical.
    • Style of template methods:
       
        template <class Type>
        inline long  Array<Type>::GetSize (void) const
        {
            // body
        }
         
    • The method declaration chapter defines the style of the method header.
       
    • You should use the this pointer to access the data members and functions (e.g. this->x) only if this has a distinctive role. Never use it for static members. Also, you don't have to use it for accessing the members of the ancestor class(es). When accessing names outside of the scope of the class, you have to use the scope. E.g. call a (maybe OS) Sleep () function this way: ::Sleep ().
       
    • Do not use method arguments which have the same names as data members if possible. If you couldn't find any other logical name, then please use the this pointer to access the data members.
       
    • Recommended style for writing an assignment operator (operator=):
       
        Point2D&  Point2D::operator= (const Point2D& source)
        {
            // testing whether source is NULL

            if (this != &source) {
                BaseClass1::operator= (source);    // if Point2D has base class named BaseClass1
                BaseClass2::operator= (source);    // if Point2D has base class named BaseClass2

                x = source.x;    // own data member
                y = source.y;    // own data member
            }

            return *this;
        }
         

    • In other points the method implementation follows the rules of function implementation.
     
     

10. Namespace
 

      namespace CLib {
          void*  MemCopy (void* destPtr, const void* srcPtr, unsigned long count);
          void*  MemMove (void* destPtr, const void* srcPtr, unsigned long count);
          void*  MemSet  (void* destPtr, int filler, unsigned long count);
      }
       
    You should indent with one tab the body of such namespaces, which contain only simple functions, type declarations (typedef), and constants.
    If you define a (bigger) class in the namespace, then indenting is not always practical. In this case use the following style:
     
      namespace Math  {
       
      class Vector {
          // body
      };

      }   // namespace Math
       

    In this case you should add a closing comment at the end of the namespace.

    Don't open the namespace again for the implementation of the functions. E.g. write the implementation of the MemCopy function above outside the namespace:
     

      void*  CLib::MemCopy (void* destPtr, const void* srcPtr, unsigned long count)
      {
          // body
      }
       
    The compiler will give a warning if you accidentally write MemCpy instead of MemCopy in this case, otherwise it will put the new "bad" MemCpy into the namespace .

    This error is very rare in class method implementations (as the given method should be declared up front in the class), so in that case you can write the implementation outside the namespace, open the namespace again, or use the using directive.

    Except for the main () function everything should go into a namespace (at best you (should) open the given namespace ). This is important to avoid potential name clashes, especially if you use external libraries.

    Use the using namespace X directive mostly in the implementation file, it should appear in the headers only if it is really necessary (e.g. if you would like to have your base module in an open namespace). Don't use "separate" using declarations in headers, as these declarations will be involuntarily inherited by those files which #include the header.

    The main reason behind the usage of the using directive is to put it only into those files where the given name(s) appear quite often, e.g. because it is a base unit (type, constant, etc.) of that area. Don't put using in only for a couple of abbreviations, or put it maybe just into a smaller scope, e.g. inside a method. Basically we use namespaces to separate and distinguish between the modules and the names declared in them, so limit the number of using directives both in quantity and scope.

    Use namespace alias only where it is really necessary.


11. Preprocessor directives
 

      #if defined XXX

      // body

      #else

      // body

      #endif

     
    Do not indent "normal" C++ code in conditional preprocessor statements; but indent other preprocessor statements with one tab.
    Put comments into structures which are longer than two screens, so that matching directives could be easily found.
    Do not use the short #ifdef and #ifndef directives.

    Leave a space between the #include and the file name:
     

      #include "Array.hpp"
       
    A few recommendations:
    • Avoid #elifs.
    • Avoid complex #if expression variations. (But this may be necessary for #if DEBUG_LEVEL > 5.)
    • Avoid macros. Use inline and / or template functions and methods.
    • Avoid #defines; use consts or enums instead.
     
     

12. Writing comments
 

  • The language of the comments should be English; avoid bad words. Do not use accented characters.
     
  • You should use the following style for comments; any other form is forbidden:
    File header:

      // *****************************************************************************
      // One sentence summary
      // Module / project name, platform
      //
      // Namespaces:        Contact person:
      //     DG                 BM
      //     LibPart            PJ
      //
      // [SG compatible] - if it really is
      // *****************************************************************************

      Any code can come only after this, including the #if !defined ARRAY_HPP directive as well.

  • The length of the full comment line is 120 characters.
     
  • You should always indicate whom the namespace belongs to. For classes you may indicate the owner.
     
  • You should always put comments behind data members in class declarations: possibly in the same state what that data member is used for. The same is valid for global variables. Methods are not commented in class declarations.
     
  • Comment unused function parameters with /* ... */; do not use the #pragma unused directive. Use the UNUSED_PARAMETER macro instead if it is needed in the release build.
     
  • For the non-public functions of the module you should always explain the meaning of non-trivial parameters and the return value. E.g. for indices always indicate the range (starts from 0 or 1, any special values, etc.). Example:

      // -----------------------------------------------------------------------------
      // Function:     Reads ...
      //
      // inHandle:     Contains the text. If nil read from 'inFile'
      // inFile:       Input file description
      //
      // return value: OK or ERROR
      // -----------------------------------------------------------------------------

      OSErr  ReadTemplate (Handle inHandle, FileDefRec inFile)
      {
          ...
      }
       

  • You should always indicate with three exclamation marks if you leave something unfinished:
     
      n = 5;    // !!! st; It will come from database
       
  • You can also put optional section descriptions in between the lines of the source code, beginning at the current tab depth. You can also add short explanations to the end of the source line by adding a tab after the semicolon; or, if there are more of those, you can align them with tabs. E.g.:
     
      n = 5;

      // Searching will follow

      for (i = 0; i < n; i++) {
          if (a[i].GetCode () == pattern) {
              b[i].SetValue (a[i].GetValue ());    // Found
          }
      }
       

    You should always add comments:
    • For unusual solutions (leaving break out, empty loop, etc.)
    • If it would take too long to understand the code for someone else without the comment.
    • If something is forbidden or not recommended for others.
    Optional (others will be thankful) if it helps in any way.
    Do not let the comments break the flow of the code, and don't break up the code.
     

13. File structure
 

  • Always use 4 spaces as tabs in all files.
     
  • The maximum length of the lines is 120 characters. Statements (function and method bodies) shouldn't even get close to this number.
     
  • The body of a header file should be braced with the following preprocessor structure. The name is made up of the namespace identifier, the file name and the HPP suffix, in capitals and separating them with underscores (_). E.g.:
     
      #if !defined IO_LOCATION_HPP
      #define IO_LOCATION_HPP

      // body

      #endif
       

    if the name of the header file is Location.hpp and the namespace is IO.
    This is a special case for preprocessor structures; do not indent the lines within.
     
  • Write all the #includes at the beginning of the source file, and all the forward declarations in case of header files. E.g.:
     
      // -------------------------------- Includes ---------------------------------

      #include "Base.hpp"

      #include "Array.hpp"
      #include "Stack.hpp"
       

      // ---------------------------- Forward declarations ------------------------------

      class SymbolSet;

     
  • The maximum length of file names is 31 characters. File names are case sensitive, and use the extensions .cpp and .hpp, or .c and .h, depending on whether they can be used in pure C code. The file names should be given case sensitive in all references (#include, makefile).
     
  • Relative paths in #includes are allowed, but the ".." path component is forbidden. Furthermore it is not allowed to reference module roots (e.g.: #include "GSRoot/Array.hpp"), only subdirectories inside them.
     
  • Preferably every class has its own header and source file, but if you have many closely related classes, put them into one file. A template class and its specialized versions should also be kept in one file. On the other hand, avoid placing a base class and its descendants into one file.
     
  • Every header file #includes only what the class declaration needs: the ancestor classes and the data member types. If a type is used only as a pointer, reference, or method return value in the class, then use forward declarations instead of #include, to reduce the number of dependencies and included (and opened at compile time) files.
     
  • The files of each subsystem (module, package) should be kept in a separate subdirectory.
     
     
Jump to top
Copyright © 2006 - Graphisoft R&D Zrt. All rights reserved worldwide.
Built on June 25, 2003