Coding Conventions and Good C++ Practice

Started by Blizzard, March 10, 2011, 03:25:00 am

Previous topic - Next topic

Blizzard

March 10, 2011, 03:25:00 am Last Edit: March 18, 2011, 06:42:55 am by Blizzard
I will list all coding conventions here to make coding simpler and easier to understand between all of us who will script. You wouldn't believe how much it can improve coding, save time and make everything just easier if the coding is consistent.



Classes, Variables, Methods, Operators

A proper convention for classes, variables, methods and operators can make it much easier to read the code of others as you instantly know their purpose on sight.

Template: ShowHide
#ifndef ZER0_CLASS_NAME_H // careful, the 0 in ZER0 is a zero, not the letter O
#define ZER0_CLASS_NAME_H

#include <someSystemHeader.h>
#include <someOtherSystemHeader>

#include <library/someName.h> // e.g. for april & friends

#include "RGSS/headerRgssHeader.h"
#include "headerWithinZer0.h"

#define CONSTANT (0)

namespace zer0
{
    // forward declarations
    class X;
    class Y;
   
   // contains the Ruby class
   static VALUE rb_ClassName;
   
   class zer0export ClassName : public PossibleInheritance
   {
   public:
       
       // public instance variables
       int PublicVariable;
       static int StaticVariable;
       
       // public constructors and destructors
       ClassName();
       ClassName(int a);
       ~ClassName();
       
       // public inline getters and inline setters
       int getPrivateVariable() { return this->privateVariable; }
       void setPrivateVariable(int value) { this->privateVariable = value; }
       
       // public getters and setters
       int getA();
       void setA();
       
       // public operators
       ClassName operator+(const ClassName& operator) const
       ClassName operator+=(const ClassName& operator)
       bool operator==(const ClassName& operator) const
       
       // public methods
       void descriptiveMethodName(int parameter, float anotherParameter);
       static void staticMethod();
       static VALUE rb_rubyInterfaceMethod(VALUE self);
       
   protected:
       // protected instance variables
       int protectedVariable;
   
       // protected inline getters and setters
       int _getProtectedVariable() { return this->protectedVariable; }
       void _setProtectedVariable(int value) { this->protectedVariable = value; }
       
       // protected getters and setters
       int _getB();
       void _setB(int value);
       
       // protected methods
       void _protectedMethod();
       
   private:
       // private instance variables
       int privateVariable;
       int c;
       
       // private inline getters and setters
       int _getC() { return this->c; }
       void _setC(int value) { this->c = value; }
       
       // private getters and setters
       int _getD();
       void _setD(int value);
       
       // private methods
       void _protectedMethod();
   
   };

}
#endif


Don't be confused about "zer0export". This only ensures that the class is visible outside of the DLL. When you inherit a class, you have to add "public" before the name of the superclass. Honestly, I don't know exactly why, but you have to do it.


  • Header Guards are always named like ZER0_CLASS_NAME_H.

  • Sort header includes by type; first external library headers, then internal library headers, the internal project headers. Sort each type alphabetically.

  • Sort the declaration by access; first public, then protected and then private.

  • Sort members by type; first instance variables, then static variables, then constructors, then inline getters and setters, then normal getters and setters, then operators, then methods, then static methods.

  • Always first put the getter, then the setter. The parameter name in the setter is always "value".

  • Both destructive and non-destructive operators should return an instance of the given class. If the operator is destructive, the operator should return the calling class.

  • Avoid operators other than public.

  • In method calls use "const TYPE&" and "const TYPE" to explicitly disallow the changing of that parameter during the execution of that method. Using "TYPE&" is better performance-wise when working with classes because it does not create a new class but uses the actual classes passed on to the method. Be aware that you can't use "const TYPE&" or "TYPE&" in some situations.

  • Add "const" at the end of a method definition is the method is not allows to modify this instance of a class.

  • When declaring variables of the same type, declare each in a new line. Do not declare more than one variable of the same type in one line.





Spaces

Use proper spaces between operators and operands, method parameters etc.

Examples: ShowHide
a = b *c+1; // incorrect
a = b * c + 1; // correct

if (a&&b||c) // incorrect
if (a && b || c) // correct

void method_1(int a,int b,int c) # incorrect
int method_1(int a, int b, int c) # correct




Parenthesis

Using the right parenthesis at the right place can make things easier to understand. You should use parenthesis when:


  • having a return value that is a composite: return (a * b);

  • using a ternary branching operator: a = (b ? c : d);

  • having multiple conditions in a ternary branching operator (required since it won't work otherwise): a = ((b && c < 4) ? d : e);

  • (optional) explicit marking of separated code parts in a line: new_position = current_position + (moving_speed * distance) * direction;





Header Includes

Don't include headers in headers except if you must. This reduces recompilation time when you change something in a header because headers are not connected then. Adding headers in source files is fine and you can add as many as you need.

If you are using pointers or references to a class, but never the actual class in a header, then you can use so-called forward declarations of those classes to avoid the include of its header. Basically you are telling the header "this class surely exists somewhere, don't worry about where".


class B
{
public:
   int c = 0;
};


class B; // forward declaration of class B
class A
{
public:
   B* aPointer;
   A();
   ~A();
};

A.cpp: ShowHide

#include "A.h"
#include "B.h" // notice how here is is necessary to include B's header
A::A()
{
   this->aPointer = new B();
}
A::~A()
{
   delete this->aPointer;
}




Inline Implementation of Trivial Getters and Setters

While in classic C, implementing a function in a header will cause linking problems because of multiple implementations, in C++ this can be a useful thing if used with classes. We will implement trivial getters and setters for our classes as inline members. Do not confuse this concept with inline implementations that explicitly use the keyword inline (even though they technically do the same thing). This will result in a bigger executable (slightly), but it will also increase speed. Here is an example of a Point class without and with inlined trvial getters and setters.

without inline: ShowHide
Code: Point.h
class Point
{
public:
   Point();
   ~Point();
   
   int getX();
   void setX(int value);
   int getY();
   void setY(int value);
   
protected:
   int x;
   int y;
   
};

Code: Point.cpp
#include "Point.h"

Point::Point() : x(0), y(0)
{
}

Point::~Point()
{
}

int Point::getX()
{
   return this->x;
}

void Point::setX(int value)
{
   this->x = value;
}

int Point::getY()
{
   return this->y;
}

void Point::setY(int value)
{
   this->y = value;
}

with inline: ShowHide
Code: Point.h
class Point
{
public:
   Point();
   ~Point();
   
   int getX() { return this->x; }
   void setX(int value) { this->x = value; }
   int getY() { return this->y; }
   void setY(int value) { this->y = value; }
   
protected:
   int x;
   int y;
   
};

Code: Point.cpp

#include "Point.h"

Point::Point() : x(0), y(0)
{
}

Point::~Point()
{
}


We will use inline methods ONLY for trivial getters and setters. Trivial means that it either gets or sets the value directly. No other kind is allowed.



Miscellaneous

Using the right parenthesis at the right place can make things easier to understand. You should use parenthesis when:


  • Braces go always into a new line and in that line there is nothing but the braces.

  • ALWAYS use "this->" when working with members. Syntax coloring allows you instantly see which variable is a member and which is not.

  • Use strict camel case. CamelCaseLooksLikeThis or somethingLikeThis. Even if you use acronyms like GUI and ID, use proper CamelCaseForTheId or theGuiPartOfTheName.

  • Use explicit typing of literals. If a variable is a float, don't use "float a = 0;", use instead "float a = 0.0f";

  • Use hstr for all your string needs. Always use chstr if you use it as a parameter in methods. chstr is a typedef of "const hstr&".

  • Before you commit anything, try to build the project first! It's frustrating to update and then realize it doesn't work.






I will add more conventions as I notice people deviating from things.

If you are unsure into which category something belongs, ask.
If you have suggestions for more conventions, post them.
If you feel that a convention is pointless, post and I will argument the convention more detailed so that you understand its purpose.

If you have any problem understanding the code examples I have given or even if the slightest piece of code is unclear, please post. C++ is a very complex language and it is better if you know what's going on.
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

Blizzard

March 10, 2011, 02:17:24 pm #1 Last Edit: March 10, 2011, 03:40:24 pm by Blizzard
I have added Inline Implementation of Trivial Getters and Setters. I will update the code in SVN now to match this convention.

EDIT: I added this to variables, etc.:


  • When declaring variables of the same type, declare each in a new line. Do not declare more than one variable of the same type in one line.



EDIT: I edit this:


  • Sort header includes by type; first external library headers, then internal library headers, the internal project headers. Sort each type alphabetically.



EDIT: Added one more thing to misc:


  • Before you commit anything, try to build the project first! It's frustrating to update and then realize it doesn't work.

Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

Blizzard

March 13, 2011, 11:25:43 am #2 Last Edit: March 18, 2011, 06:12:20 am by Blizzard
Guys, you are not keeping the coding convention and you are not committing stable builds.

Also, Ryex, it's visible, not visable. :P

And you don't really need to add comments that say "forward declaration of blablabla" or "public variables". Those things are more or less obvious. The documentation itself and self-documenting code is pretty much all that is needed, except in some very abstract cases where comment really should be added or if you want to mark specific blocks of code.

EDIT: Also, please end your sentences with fullstops.

EDIT: Don't get me wrong. I'm not angry at you. I'm merely asking you to pay attention in the future and fix your coding so far.

EDIT: I updated the template so we use a rb_ prefix for functions that are interfaced with Ruby.
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.