views:

1948

answers:

38

I've been involved in developing coding standards which were quite elaborate. My own experience is that it was hard to enforce if you don't have proper processes to maintain it and strategies to uphold it.

Now I'm working in, and leading, an environment even less probable to have processes and follow-up strategies in quite a while. Still I want to uphold some minimum level of respectable code. So I thought I would get good suggestions here, and we might together produce a reasonable light-weight subset of the most important coding standard practices for others to use as reference.

So, to emphasize the essence here:

What elements of a C++ coding standard are the most crucial to uphold?

  • Answering/voting rules

    • 1 candidate per answer, preferably with a brief motivation.

    • Vote down candidates which focuses on style and subjective formatting guidelines. This is not to indicate them as unimportant, only that they are less relevant in this context.

    • Vote down candidates focusing on how to comment/document code. This is a larger subject which might even deserve its own post.

    • Vote up candidates that clearly facilitates safer code, which minimizes the risk of enigmatic bugs, which increases maintainability, etc.

    • Don't cast your vote in any direction on candidates you are uncertain about. Even if they sound reasonable and smart, or on the contrary "something surely nobody would use", your vote should be based on clear understanding and experience.

+4  A: 

If the toolchain in use (or projected use) has an inefficient implementation of exceptions, it might be wise to avoid their use. I've worked under such conditions.

Update: here is someone else's rationale for "Embedded C++", which seems to exclude exceptions. It makes the following points:

  • It is difficult to estimate the time between when an exception has occurred and control has passed to a corresponding exception handler.
  • It is difficult to estimate memory consumption for exception handling.

There is more elaborate text on that page, I didn't want to copy it all. Plus, it's 10 years old so it might be of no use any longer, which is why I included the part about the toolchain. Perhaps that should also read "if memory is not considered a major problem", and/or "if predictable real-time response is not required", and so on.

unwind
Very interesting. Please edit and elaborate a bit more on why this is so.
sharkin
domain specific argument. if you're not using exceptions, you need to understand why they are awesome, have metrics documenting why they aren't 'whatever' enough and how much more 'whatever' they need to be, and be really upset that you can't use them. Only then should you stop using them.
Dustin Getz
There are very specific reasons for not using exceptions, however, EC++ is not the definitive source I would use. EC++ bans lots of features from C++ and actually caused the ISO C++ Committee to respond to the claims that were made by the standard.
Richard Corden
The only valid reason I've read to not use exceptions is when we are dealing with a legacy code that does nothing about exceptions -- see google C++ Coding Standard.
Luc Hermitte
Richar Corden is right. Just reading http://en.wikipedia.org/wiki/Embedded_C%2B%2B , and that they removed namespaces, templates and C++ casts show AC++ was a joke more than anything else
paercebal
good point about different standards required for different environments. Aircraft avionics will require different ones to a website, for example.
gbjbaanb
+4  A: 

Method and variable names in a common naming scheme for consistency; I don't tend to be bother much by anything else while reading source.

Jasper Bekkers
A: 

No tabs (allows better use of external/other tools) and a fixed spaces inserted for tabs.

kenny
subjective style/formatting. i prefer tabs.
Dustin Getz
I agree with this. Tabs change formatting in different editors, while spaces are consistent.
Thomas Owens
-1. Tabs should always be used for indentation, everything else can be spaces, but tabs should always be used for indentation.
ephemient
@owens, thats the point, my editor can show it to me the way I like it.
Dustin Getz
Tabs-vs.-spaces is a holy war. No need to argue it here.
Kristopher Johnson
Tabs are a form of encapsulation and are preferable to spaces. Anyone can change the 'size; of their tabs, but not so with spaces.
Ed Swangren
This is one area you do need to pick a rule (or _actually put in a tool_, not just handwave and say a tool _could_ do that). Code has to be either all-tabs or all-spaces, anything in between is broken.
soru
A: 

Curly braces required if you have more than one step of indentation:

if (bla) {
  for (int i = 0; i < n; ++i)
    foo();
}

This helps to keep indentation in line with how the compiler sees the code.

Frederik Slijkerman
I like this better than "always use curly braces."
Ferruccio
'not always' is one of the choices that burdens the brain understanding the code. 'always' leads to 1. less choice=>less time spent choosing and 2. less occasion to write bugs overlooked by indentation (especially for people who also write python...)
xtofl
format for readability with braces or whitespace, but i certainly think requiring them is arbitrary.
Dustin Getz
@xtofl: even if you're supposed to always do it one way, the compiler takes more than that, so ignorant cases are inevitable. i'd argue brainparsing braces doesn't burden the brain - it becomes rote.
Dustin Getz
+31  A: 

Keep functions to a reasonable size. Personally, I like to keep functions under 25 lines. Readability is enhanced when you can take a function in as a unit rather than having to scan up and down trying to figure out how it works. If you have to scroll to read it, it makes matters even worse.

Ferruccio
It's indeed important. And it is a consequence of : "a function must do only one thing, and do it well"
Luc Hermitte
I've worked on jobs that had McCabe complexity limits for all functions. The cutoff was kind of arbitrary some times, but the overall effect forced smaller routines, which was great.
T.E.D.
+11  A: 

Only trivial use of the ? : operator, i.e.

float x = (y > 3) ? 1.0f : -1.0f;

is ok, but this is not:

float x = foo(2 * ((y > 3) ? a : b) - 1);
Frederik Slijkerman
I agree with the spirit of this rule, but I think the non-trivial example you give is perfectly fine. I would disallow nested ternary operators.
Ferruccio
Readability is the key.
xtofl
The ternary operator should be reserved to the cases where the result is more important than the test. BTW, it quite useful to use const pro-activelly.
Luc Hermitte
Really, the second example violates my rule of only one thing per line. An assignment with the operator? would make this code readable. In shorter terms, this is'nt an example of bad operator? use.
kenny
This shouldn't be in a coding standard because there's no objective definition of good/bad. It'd be covered in an introductory statement "Write clear, maintainable code."
280Z28
I agree with Ferruccio. That example is ok, it is the nested form that gives me headaches.
Ed Swangren
+0: I disagree. Arithmetic should nearly always be preferred to conditionals, even inlined. Reduces **cyclomatic complexity**
Pavel Radzivilovsky
A: 

Sort functions in class declarations and definitions by name. This makes it easier to locate them in the .cpp file. Also, it frees your mind because you don't have to think about where to put your new function.

Frederik Slijkerman
i group my methods by functionality - e.g. string::begin() would be adjacent to string::end()
Dustin Getz
Why not plop all your functions, class definitions, etc. into one file and alphabetize them? Why not alphabetize #include lists? Why not alphabetize instructions?
strager
Yeah, this is silly. I agree with Dustin.
Ed Swangren
+30  A: 

Make sure that your compiler's warning level is set high enough (/Wall preferably) so that it will catch silly mistakes like:

if (p = 0)

when you really meant

if (p == 0)

so that you don't need to resort to even sillier tricks like:

if (0 == p)

which degrade the readability of your code.

Ferruccio
... and compile with WISE (Warning is Error)
xtofl
Another way: If p is const, then if(p = 0) won't compile anyway, but this is another post...
paercebal
For moderation, see http://stackoverflow.com/questions/237719/I am surprised myself and not happy about how much of a nerve it hit. Nonetheless, I think it is a valuabe rule!
peterchen
+29  A: 

assert all assumptions, including temporary assumptions, like unimplemented behavior. assert function entry and exit conditions if nontrivial. assert all nontrivial intermediate states. your program should never crash without an assert failing first. you can customize your assert mechanism to ignore future occurances.

Use error-handling code for conditions you expect to occur; use assertions for conditions that should never occur. Error handling typically checks for bad input data; assertions check for bugs in the code.

If error-handling code is used to address an anomalous condition, the error handling will enable the program to respond to the error gracefully. If an assertion is fired for an anomalous condition, the corrective action is not merely to handle an error gracefully—the corrective action is to change the program's source code, recompile, and release a new version of the software. A good way to think of assertions is as executable documentation—you can't rely on them to make the code work, but they can document assumptions more actively than program-language comments can [1].

  1. McConnell, Steve. Code Complete, Second Edition. Microsoft Press © 2004. Chapter 8 - Defensive Programming
Dustin Getz
I would even add: compiletime assert where possible.
xtofl
Regarding Programming by Contract, see also B.Meyer book, and M.Wilson's Imperfect C++.
Luc Hermitte
I would even add: release-time check at nearly all places where you have debug-time assert(). The only reason to have one without the other is performance, which is seldom a resonable subject for optimization.
Pavel Radzivilovsky
+17  A: 

Curly braces for any control statement. (Thanks to own experience and reinforced by reading Code Complete v2):

// bad example - what the writer wrote
if( i < 0 ) 
    printf( "%d\n", i );
    ++i; // this error is _very_ easy to overlook!  

// good example - what the writer meant
if( i < 0 ) {
    printf( "%d\n", i );
    ++i;
}
xtofl
if (i<0) printf; \n ++i; //easy to spot
Dustin Getz
That's right. But not generally applicable for longer conditions and statements.
xtofl
shrug, i think its subjective. for early bailout type checks the braces just get in the way. i'd be annoyed if this was in a coding standard.
Dustin Getz
the indentation is more important than the braces - that's what shows you where something should be, the "++i;" will always stand out in well-indented code.
gbjbaanb
I agree. I see no advantage in leaving out the braces, only potential problems.
Ed Swangren
-1 shortening code is important. Proper indentation seems to solve these problems, and misindentation problems are easy to spot during CR.
Pavel Radzivilovsky
this is subjective, but in my book avoiding potential mistakes is more important than having less curly braces to look at
slf
+60  A: 

Prefer RAII.

STL's auto (and shared in boost & C++0x) pointers may help.

Aardvark
i would say "prefer RAII" - i certainly wouldn't require it in a standard.
Dustin Getz
Agreed. One place I worked people would argue symantics like "perfer". Eventually our standard was written like everything was an absolute requirement (dispite being able to deviate from it after a review).
Aardvark
Without RAII, it's quite difficult to write error-safe code that's easy to maintain.
Luc Hermitte
I wasn't even aware of the term "RAII" until recently. I always considered the concept as "common sense".
Aardvark
It amazes me how many C++ programmers don't know the RAII idiom. I've had to enforce this with a rule saying "Never use 'new' without checking with the team lead."
Kristopher Johnson
@kris, i like that a lot
Dustin Getz
+36  A: 

Use references instead of pointers where possible. This prevents constant defensive NULL checks.

Aardvark
But if you dereference a pointer to initialize the reference, make sure the pointer isn't NULL first!
Mark Ransom
Yes - but if you always used references you wouldn't have that pointer to begin with! I'm kidding... I've been bitten by the very issue you bring up many times.
Aardvark
+1 to the effect, not to the reasoning. i sometimes return null iterator, e.g. find(). is that bad? higher level languages that use reference types typically have a `None` object.
Dustin Getz
@Dustin Getz - so what reasoning would you like to see? I'll edit.
Aardvark
+8  A: 

Use a lint tool - i.e. PC-Lint. This will catch many of the 'structural' coding guideline issues. Meaning things that read to actual bugs rather than style/readability issues. (Not that readability is not important, but it is less so than actual errors).

Example, rather than requiring this style:

if (5 == variable)

As a way of preventing the 'unintended assignment' bug, let lint find it.

Steve Fallows
I advocate exactly this in my company. Lint should always be used, and will catch this problem. Plus, any experienced dev won't make this mistake anymore (not even a typo). Lastly, visually, the style with the constant first is hard on my eyes... so kudos, I agree.
Dan
Placing the constant first harms readability by diffusing the flow of information. I absolutely agree with letting the tools do their job here.
280Z28
+2  A: 

Whatever guidelines, make it very easy to recognize applicability: the less choice you have, the less time you loose choosing. And the easier it becomes to brainparse the code.

Examples of 'hard to recognize':

  • No braces if only one line in the conditional body
  • Use K&R brace placement for namespaces, but put brace underneath conditions in function definition code
  • ...
xtofl
+50  A: 

Use const identifiers by default. They provide guarantees for the reader/maintainer, and are way easier to build in than to insert afterwards.

Both member variables and methods would be declared const, as well as function arguments. const member variables enforce proper use of the initializer list.

A side-effect of this rule: avoid methods with side-effects.

xtofl
So easy to use, and so much more safety in the code. +1. In fact, this is more important than the amusing "use if(0 == p) instead of if(p == 0)"...
paercebal
Indeed, have p const, and you won't have to worry about `if (p=0)`.
Luc Hermitte
Voted up, but I'd suggest making it clearer that you are talking about *methods* as well as just data items.
T.E.D.
Good point about inserting const later - that's painful... +1
Aardvark
+6  A: 

Forbid t[i]=i++; f(i++,i);, and so on as there is no (portable) guarantees regarding what is executed first.

Luc Hermitte
I would have to look it up, but I can hardly believe the Standard leaves this construct undetermined.
xtofl
Luc Hermitte
Again, this shouldn't be in a written coding standard because it just clutters up the document with obvious information.
280Z28
Unfortunately this is not that obvious for everybody. Still your comment is interresting. It raises a new question: where does the obvious starts and where does it ends? Because in the end, a standard is about safety rules that are well known and understood by a few people.
Luc Hermitte
+44  A: 

Use C++ casts instead of C casts

use:

  • static_cast
  • const_cast
  • reinterpret_cast
  • dynamic_cast

but never C-style casts.

How it clearly facilitates safer code, which minimizes the risk of enigmatic bugs, which increases maintainability, etc.

Each cast has limited powers. E.g., if you want to remove a const (for whatever reason), const_cast won't change the type at the same time (which could be a bug difficult to find).

Also, this enables a reviewer to search for them and then, the coder to justify them if needed.

paercebal
Moreover a C cast is always handled as a reinterpret_cast<> when dealing with forward declarations. And C casts is unable to correctly downcast or crosscast.
Luc Hermitte
Yes, absolutely, but I don't know that I would recommend reinterpret\_cast either...
Ed Swangren
This conflicts with the "use weakest possible cast" rule, as reinterpret_cast is stronger than C-style cast and sometimes you have nothing between it and static_cast other than c-style cast. For instance, converting a pointer to a byte buffer to a struct{} (regular binary parsing pattern). Would you recommend a reinterpret_cast? I think not.
Pavel Radzivilovsky
+22  A: 

Side note: Do not impose SESE (Single Entry Single Exit) (i.e. do not forbid more than one return, the use of break/continue/...)

In C++, this is an utopia as throw is another return point. SESE had two advantages in C and exception-less languages:

  • the deterministic release of resources that is now neatly handled by the RAII idiom in C++,
  • making functions easier to maintain, that should not be a concern as the functions must be kept short (as specified by the rule of "one function, one responsibility")
Luc Hermitte
I disagree that single point of return ever made functions easier to maintain. It usually just complicates the function body unnecessarily.
Greg Rogers
Greg, I totally agree with you. I don't think a SESE function is easier to read. Some think so, but it is not my case either.
Luc Hermitte
Modding up, but I generally don't mind such requirements, because there is almost *never* more than one exit point from a routine or loop. There might be more than one place the exit *came from*, but never more than one point it goes to. Without goto's *all* routines are single exit point.
T.E.D.
..another way to look at this is that if you have exceptions, *all* your routines and loops are multiple exit anyway. :-)
T.E.D.
+31  A: 

Use vector and string instead of C-style arrays and char *

Use std::vector whenever you need to create a buffer of data, even if the size is fixed.

Use std::string whenever you need to have a string.

How it clearly facilitates safer code, which minimizes the risk of enigmatic bugs, which increases maintainability, etc.?

std::vector: The user of a vector can always find its size, and the vector can be resized if needed. It can even be given (through the (&(myVector[0])) notation) to a C API. Of course, the vector will clean after itself.

std::string: Almost the same reasons above.And the fact it will always be correctly initialized, that it can't be overrun, that it will handle modifications gracefully, like concatenations, assignation, etc, and in a natural way (using operators instead of functions)

paercebal
The problem with this is that I write a lot of OS-interface code, which makes heavy use of char arrays. Having some kind of standard that required me to use std::string for objects that are never accessed as anything but char arrays would lead to a lot of dumb code.
T.E.D.
The solution, then, would be to interface the OS API with C++ friendly methods. I worked on such thing to interface my C++ code with raw Win32 API, and usually, it is Ok. And my C++ interface checked for "GetLastError" and logged the info, which no developer did on its own!
paercebal
for domain specific solutions (drivers and such), of course you can prefer native APIs, so long as you can justify it.
Dustin Getz
@Dustin Getz: Of course, you're right. Drivers are low level APIs themselves, the constraints are very specifics, so the solution usually are, too
paercebal
What is `std::string`? Maybe you meant `std::wstring`? :)
280Z28
@280Z28 : I assume you are joking, of course. For those not knowing the difference between std::string and std::wstring, the first is a std::basic_string templated to "char" and the second is the same std::basic_string, but this time templated to "wchar_t".
paercebal
+8  A: 

Never use structs without proper constructors

structs are legal C++ constructs, used to aggregate data together. Still, the data should be always properly initialized.

All C++ structs should have at least a default constructor, which will set its aggregated data to default values.

struct MyStruct // BAD
{
   int i ; bool j ; char * k ;
}

struct MyStruct // GOOD
{
   MyStruct() : i(0), j(true), k(NULL) : {}

   int i ; bool j ; char * k ;
}

And if they are usually initialized in some way, provide a constructor to enable the user to avoid a C-style struct initialization:

MyStruct oMyStruct = { 25, true, "Hello" } ; // BAD
MyStruct oMyStruct(25, true, "Hello") ;      // GOOD

How it clearly facilitates safer code, which minimizes the risk of enigmatic bugs, which increases maintainability, etc.?

Having struct without a proper constructor leaves the user of this struct the task of initializing it. So, the following code will be copy pasted from function to function:

void doSomething()
{
   MyStruct s = { 25, true, "Hello" } ;
  // Etc.
}

void doSomethingElse()
{
   MyStruct s = { 25, true, "Hello" } ;
  // Etc.
}

// Etc.

Which means that, in C++, if you need to add a field in the struct, or change the order of the internal data, you have to go through all these initializations to verify each is still correct. With a proper constructor, modifying the internals of the structs is decoupled from its use.

paercebal
The interesting part about this answer is that I disagree with the title, but very strongly agree with the text.
T.E.D.
You're right: I'll change the title...
paercebal
A constructor makes the type a non-POD. How do you create a POD struct?
MSalters
@MSalters: Good question, the answer being already given above, but not clearly, it's true: In C++, avoid POD structs as much as possible...
paercebal
Note that "PODs" structures/classes in C++ have little to none advantage over non-PODs ones. The only moment you'll consider using a POD (or a POD-like) is when interfacing with a C API.
paercebal
If you really need a POD, you can use inheritance to create the version with a constructor. Useful also if the struct is part of a library or API you can't modify.
Mark Ransom
@Mark Ransom: Yes, this solution was explored for a Win32 API struct in the post: http://stackoverflow.com/questions/112085/is-this-c-structure-initialization-trick-safe#112100
paercebal
If you are simply initializing the values of `i` and `k` to defaults for the type, you should use `i(), k()` instead. That way the items in the initializer list that have particular meaning to that constructor are the only ones with specific values in parentheses.
280Z28
@280Z28 : Remember the types of i and k are C/C++ built-in types, which means that they ARE NOT initialized by default. So, using the "i()" notation on C/C++ built-in types means you'll probably have problems (i.e. bugs) later because of semi-random values poping out... ^_^ ...
paercebal
+2  A: 

A point should be dedicated to explain the difference between value semantics and entity semantics. It could provide the typical code snippets about how copy is handled is the various cases.

See also Checklist for writing copy constuctor and assignment operator in C++

Luc Hermitte
+4  A: 

Public inheritance must model The Liskov Substitution Principle (LSP).

Code reuse/import without substituability must be implemented with private inheritance when a very strong coupling makes sense, or with aggregation otherwise.

Luc Hermitte
+8  A: 

Don't add types or functions to the global namespace.

marijne
This is a good suggestion, but the rationale is missing: you pollute the global namespace by doing it and thereby potentially causing a whole lot problems if the name exists elsewhere.
twokats
+1  A: 

Beware of C API

The C API can be very efficient, but will need exposed raw data (i.e. pointers, etc.), which won't help the safety of the code. Use existing C++ API instead, or encapsulate the C API with C++ code.

e.g.:

// char * d, * s ;
strcpy(d, s) ; // BAD

// std::string d, s ;
d = s ;        // GOOD

Never use strtok

strtok is not reentrant. Which means that if one strtok is started while another is not ended, one will corrupt the "internal data" of the other.

How it clearly facilitates safer code, which minimizes the risk of enigmatic bugs, which increases maintainability, etc.?

Using C API means using raw types, which can lead to interesting bugs like buffer overflow (and potential stack corruption) when a sprintf goes too far (or string cropping when using snprintf, which is a kind of data corruption). Even when working on raw data, malloc can be easily abused, as shown by the following code:

int * i = (int *) malloc(25) ; // Now, I BELIEVE I have an array of 25 ints!
int * j = new int[25] ;        // Now, I KNOW I have an array of 25 ints!

Etc. etc..

As for strtok: C and C++ are stack-enabled languages, that enable to user to not care about what functions are above his own on the stack, and what functions will be called below his own on the stack. strtok removes this freedom of "not caring"

paercebal
+22  A: 

Premature optimization is the root of all evil

Write safe and correct code first.

Then, if you have performance problems, and if your profiler told you the code is slow, you can try to optimize it.

Never believe you will optimize snippets of code better than the compiler.

When looking for optimizations, study the algorithms used, and potentially better alternatives.

How it clearly facilitates safer code, which minimizes the risk of enigmatic bugs, which increases maintainability, etc.?

Usually, "optimized" (or supposedly optimized) code is a lot less clearer, and tend to express itself through raw, near-the-machine way, instead of a more business-oriented way. Some optimizations rely of switchs, if, etc., and then will be more difficult to test because of multiple code paths.

And of course, optimization before profiling often lead to zero performance gain.

paercebal
A: 

The best standards are those that are small and tightly focussed on what really matters to making quality code. They do not try to teach coding, they do not try to force a particular way of coding. They generally stick to consistency features and subjective reviews (eg, if the rest of your team think a piece of code is readable, fits with the consistency rules, and is commented, then its always going to be good code)

So to re-emphasise: consistency - naming convention, whitespace management, commenting blocks, directory structure. Nothing else really matters

Edit for Dustin: the big problem with standards comes with the exceptions. If you have a standard that says "1 statement per line", you cannot write the following made-up example:

SetColText(1,"col1"); SetColWidth(1, 10);
SetColText(2,"col1"); SetColWidth(2, 10);
...
SetColText(9,"col1"); SetColWidth(9, 10);

But I'd say that was more readable, and therefore less error-prone that splitting them up. (I'm sure you can come up with better examples).

This is my point - telling people how to write code, and how to format it to strict rules is always going to fall over in ways and places you didn't anticipate. So its far better to trust your coders to do it right after enforcing a few rules. If they have a few rules to follow, they will write good, disciplined code so you won't need the rest of the crappy rules.

You see some standards that go on for pages and pages. (The Philips C# one is 48 fecking pages long!)

So, given that you have a team of quality coders, what do you need to do to make it easier to work with their code? the answer is always consistency of 'where' they put the code, not how they write it. eg. you always have a bin, and obj directory in your project is a good standard. You can pick up any project and know where things are .. unlike someone building all his binaries in his c:/mybin directory because its easier for him.

gbjbaanb
i dunno, man. one you can brainparse c++ you can brainparse ANY c++. i think the important values relate to managing complexity. i guess i agree with the subjective review part, but i sure don't agree with the latter part
Dustin Getz
+27  A: 

Know who is owner of that memory.

  • create objects on stack as much as possible (no useless new)
  • Avoid transfer of ownership unless really needed
  • Use RAII and smart pointers
  • If transfer of ownership is mandated (without smart pointers), then, document clearly the code (the functions should have a non-ambiguous name, always using the same name pattern, like "char * allocateMyString()" and "void deallocateMyString(char * p)".

How it clearly facilitates safer code, which minimizes the risk of enigmatic bugs, which increases maintainability, etc.?

Not having a clear memory ownership philosophy leads to interesting bugs or memory leaks, and time lost wondering if the char * returned by this function should be deallocated by the user, or not, or given back to a special deallocation function, etc..

As much as possible, the function/object allocating the memory must be the function/object deallocating it.

paercebal
I'd add that the "owner" policy should also include a "transfer of ownership" policy.
twokats
+1  A: 

Limit the types you use

If you need to use an integer type, choose one and keep it. This will avoid the problems associated with mixing of short, int, long, etc.. types.

// BAD
int i ;
long j ;
short k ;

// GOOD (if you choose the "int" as integer)
int i ;
int j ;
int k ;

The same goes for real types: Choose one (e.g. double), and do not use another.

Etc.

Note: There is still the issue of signed/unsigned, which can't always be avoided, and the fact STL use its own integer types (i.e. std::vector::size_type), but all the remaining code should not mixing.

Note 2: You could use typedef to "choose" your prefered type for signed integer and real numbers. This would enable a low-cost change if needed.

How it clearly facilitates safer code, which minimizes the risk of enigmatic bugs, which increases maintainability, etc.?

Some bugs are created by comparing unsigned type to signed types, mysterious loss of precision, or integer under/overflow.

Compilers usually send warnings at compile time, but then, the usually answer is to "cast" the warning away, which can help hide the error.

Edit

plinth made an useful comment I'll copy paste here:

Having written a lot of code that has to interact with things at the hardware level, I can't say much for this guideline. For this level of work, I prefer the integral types to be abstracted to names that include the precision (ie, int16, uint16, int32, uint32, etc.) – plinth Aug 18 at 20:50

plinth is right, of course. Sometimes you have to deal with int16, uint8 and other "precisely defined" types.

This does not invalidate the post above, only complete it.

The source of the bug is mixing different types (converting unsigned char into int, for example), thus, this kind of mixing must be avoided. The following rules thus apply:

  • Choose one generic integral type (e.g. int), and stick to it when dealing with generic integers (the same can be said about reals)
  • If (and only if) you need exact types (like uint8 or int16), use them
  • Never mix different types.
  • If you really must mix different types, then be very very cautious.

Below is an example of code that would break:

void * doAllocate(uint32 i)
{
   // try to allocate an array of "i" integers and returns it
}

void doSomething()
{
   uint32 i0 = 225 ;
   int8   i1 = 225 ;  // Oops...

   doAllocate(i0) ;   // This will try to allocate 255 integers
   doAllocate(i1) ;   // This will TRY TO allocate 4294967265
                      // integers, NOT 225
}
paercebal
Having written a lot of code that has to interact with things at the hardware level, I can't say much for this guideline. For this level of work, I prefer the integral types to be abstracted to names that include the precision (ie, int16, uint16, int32, uint32, etc.)
plinth
@plinth : This is a different problem. In the common cases, the coder wants to put an integer into a variable. Mixing types, both in size and in sign is a recipe to disaster. Usually, the coder needs a simple type able to hold all possible values. Choosing one and ONLY one is a good thing to do. <to be continued> ...
paercebal
@plinth : <continuing> ... In your case, because you are working near the hardware, you NEED to use the exact type needed, thus, "int" or "short", which potentially varies with each compiler, is the wrong thing to do, thus, the need to use the exact type, like int32 or uint8. <to be continued> ...
paercebal
@plinth : <continuing> ... But note that even near the hardware, converting a int8 into a uint32 will break unless you're very very careful. This means that in your case, instead of relying on ONE and ONLY ONE integer type like I advised, you must be either be careful to not mix them, or be very very careful when you DO mix them. Conclusion: Even if the solution I gave on my post (using one integer type only) can't be applied on your particular case, the reason (i.e. source of bugs when mixing) is still valid in your particular case, and thus, needs extra work/caution.
paercebal
@plinth : <continuing> ... But note that even near the hardware, converting a int8 into a uint32 will break unless you're very very careful. This means that in your case, instead of relying on ONE and ONLY ONE integer type like I advised, you must be either be careful to not mix them, or be very very careful when you DO mix them. <to be continued> ...
paercebal
@plinth : <continuing> ... Conclusion: Even if the solution I gave on my post (using one integer type only) can't be applied on your particular case, the reason described in my post (i.e. source of bugs when mixing) is still valid in your particular case, and thus, needs extra work/caution. This post is mostly aimed at coders who will think they are smarter just because they used an unsigned short instead of an int for "memory optimizations purposes". I met them, and I met their bugs, and it was not pleasant... ^_^ ...
paercebal
+8  A: 

Principle of least surprise.

Maybe it's not the "flavor" of rules you are looking for, but I'd definitely put it first.

It is not only the root, reason and sanity check for all the boring stuff like formatting and commenting guidelines, but - to me more importantly - puts the emphasis on the code being read and understood, rather than just compiled.

It also covers the only reasonable code quality measure I have ever encountered - WTF's per minute.

I'd use that first point to stress the importance and value of clear, consistent code, and to motivate the following items in the coding standard.

peterchen
+10  A: 

Prefer standard-compliant code. Prefer to use the standard libraries.

Dustin Getz
+1  A: 

I think a coding standard document isn't the solution to this problem. The solution is to motivate your labor to learn/care about the human side of coding - "code for people first and computers last".

Obviously it is not possible to just fire the ones that don't care - but a standard document isn't going to help them much, either.

Dustin Getz
I partially agree. Motivating coders to proper coding is good to limit the fix-this-and-that list created in code reviews. However I believe some parts of a standard are not automatically achieved by relying on common sense, for example const correctness.
sharkin
sure, but if they are motivated and care, teach them const once and they use it forever out of pride. if they don't care, it will be half assed to meet the min spec.
Dustin Getz
+1  A: 

I suggest just requiring developers to read a bunch of guidelines, and the effective C++ and more effective C++ books by Meyers.

If you want lightweight, you are going to have to rely on common sense and a common ideal.

Code reviews help enforce this as well.

To keep it lightweight I would avoid a document and code police. Praise good code publicly.

EDIT - I started with a comment here, but will put it in the response for ease of viewing:

reviews done correctly will do wonders - but you can't allow reporting hierarchies into the review and no statistics with people's names can be on the review results.

Make sure to keep the document small and be sure to give REASONS for the "rule"/guideline. Without that then you ae just demanding blind obedience. With rationale and reasons you educate so that actually posting/writing the "rule" becomes unneeded. (as the concept will be internalized)

Tim
Light-weight indeed relies heavily on common sense. In my opinion a document should exist so anyone can read and everyone knows the shots (especially juniors). Policework and court-style reviews are shadows from failing leadership and communication.
sharkin
+5  A: 

Always, always, always do proper data member initialization on object construction.

I ran into a problem where an object constructor was relying on some "default" initialization for its data members. Building the code under two platforms (Windows/Linux) gave different results and a hard-to-find memory bug. The result was that a data member was not initialized in the constructor, and used before it was initialized. On one platform (Linux), the compiler initialized it to what the code writer thought appropriate default. On Windows, the value was initialized to something - but garbage. On use of the data member, everything went haywire. Once the initialization was fixed - no more problem.

twokats
+2  A: 

Probably a no-brainer, but nevertheless an important rule:

Avoid undefined behavior.

There's an awful lot of it in C++, and it's probably impossible to write a nontrivial application that doesn't depend on it somehow, but the general rule should still be "undefined behavior is bad". (Because sadly there are C++ programmers out there who feels that "it works on my machine/compiler" is good enough).

If you have to rely on it, make it clear to everyone what, why, where and how.

jalf
I think I have developed many non-trivial C++ applications that don't depend on undefined behaviour
Elemental
I think you just don't realize that they depend on undefined behavior. ;)
jalf
+1  A: 

Make sure destructors are defined as virtual:

 class GoodClass {
 public:
   GoodClass();
   virtual ~GoodClass()
 };

 class BadClass {
 public:
   BadClass();
   ~BadClass()
 };
jussij
No. It makes no sense to have the destructors from value classes virtual. Moreover, in /C++ Coding Standards/ H.Sutter and A.Alexandrecu recommend to make the destructor from a *base class* either public and virtual, or protected and non virtual.
Luc Hermitte
I agree with Sutter/Alexandrecu except I would go further and say it be a blanked rule for all destructors. My argument is a class may not be a base class today, but there is nothing to say it won't be a base class tommorrow. In this scenario the missing virtual is a bug waiting to happen.
jussij
When the class is known to be a value class, inheriting it later will introduce many other problems. By saying clearly: "no this class is NOT meant to be (publicly) inherited" (thanks to the non-virtual destructor, and to a comment), we have a chance to avoid the problems.
Luc Hermitte
If it is made a baseclass later, it will also get virtual methods. Then you can still make the dtor virtual. Otherwise at least gcc will geenerate a warning.
Frank
+4  A: 
fnieto
This rule is two simplistic. When all the attributes are copyable and assignable, AND when we want the new class to be copyable and assignalble, we must prefer the default generated copy-constructor and assignement operator. In those cases, we should consider leaving a comment stating that this is a deliberate choice as all the members are copyable and assignable.We are back to my answer: http://stackoverflow.com/questions/242728/most-crucial-elements-in-a-light-weight-c-coding-standard/243256#243256
Luc Hermitte
Yes, if the commentary is clear enough to prevent anybody to add a new non trivial-copy attribute to the class, breaking the initial assumptions.
fnieto
Edited to clarify this rule is tells you that you should avoid using them by default, and make the non-copyable behavior the default option.
fnieto
A: 

The Art of Computer Programming Tome{1,2,3}

ZeroCool
+2  A: 

Pass input arguments by const reference, and output or input-output arguments by pointer. This is a rule borrwed from the Google style guide.

I used to have an absolute aversion to pointers and preferred to use references whenever possible (as suggested by one of the posters in this thread). However, adopting this output-arg-as-pointer convention has made my functions much more readable. For example,

SolveLinearSystem(left_hand_side, right_hand_side, &params);

makes it clear that "params" is being written to.

SuperElectric