Coding Style |
||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
||||||||||||||||
|
||||||||||||||||
This coding style has developed as a result of years of programming in many languages.
If you're writing for a company: do things their way! Since this site is my own code, by me and for me, I am free to find what is most productive.
Glance at it and it will seem evil and mad in the same way that Extreme Programming or just plain Unit Testing seem, to those who fail to see the point.
Looking at this style may hurt your eyes for a day, but afterwards, you'll be reading my C++ in blocks instead of lines.
Use the style and you'll start to notice it helps you correct bugs as you type.
Your use of the scroll bars / Mouse Wheel and printer may drop dramatically too!
A coding style should maximise your coding efficiency.
It should enable you to read and understand sections of code,
then blocks of code,
then lines of code,
then instructions.
and it should be used consistently.
These are the main principles (which will only benefit you if you use more than you ignore):
- One line should do something.
- Only use spaces where necessary.
- Use indentation! But know that two spaces for an indent is enough!
- Make the editor convert Tab characters to space characters.
- Only leave blank lines when it means something.
- Line things up vertically (left and right justified).
- Keep multiple closing braces together (except end of function, class or struct).
- Make only essential comments and when comments are essential, make them!
- Make comments visually separate (bright pink) from the code.
- Declare variables where they are used. Learn to hate 'C' (step into the light and adopt 'D').
- Use Capitalised naming (IncludingTheFirstLetter).
Some languages (like LISP) require the programmer to efficiently manage parentheses and indents to great depths.
Here's a little routine written using the coding style:CString GetNextWord() { char c; if(Msg.GetLength() > end-Msg) { stt=end; while(c=*stt++) { if(isLetter(c)) { end=stt; while(c) { if(!isLetter(c=*end++)) { if(end-stt==1) break; return Msg.Mid(stt-Msg-1,end-stt); } } } } } return ""; }(The above code has a deliberate bug, so don't use it!)
Generally, if you have lots of indenting, you should have more function calls.
Sometimes, however, in small busy sections (as above) or things like opening DocFiles (where all sorts of things need releasing after use) deep indents are useful.
The first thing to notice is the "Lateral Thinking": a line of code does as much as possible which provides visual cues to the functionality of the block of code as a whole.
The code is also (vertically) a discrete lump which is easily identifiable when scrolling though the file.
If you have some awful bug - it's not a complex enough example to be real, but say you have a compiler problem with:if(!isLetter(c=*end++)) {you put it on separate lines while you debug:if( !isLetter( c= *end++ ) ) {The compiler will tell you which line the problem is: you correct the problem, then put the line back together so the code can be read as a block again.
Also laterally, the minimum number of delimiters are used, to maintain readability of the block of code.
This could be writtenif ( ! isLetter ( c = *end++ ) ) { ... } else { ... }but if there is no value in introducing spaces and delimeters and non-functional lines then don't!
If a single line of Assembly Code is atomic (one instruction), then a single line of C++ should be molecular: the next level of discrete operation up.
This is never done (except when debugging):if(Condition) Operation();because it's not clear: I see the if with a blank line under it and look for the closing brace but there isn't one.
After debugging with a break-point put it back as one line!
The following patterns of "if" block are used:x=(Condition ? y : z); if(Condition) Operation1(); else Operation2(); if(Condition) Operation1(); else Operation2(); if(Condition) { Operation1(); Operation2(); }else{ Operation3(); Operation4(); }The last two examples are equivalent:if(Condition) { for(;;) { Operation1(); Operation2(); } }else{ Operation3(); Operation4(); }or:if(Condition) { for(;;) { Operation1(); Operation2(); } } else{ Operation3(); Operation4(); }Similarly vertical minimalism exists: only insert blank lines where they mean something. Always use an indent of 2 spaces (the minimum that give a definite indent) and make tab characters turn into spaces. Tab characters slow your editing and at an indent of 2 they don't make much difference to the source file size (like that matters anyway)!
Column Cut and Paste (hold down Alt and drag with the mouse) in Visual Studio means you have great coding speed advantages if you keep things in columns:
output[j ]=(unsigned char)( input[i] & 0xFF); output[j+1]=(unsigned char)((input[i]>> 8) & 0xFF); output[j+2]=(unsigned char)((input[i]>>16) & 0xFF); output[j+3]=(unsigned char)((input[i]>>24) & 0xFF);This principle needs restricting when declaring variables but is still useful in small blocks.
Most editors have a function to show you the matching bracket (), brace {} or parenthesis () - so use it to make the code fit on one screen and put all the close braces together. Of course the braces still have to comply with the indentation for the vertical cue of a missing one... and this reverses their order... but don't panic! All you need to know is that you have the right number of braces!
Beginners may like to know that indenting consistently is one of the best way to avoid bugs.
You'll see people write:} //End If } //End while } //End If } //End while } //End If return ""; }but that has no value; It actually makes you study the ends of the loops to see if there's any code amongst the comments. You may not get the whole function on-screen at once and have to print the code... "Time wasting"...
You can be sure that someone will update something and forget to update one of those trailing comments too, and leave you completely bewildered when it's your turn to find the bugs.
So don't spend time writing verbosities, and that stops you spending time printing and reading them :-)
{he said, trying to hold the audience of verbosiphiles by reiterating}
The same principles apply to Comments.
Only use comments where the code isn't obvious or at least deducible.
The above code section would implement a Word-Finding algorithm (if it didn't have the deliberate bug)...
So if you see:if(...) { ... while(...) { if(...) { ... while(c); if(...) { if(...) break; return ...; } } } } } return "";you're likely to be looking at a Word-Finding section again. (Letting your brain do the pattern-matching thing that it's best at).
Code is not meant to be a tutorial, so no comment should be necessary for the code - the function Name is self-explanatory.
If, however the function implemented a standard algorithm, like Binary Chop, the only comment necessary for this routine would be:// Using Binary Chop AlgorithmWhen releasing my code into the Wild Wild Web, The Binary Chop Algorithm would be a distinct class with a comment at the top of the header file describing the algorithm.
Multi-line comments don't need indents or starry borders, so use:
/* Lots of comments here. Kept to one sentence per line where possible because you have a high-resolution screen and a scroll bar and you know how to use them. Magenta comments allow your mind to flip between seeing code or comments... You probably have to try it! Setting the background colour for strings to silver is also a great help in visually distinguishing code sections: */CString S("OK" + ' ' + "here's coloured" + ' ' + "String Number " + itoa((x+2y-z*z)/4a) + " 4U.");
Use the minimalist naming convention of capitalism: VeryLongName. Occasionally slip a p in for a pointer if it's not obvious:char Buffer[128]; char* pBuffer=Buffer;but generally better to give pBuffer a short obvious temporary name like ptr(pointer), src(source), dst(destination), it(iterator), S(string) if it was short-lived.
Most languages have conventions for the position of the declaration of variables and the naming of those variables,
but some scripting languages remove the need for any variable definitions.
Don't list local variables at the start of functions: declare variables as close to their first usage as possible.
Obviously it's important in C++ is that the Type of the variable is as useful as possible,
but do you trust a programmer who leaves left-over variable declarations for variables that are no longer used?
If a variable in no longer used, its definition should be easy to remove as well.
You'll see things like:for(int i=0; i<5; ++i) {++i is used here instead of i++ here because that's how you say it: "increment i" - and also because pre inc/dec compile to faster code than Post inc/dec because the latter need to store the old value before returning...
That doesn't really matter with modern PCs and C++; if you needed speed you'd use Assembly language, but knowing you're doing the best you can do gives satisfaction and that's the best reason for habitual optimisations!
Having said that, direct initialisation is a little faster:CString S("Hello")rather thanCString S="Hello"orCMe::CMe() : x(0) {}rather thanCMe::CMe() {x=0;}but sometimes you can make code much more readable when you have many similarly used variables with:
int a1,a2,a3,a4,b1,b2,b3,b4,c1,c2,c3,c4,d1,d2,d3,d4; a1=a2=a3=a4=b1=b2=b3=b4=c1=c2=c3=c4=d1=d2=d3=d4=0;A last comment on coding regards formulae.
There are two types of formula: obvious and cryptic.
If it's already cryptic, there is little point in making it pretty so just make it safe.
Write the shortest possible formula until you know that a formula may be messed up by a programmer.
When you need to make sure that you and the compiler will always agree on a formula, use a lot of parentheses. If you minimise the spaces, your eyes can learn to see blocks of parenthesised code instantly.
It still goes on one line once it's debugged!
Here are some examples:*dst++=Encode(((b2<<2) & 0x3C) | ((b3>>6) & 0x03)); int GetPin() const {static const BYTE Pins[]={12,16,16,20,24,30,36,42,48,56,64,72,80,80}; return Pins[GetLoadGroup()==-1 ? 0 : GetLoadGroup()];} int GetDevLength() {return int(2*(GetTheta()*(0.5*GetClampID() + 2.5*GetFlatThick()) + 0.5*GetCBC() - (0.5*GetClampID() + 2.3*GetFlatThick()) * sin(GetTheta())) + 4*GetPin()+0.5);} double GetScantlingWeight() {return GetDevLength()*GetFlatWidth()*GetFlatThick()*Density;} double GetWeight() {return 2*GetScantlingWeight()+GetPin()*(PI*GetPin()/3*GetGap()+4.5*GetPin()*GetPin())*Density;}These formulae should have fully explanatory comments near them.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.