Welcome Guest [Log In] [Register]
We hope you enjoy your visit.


You're currently viewing the Ultimate 3D Community as a guest. This means that you can only read posts, but can not create posts or topics by yourself. To be able to post you need to register. Then you can participate in the community active and use many member-only features such as customizing your profile, sending personal messages, and voting in polls. Registration is simple, fast, and completely free.

Join our community!

If you are already a member please log in to your account to access all of our features:

Username:   Password:
Add Reply
Good coding style and naming conventions; The earlier you get consistent the bette
Topic Started: Jul 13 2008, 03:15 PM (736 Views)
Dr. Best
Member Avatar
Administrator
[ *  *  *  *  *  * ]
Hi everybody,

this is my first tutorial for the new forums and the reason why it is the first one simply is that you should get to read something like this as early as possible. Mostly it is meant to avoid that you make used to bad habits. It is aimed towards novices. Today I am going to talk about good coding style and about naming conventions. This tutorial applies to every programming language, but in particular it does apply to C++ and C#. So lets get started.

What are naming conventions and why are they important?
If you are programming in C++ you will need many names for many different things: Variables, functions, structures, classes, enumerations, macros and constants specified through define are the most important ones. The name can be any alpha numerical string and it can include underscores ( _ ) . Naming conventions are rules, which are used to chose the names. To demonstrate their importance here comes a little example in which no clear naming conventions are used:
Code:
 
include <iostream>
using namespace std

int val[5]={3,2,7,9,4};
char program_start_text[]="This application prints several numbers. Enter 1 now to print the numbers with indices or 0 to print exceptionally the numbers.";

void PrintVals(bool withNums){
if(withNums){
for(int i=0;i<5;i++){
cout<<i<<val[i]<<endl;
}
}
else{
for(int i=0;i<5;i++){
cout<<val[i]<<endl;
}
}
}

int main(){
cout<<program_start_text<<endl;
bool bPrintIndices;
cin>>bPrintIndices;
PrintVals(bPrintIndices);
return 0;
}

So what is bad about this piece of code? The answer is simple. It is not consistent with its naming conventions. Of course the example is a bit exaggerated, but it shows the problem quite well. Some variable names start with lower case letters and have their words separated through underscores, some start with lower case letters, but have their words "separated" through capital letters and PrintVals starts with a capital letter and separates the words through capital letters. bPrintIndices does have the prefix b, which implies that it is boolean, withNums does not have a prefix like this although it is boolean, too. Some names are abbreviated (like val instead of value) some are not. The names are random and do not match to any set of rules. This makes the code hard to read, since you have to look out for different markers for new words and variable beginnings all the time and it makes it hard to write since such random variable names are hard to remember. This is why naming conventions are important.

Naming conventions are rules in the form of "If you have this, call it like that.". It does not make a big difference what naming convention you are using, but it is important that you are using the same convention everywhere. If you do so you will find it significantly easier to remember the names you gave to particular things, because you always know what form they must have. And it also makes it easier to read your code, because with a good naming convention the name already tells a lot about the object behind this name.


An exemplary naming convention:
At the beginning it can take a while to find a naming convention you like. Though you should find one as fast as possible, because as long as you have not found one your names will be inconsistent. To help you in getting an idea of how a naming convention may look here is a description of the one I am using. I will formulate it as a list of rules:

  • Names for functions start with a capital letter and new words in the name start with capital letters. In between lower-case letters are used. No underscores are used. Examples: ComputeSquare(...); Clear(...);
  • Names for variables look like names for functions, but may have a prefix. The prefix is always a lower case letter and gives information about the type of the variable and about the information it contains:

    • Indices and iterators (part of the STL, it does not matter, if you do not know about them currently) get the prefix i. Example: unsigned int iVertex;
    • Offset values (values which are usually added to indices) get the prefix o. Example: unsigned int oNextChunk;
    • Size values get the prefix s. Example: unsigned int sBuffer;
    • Variables, which give the count of something (e.g. the number of elements in an array), get the prefix n. Example: unsigned int nTriangle;
    • References get the prefix r. Example: double& rValue;
    • For pointers and arrays a p is used as prefix. There may be more than one p, if the pointer can be dereferenced multiple times or if it is an array of pointers. The rest of the name does not use a plural. At first it may contain some words describing what the array holds information for, finally the type of information follows. Examples:
      int pWorkerSalary[50]; (There are 50 workers, the array holds information about the workers and every element of the array is a salary value.)
      D3DXVECTOR2** ppSetVertexTexCoord; (It is a 2D array; you need the index of a texture coordinate set and the index of a vertex to identify its members. Then you get a D3DXVECTOR2, which is a structure containing two floats, giving a texture coordinate.)
      CObject* pObject; (It is a pointer to an instance of some class called CObject.)

  • Names for structures look like names for functions, but have a capital S (for structure) as prefix. Example: struct SVertex;
  • Names for classes look like names for functions, but have a capital C (for class) as prefix. Example: class CObject;
  • Defines (specified through #define) use exceptionally capital letters and the words are separated through underscores. Example: #define EULERS_NUMBER 2.718281828
  • Enumerations use exceptionally capital letters and the words are separated through underscores. The same rule applies to the elements of the enumeration. All elements of an enumeration get a prefix, which shows which enumeration they belong to. Example:
    enum U3D_SHADOW_RECEIVER_TYPE{
    U3DSRT_PRIMITIVE,
    U3DSRT_MODEL,
    U3DSRT_TERRAIN
    };
  • All names should be as descriptive as possible. The name should give a quite precise idea of what this object is good for. Abbreviations should be used exceptionally for very long terms, which occur very often (e.g. U3D for Ultimate 3D or USS for unit sphere space). Function names should use imperatives, names for boolean variables should look like questions.
    Examples for good function names: GetNextFile(...), SearchMaterial(...), LoadModel(...)
    Examples for bad function names: Next(...), SearchMat(...), Load(...)
    Examples for good variable names: pWorkerSalary, iLastObject, IsBillboard
    Examples for bad variable names: pValueArray, iObject, Billboard

As I already said it is more important that you decide for some naming convention and use it everywhere. It does not have to be this one. Though I have made good experiences with this naming convention. Even in code, which I wrote month ago I often can guess variable names correctly. This eases the development significantly. Once you have decided for a naming convention (and you should do this as early as possible, but with bethought) you should use it also for variables, which use types from some external APIs. If they do have names, which do not match to your naming convention in some tutorials this does not mean that they can not have names, which match your naming convention in your code. To show you how significantly a naming convention can increase the readability of code here is the code above with consistent naming:
Code:
 
include <iostream>
using namespace std

int pValue[5]={3,2,7,9,4};
char ProgramStartText[]="This application prints several numbers. Enter 1 now to print the numbers with indices or 0 to print exceptionally the numbers.";

void PrintValues(bool PrintIndices){
if(PrintIndices){
for(int i=0;i<5;i++){
cout<<i<<pValue[i]<<endl;
}
}
else{
for(int i=0;i<5;i++){
cout<<pValue[i]<<endl;
}
}
}

int main(){
cout<<ProgramStartText<<endl;
bool PrintIndices;
cin>>PrintIndices;
PrintValues(PrintIndices);
return 0;
}

It does look significantly better now, doesn't it?


Comments in code:
There is a neat little quote, which is always good to start talking about comments: "Good programmers do not comment their code. If it was hard to write it should be hard to read.". Notice how the sarcasm pours out of this sentence. There are many good reasons why good programmers do have lots of comments in their code. I want to list some of them here:

  • It makes it easier to read and understand the code. Everybody who ever uses your code or has to change it will have it easier. And the person, who has to use and change your code more often than anybody else, are you after all ;) . While you are writing a piece of code you usually know what it does. But your software changes over time and usually code needs to be changed some day. Then you will need to be able to understand it again. Comments are very helpful in this. And they ease the use of the code since they can say how particular functions, classes, structures or enumerations should be used.
  • Writing good comments eases development, makes it more convenient and shortens the development time. This point may sound a bit paradox, since writing comments is work and more hard work makes development harder, less convenient and stretches the development time. But that is simply not true. Before you write a piece of code you need to know precisely what this piece of code should do. Usually you have an idea of what the code should do in your mind. The trick with good comments is that you simply write down that idea precisely before you start writing the piece of code. By enforcing yourself to write it down the idea usually becomes more precise. And if you know what you want precisely it will be easier to do it.
  • Good comments can function as provisional documentation and they make it significantly easier to write a real documentation later on. As an example take Ultimate 3D 2.0. I have been developing it for more than a year and I did not start writing the documentation before the beta was done. It would have been impossible to remember how every single function works. Fortunately I found all the information I needed to write the documentation in the comments. If there are no comments like this, it can happen that you simply forget how a function you wrote does work and then it is absolutely useless.

So how do you create good comments? The second one of the points above already included the most important trick. Before you write a piece of code you write what this piece of code should do in a comment. This way the comment functions as a mini to do. You could see it as an order to yourself: "Write a piece of code, which does this now.". This method of commenting code is especially important for code that implements functions. But a C++ project usually has more than that. There are function declarations, variable declarations, class definitions, etc. These should be commented as well. These comments are the comments, which will be important for documenting your project (and they are even more important, if you do not plan to document your project properly). For a function declaration the comment should say what the function does and what has to be passed for its parameters to reach that it will work properly. Again the comment can function as a mini to do here. For a variable declaration the comment should say what information this variable saves. For a class the comment should say what the purpose of this class is. In general comments for declarations should always describe the purpose of the declared object.

Now I have been saying why you should comment your code and how you can write good comments. What is still missing are some words on the form of comments. I have noticed that C++/C# beginners (I did so, too) often use strange constructs to highlight comments. Like this:
Code:
 
//***********************************************
//* This function fulfills an exemplary purpose *
//***********************************************
void FooBar();

What I am talking about are the many asterisks (*). This habit probably results from constructs like this, which have been read in tutorials and code by others. I personally absolutely do not like such constructs. It takes quite a lot of time to write them, having them makes it more complicated to change comments and they do not fulfill any good purpose. Comments already salience due to their green color (which they have in almost every IDE by default). Comments, which stand at the beginning of a function or a class definition can be recognized without such constructs, since they are not indented. The comments simply do not need to be highlighted like this. You should better use more empty lines instead. They can highlight comments just as well.

Other than that comments should use full sentences in my opinion (although you may have a different opinion in this point) and they should be specific enough. A bad example would be "saves a model", while a good one would be "This function saves a model to a file". I made used to ending comments with a dot exceptionally, if they contain multiple sentences and I find that quite practical. Additionally comments should always have their own lines of code in my opinion. Here is a bad example followed by a good one.
Code:
 
// A vertex
struct SVertex{
D3DXVECTOR3 Position; // The position
D3DXVECTOR3 Normal; // The normal
D3DXVECTOR2 TexCoord; // The texture coordinate
};

Code:
 
// This structure represents a vertex with a default vertex format
struct SVertex{
// This 3D vector specifies the position in mesh space
D3DXVECTOR3 Position;
// This 3D vector specifies the normalized normal vector in mesh space
D3DXVECTOR3 Normal;
// This 2D vector specifies the texture coordinate
D3DXVECTOR2 TexCoord;
};

The good example is almost a bit exaggerated, but I hope you get the point. One last rule is that you should start a new line for your comments, if they are too long to match into the editor window otherwise. If you follow all these rules your code will be a lot easier to read and to use, since it is always easy to find out what a function or a piece of code does thanks to the comments. Additionally you can write it easier, since you enforce yourself to make up a clear idea of what you want to code next. Finally to give another good example here is the example code above again, but this time with good comments.
Code:
 
// Input-output-streams are needed to get input from the console and to output to it
include <iostream>
// To avoid the necessity of writing std:: all the time this namespace is used now
using namespace std

// This array contains five integer values, which will be output to the console
int pValue[5]={3,2,7,9,4};
// This short text describes what the application does and is output at the beginning
char ProgramStartText[]="This application prints several numbers. Enter 1 now to print the numbers with indices or 0 to print exceptionally the numbers.";

// This function outputs the values saved in pValue to the console. If PrintIndices is true it does
// print an array index before every value.
void PrintValues(bool PrintIndices){
// If PrintIndices is true print all values with index before each of them
if(PrintIndices){
for(int i=0;i<5;i++){
cout<<i<<pValue[i]<<endl;
}
}
// Otherwise just print the values
else{
for(int i=0;i<5;i++){
cout<<pValue[i]<<endl;
}
}
}


// This is the program entry point. The application asks the user whether indices should be
// printed and does the printing then.
int main(){
// Output the description
cout<<ProgramStartText<<endl;
// Ask the user whether indices should be printed
bool PrintIndices;
cin>>PrintIndices;
// Print the array of values
PrintValues(PrintIndices);
// The application has finished successfully
return 0;
}



Other aspects of good coding style
Last but not least there are also several other aspects of good coding style. First of all I want to say that you should always indent your code properly. Most IDEs do this automatically for you, so it is not much work anyway. C++ and C# compilers do not distinguish between different white spaces (meaning that it does not matter for the compiler whether you indent or not), but it simply makes the code easier to read. Another aspect of good coding style is how you place your brackets. Especially for curly brackets this is important. I recommend that you either place every curly bracket in its own line, or that you place opening curly brackets at the end of lines and closing curly brackets in their own lines (this is what I am doing as you can see in the code above). Doing so makes it easier to recognize where particular scopes start or end. Other than that I recommend that you use curly brackets after every if, while or for statement, even if they will contain just a single statement. This makes it easier to read the code, it makes it more clear, avoids mistakes and makes it easier to add code to the scopes of the statements.

You should also use enough spaces and new lines in your code. Do not try to keep it compact by not doing so. When I was a beginner I used to have extremely compact code. It was hard to read and did not look good. You do not need to save space, a bit of scrolling does not take long and IDEs offer many tools to jump to particular declarations or definitions in code. So simply take the place you need to lead the eye of the reader to the parts that matter. The code above can be considered a good example regarding these aspects of coding style. For this reason I will not give another good example, but just another bad example. So this is how the function PrintValues(...) would look in bad coding style (regarding the aspects I have talked about recently):
Code:
 
void PrintValues(bool PrintIndices){
if(PrintIndices){
for(int i=0;i<5;i++)
cout<<i<<pValue[i]<<endl;}
else{
for(int i=0;i<5;i++)
cout<<pValue[i]<<endl;}}



Languages in code
One last thing that needs to be mentioned in a tutorial about good coding style is how language should be used (I am not talking about programming languages, but about "speech languages"). Language is relevant for two things in source code: The naming of variables, functions, classes and other objects and for comments. I recommend that you always use English for both. English is a very good language for programming for the following reasons:

  • It does not use any special characters. For example in German you have ä, ö, ü and ß, which must not be part of names in C++.
  • It is a very lean language. If you formulate something in English, the result usually is significantly shorter than in most other languages. Especially simple logical relations can be formulated very well and with few words.
  • Almost all special terms related to programming are English. If you do not program in English every third word will be English though.
  • Every good programmer knows English. If you code in English everybody will be able to use your code.
These are especially good reasons why you should use English names, but comments should always use the same language. It is easier to connect names with comments then. If the names describe things in English, but the comments do not, you always have to translate, before you can make a connection between the comment and the name. Last but not least it is a good practice to get better at English, isn't it ;) .


Final words
This tutorial has become a lot longer than I had expected. I have seen code with inconsistent style and bad names very often and I used to think something like "Why can't this guy just code a bit cleaner.". Having written that much about this topic now I realize that a good coding style is not as self-evident as I had thought. Developing your own coding style can take a while, but it is very important. Without a consistent coding style your work is less efficient, more fault-prone, harder to reuse and harder to maintain. For this reason I hope that I have been able to help you in doing so. Many things I have talked about are a matter of taste, but for most of them I said that they are one. The three things, which are not a matter of taste or opinion are that names should be chosen in a consistent and descriptive way, that precise comments are helpful for reading and writing and that code should be indented and not too compact. So for your own good, keep these points in mind.


With friendly regards,
Dr. Best

P.S.: I did not check the example code for mistakes using a compiler, since it is more about how it looks than about how it works anyway.
Offline Profile Quote Post Goto Top
 
skarik
Member Avatar
kitten eating scum
[ *  *  *  *  *  * ]
Huzzah for readability!

(Nice tut.)
Blog|EHS
Offline Profile Quote Post Goto Top
 
Rixeno
Member Avatar
Teo-Carliss
[ *  *  *  *  * ]
Wow, that was long, but very useful, especially when it would come to new programmers in any language... My GML coding style was close to that when it came to description, indenting and consistency (a little)... I liked the tips on naming the variables/functions/classes/arrays/etc. and I'll try to write like that. :thumb_up:
Report spam the nanosecond you see it.
Ninjas are everywhere, for all you know there are eleven behind you right now, including me...
Offline Profile Quote Post Goto Top
 
Dr. Best
Member Avatar
Administrator
[ *  *  *  *  *  * ]
Thanks for the positive feedback. I just added in another part. It was planned from the beginning, but I simply forgot about it, while writing the tutorial. The new part is the last but one and it explains why it is best to code and comment in English.
Offline Profile Quote Post Goto Top
 
« Previous Topic · Tutorials · Next Topic »
Add Reply