C++ : Style guidelines


PART 13


Q81: What are some good C++ coding standards?
A: Thank you for reading this answer rather than just trying to set your own
coding standards.  But please don't ask this question on Usenet.  Nearly every
software engineer has, at some point, felt that coding standards are or can be
used as a `power play'.  Furthermore some attempts to set C++ coding standards
have been made by those unfamiliar with the language and/or paradigm, so the
standards end up being based on what *was* the state-of-the-art when the
setters where writing code.  Such impositions generate an attitude of mistrust
for coding standards.  Obviously anyone who asks this question on Usenet wants
to be trained so they *don't* run off on their own ignorance, but nonetheless
the answers tend to generate more heat than light.



Q82: Are coding standards necessary?  sufficient?
A: Coding standards do not make non OO programmers into OO programmers.  Only
training and experience do that.  If they have merit, it is that coding
standards discourage the petty fragmentation that occurs when organizations
coordinate the activities of diverse groups of programmers.

But you really want more than a coding standard.  The structure provided by
coding standards gives neophytes one less degree of freedom to worry about,
however pragmatics go well beyond pretty-printing standards.  We actually need
a consistent *philosophy* of implementation.  Ex: strong or weak typing?
references or ptrs in our interface?  stream I/O or stdio?  should C++ code
call our C?  vise versa?  should we use ABCs?  polymorphism?  inheritance?
classes? encapsulation?  how should we handle exceptions?  etc.

Therefore what is needed is a `pseudo standard' for detailed *design*.  How can
we get this?  I recommend a two-pronged approach: training and libraries.
Training provides `intense instruction', and a high quality C++ class library
provides `long term instruction'.  There is a thriving commercial market for
both kinds of `training'.  Advice by organizations who have been through the
mill is consistent: Buy, Don't Build.  Buy libraries, buy training, buy tools.
Companies who have attempted to become a self-taught tool-shop as well as an
application/system shop have found success difficult.

Few argue that coding standards are `ideal', or even `good', however many feel
that they're necessary in the kind of organizations/situations described above.

The following questions provide some basic guidance in conventions and styles.



Q83: Should our organization determine coding standards from our C experience?
A: No matter how vast your C experience, no matter how advanced your C
expertise, being a good C programmer does not make you a good C++ programmer.
C programmers must learn to use the `++' part of `C++', or the results will be
lackluster.  People who want the `promise' of OOP, but who fail to put the `OO'
into OOP, are fooling themselves, and the balance sheet will show their folly.

C++ coding standards should be tempered by C++ experts.  Asking comp.lang.c++
is a start (but don't use the term `coding standard' in the question; instead
simply say, `what are the pros and cons of this technique?').  Seek out experts
who can help guide you away from pitfalls.  Get training.  Buy libraries and
see if `good' libraries pass your coding standards.  Do *not* set standards by
yourself unless you have considerable experience in C++.  Having no standard is
better than having a bad standard, since improper `official' positions `harden'
bad brain traces.  There is a thriving market for both C++ training and
libraries from which to pool expertise.

One more thing: any time demand for something is high, the potential for
charlatans increases.  Look before you leap.  Also ask for student-reviews from
past companies, since not even expertise makes someone a good communicator.
Finally, select a practitioner who can teach, not a full time teacher who has a
passing knowledge of the language/paradigm.



Q84: Should I declare locals in the middle of a fn or at the top?
A: Different people have different opinions about coding standards.  However
one thing we all should agree on is this: no style guide should impose undue
performance penalties.  The real reason C++ allows objects to be created
anywhere in the block is not style, but performance.

An object is initialized (constructed) the moment it is declared.  If you don't
have enough information to initialize an object until half way down the fn, you
can either initialize it to an `empty' value at the top then `assign' it later,
or initialize it correctly half way down the fn.  It doesn't take much
imagination to see that it's cheaper to get it right the first time than it is
to build it once, tear it down, then rebuild it again.  Simple examples show a
factor of 350% speed hit for simple classes like String.  Your mileage may
vary; surely the overall system degradation will be less that 300+%, but there
*will* be degradation.  *Unnecessary* degradation.

A common retort to the above is: `we'll provide "set" methods for every datum
in our objects, so the cost of construction will be spread out'.  This is worse
than the performance overhead, since now you're introducing a maintenance
nightmare.  Providing `set' methods for every datum is tantamount to public
data.  You've exposed your implementation technique to the world.  The only
thing you've hidden is the physical *names* of your subobjects, but the fact
that you're using a List and a String and a float (for example) is open for all
to see.  Maintenance generally consumes far more resources than run-time CPU.

Conclusion: in general, locals should be declared near their first use.  Sorry
that this isn't `familiar' to your C experts, but `new' doesn't necessarily
mean `bad'.



Q85: What source-file-name convention is best? `foo.C'? `foo.cc'? `foo.cpp'?
A: Most Un*x compilers accept `.C' for C++ source files, g++ preferring `.cc',
and cfront also accepting `.c'.  Most DOS and OS/2 compilers require `.cpp'
since DOS filesystems aren't case sensitive.  Some also advocate `.cxx'.  The
impact of this decision is not great, since a trivial shell script can rename
all .cc files into .C files.  The only files that would have to be modified are
the Makefiles, which is a very small piece of your maintenance costs.  Note
however that some versions of cfront accept a limited set of suffixes (ie: some
can't handle `.cc'; in these cases it is easier to tell `make' about CC's
convention than vise versa).

You can use `.C' on DOS or OS/2 if the compiler provides a command-line option
to tell it to always compile with C++ rules (ex: `ztc -cpp foo.C' for Zortech,
`bcc -P foo.C' for Borland, etc).



Q86: What header-file-name convention is best? `foo.H'? `foo.hh'? `foo.hpp'?
A: The naming of your source files is cheap since it doesn't affect your source
code.  Your substantial investment is your source code.  Therefore the names of
your header files must be chosen with much greater care.  The preprocessor will
accept whatever name you give it in the #include line, but whatever you choose,
you will want to plan on sticking with it for a long time, since it is more
expensive to change (though certainly not as difficult as, say, porting to a
new language).

Almost all vendors ship their C++ header files using a `.h' extension, which
means you can reliably do things like:
		#include 

Well, *almost*.  There are one or two vendors that provide .  A
few #ifdef's, suffice.  There are also tools that recognize the language of a
file by its extension rather than its contents (gnu emacs looks in the first
few lines for a string magic token in a comment identifying the language).
Some of these extension-based tools use `.hh' or `.H' to identify C++ headers,
however most of the world is leaning toward `.h' for C++ headers.  C++ specific
information in a `.h' that is to be shared with a C compiler can be #ifdef'd
using:
		#ifdef __cplusplus    /*all C++ compilers define __cplusplus*/
		  // ... C++ specific stuff here ...
		#endif



Q87: Are there any lint-like guidelines for C++?
A: Yes, there are some practices which are generally considered dangerous.
However none of these are universally `bad', since situations arise when
even the worst of these is needed:
 * a class `X's assignment operator should return `*this' as an `X&'
   (allows chaining of assignments)
 * a class with any virtual fns ought to have a virtual destructor
 * a class with any of {dtor, assignment-op, copy-ctor} generally needs all 3
 * a class `X's copy-ctor and assignment-op should have `const' in the param:
   `X::X(const X&)'  and  `X& X::operator=(const X&)'  respectively
 * always use initialization lists for class sub-objects rather than assignment
   the performance difference for user-defined classes can be substantial (3x!)
 * many assignment operators should start by testing if `we' are `them'; ex:
	X& X::operator=(const X& x)
	{
	  if (this == &x) return *this;
	  //...normal assignment duties...
	  return *this;
	}
   sometimes there is no need to check, but these situations generally
   correspond to when there's no need for an explicit user-specified assignment
   op (as opposed to a compiler-synthesized assignment-op).
 * in classes that define both `+=', `+' and `=', `a+=b' and `a=a+b' should
   generally do the same thing; ditto for the other identities of builtin types
   (ex: a+=1 and ++a; p[i] and *(p+i); etc).  This can be enforced by writing
   the binary ops using the `op=' forms; ex:
	X operator+(const X& a, const X& b)
	{
	  X ans = a;
	  ans += b;
	  return ans;
	}
   This way the `constructive' binary ops don't even need to be friends.  But
   it is sometimes possible to more efficiently implement common ops (ex: if
   class `X' is actually `String', and `+=' has to reallocate/copy string
   memory, it may be better to know the eventual length from the beginning).

See Stroustrup/C++ Programming Language/Second Edition/`Rules of Thumb'/p.10