Graphisoft®

C/C++ Style GuideVersion: 1.1

C++ Style Guide

1. Introduction 


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
 


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 (Int32 i = 0; i < 100; i++)
          statement;
       
    • for (Int32 i = 0; i < 100; i++) {
          // body
      }
       
    • for (Int32 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
 


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 (Int32 x, Int32 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 (Int32 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 (Int32 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 {
            Int32 x;    // x coordinate of the Point
            Int32 y;    // y coordinate of the Point

            Point ();
            Point (Int32 xc, Int32 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 Int32 Offset;

        typedef unsigned Int32  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 Int32  MaxSize;    // short description
            static char        defChar;    // short description

                // normal (instance) data members

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

                        // static (class) functions

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

                // normal (instance) member functions (methods)

                void          Clear      (void);
            Int32          SkipSign   (Int32 from) const;
            inline Int32   SkipDigits (Int32 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     (Int32 idx);
            void          Delete     (Int32 from, Int32 range);
            void          DeleteLast (void);

            inline void   Clear      (void);

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

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

            virtual Int32  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)
          UInt32  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 Int32  Read     (IChannel& ic);
          Int32          ReadQuotedString (IChannel& ic, bool skipLeadingWhiteSpace = true);
          Int32          ReadLine (IChannel& ic);
          virtual Int32  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 (Int32 from, Int32 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, Int32 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, Int32 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
     
      Int32          Array::defaultInitialCapacity = 100;
      const Int32    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 (Int32 initialCapacity, Int32 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 Int32  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
 


11. Preprocessor directives
 


12. Writing comments
 


13. File structure