ArticleS
.
JamesGrenning
.
My2CentsOnCppCodingStandards
Edit Page:
!title Coding standards are arbitrary! Read all about it Sure they are largely arbitrary, but that does not mean we don't need one. Use the KISS principle. But can a coding standard be simple? Sure it can if the scope for the standard deals mainly with formatting and readability issues. When you get into programming practices and techniques, you probably need a whole book and there are plenty of them. One way to limit scope is to have principles that the coding standard follows. !2 Coding standard principles * Uniform style for a team is more important than my favorite style * The standard is the team’s and all team members shall abide by the standard * A standard should be short * A standard should lower the effort needed to understand the code * Don’t sweat the little stuff * Its all little stuff !2 An example can serve as a coding standard !3 Header file example {{{ #ifndef D_Failure_H #define D_Failure_H /////////////////////////////////////////////////////////////////////////////// // // Failure is a class which holds information pertaining to a specific // test failure. It can be overridden for failure messages that don't fit this mold. // /////////////////////////////////////////////////////////////////////////////// #include "SimpleString.h" class Test; class Failure { public: Failure(Test*, long lineNumber, const SimpleString& theMessage); Failure(Test*, const SimpleString& theMessage); virtual ~Failure(); virtual void Print() const; static long TheirTotalFailureCount() { return theirFailureCount; } protected: virtual void PrintLeader() const; virtual void PrintSpecifics() const; virtual void PrintTrailer() const; private: SimpleString message; SimpleString testName; SimpleString fileName; long lineNumber; static int theirFailureCount; Failure(const Failure&); Failure& operator=(const Failure&); }; #endif }}} !3 C++ file example {{{ #include "Failure.h" #include "Test.h" #include <cstdio> #include <cstring> Failure::Failure(Test* test, long lineNumber, const SimpleString& theMessage) : testName(test->GetFormattedName()) , fileName(test->GetFile()) , lineNumber(lineNumber) , message(theMessage) { } Failure::Failure(Test* test, const SimpleString& theMessage) : testName(test->GetFormattedName()) , fileName(test->GetFile()) , lineNumber(test->GetLineNumber()) , message(theMessage) { } void Failure::PrintLeader()const { //Snip } void Failure::Print()const { PrintLeader(); PrintSpecifics(); PrintTrailer(); } void Failure::PrintSpecifics()const { //Snip } void Failure::PrintTrailer()const { //Snip } }}} You might be able to stop right there. If the team understands C++ and the subtleties above, there you have a 2 page coding standard. Following is why the coding standard is the way it is. !3 Intricacies of the header file example {{{ #ifndef D_Failure_H #define D_Failure_H }}} Include guards are formed from the class name in a way that will be unique across the project. I've adopted mixed case to match the class name because my initial class is created from a template, and this makes the substitution easier. I have a shell script to create my starting point files for a new class. {{{ /////////////////////////////////////////////////////////////////////////////// // // Failure is a class which holds information pertaining to a specific // test failure. It can be overridden for more complex failure messages // /////////////////////////////////////////////////////////////////////////////// }}} A comment describes the responsibility of the class. This comment is optional if the comment is too obvious. {{{ #include "SimpleString.h" }}} Each class has its own header file. File names and class names are identical. {{{ class Test; }}} Forward declarations are preferred to file includes. {{{ class Failure }}} Class names are formatted in Pascal Case (camel case with initial letter capitalized) {{{ { } }}} Curly braces are on their own line. This is my favorite, not to be confused with the only way. I switch styles with each client. The first time it was hard to not have it my own way, but it got easy after I got over it. I disobey this style when doing inline in header files. {{{ Failure(Test*, long lineNumber, const SimpleString& theMessage); Failure(Test*, const SimpleString& theMessage); virtual ~Failure(); }}} Constructors and destructor come first so they are easy to find. Parameter names are optional unless they provide documentation value. Spacing is consistent around “()” for function calls and “’” Not shown, but I put a space after keywords and before their opening parentheses Use uniform indentation. An 8 space tab is too wide for me, 2 or 4 spaces read well {{{ virtual void Print() const; static int TheirTotalFailureCount() { return theirFailureCount; } protected: virtual void PrintLeader() const; virtual void PrintSpecifics() const; virtual void PrintTrailer() const; }}} Member function names are Pascal case. {{{ SimpleString message; SimpleString testName; SimpleString fileName; long lineNumber; static int theirFailureCount; }}} Member variables are camel case Don’t use leader characters revealing type information or hurting readability Leader words that help readability could be adopted such as “its” or “my” for member variables or “their” for static class variables. In OO programming most variables are non-static member variables, so why burden yourself with that overhead. Avoid acronyms. If an acronym is used, only capitalize the first letter so separation between parts of the names is apparent. {{{ Failure(const Failure&); Failure& operator=(const Failure&); }}} Define copy constructor and assignment operator as private and degenerate unless they are needed. The move them to the public section and make sure you write tests for them. {{{ public: protected: private: }}} Public methods proceed protected, that proceed private. This stresses the public interface of the class over the hidden details !3 Intricacies of the C++ file example {{{ Failure::Failure(Test* test, long lineNumber, const SimpleString& theMessage) : testName(test->GetFormattedName()) , fileName(test->GetFile()) , lineNumber(lineNumber) , message(theMessage) { } Failure::Failure(Test* test, const SimpleString& theMessage) : testName(test->GetFormattedName()) , fileName(test->GetFile()) , lineNumber(test->GetLineNumber()) , message(theMessage) { } }}} Split long parameter lists as needed. Prefer initialization over assignment. Initializer lists are left justified. Constructors and destructor are at the top of the file. {{{ void Failure::PrintLeader()const { //Snip } void Failure::Print()const { PrintLeader(); PrintSpecifics(); PrintTrailer(); } void Failure::PrintSpecifics()const { //Snip } void Failure::PrintTrailer()const { //Snip } }}} Don’t litter the code with extra comments that don’t help tell the story. A responsibility comment and well chosen names should make most comments unnecessary. If you need a copyright statement, can you put it at the bottom, out of the way. !2 Summary You probably noticed a number of arbitrary decisions. They are arbitrary. Keep in mind that the important part of a standard is consistency that supports communication and teamwork. Code is hard enough to understand without having to flip formatting conventions constantly. If the team agrees on a specific (arbitrary) set of formatting conventions, just do it. It is personal preference, not usually based in irrefutable logic. If there is existing code, don’t go reformatting everything unless the process is automated. New code follows the standard; modified code follows the precedent established by its current format. How do you go about getting agreement? Have a one hour meeting to decide on * Naming * Brace placement * Punctuation * etc. Consensus is great, but that may never happen. Majority rules is a good system, unless you get out voted (thanks Micah). Find areas of agreement. Have a vote on items of disagreement. Benevolent dictator can also speed things up. !3 Wherever you end up, suck it up and follow the standard. !commentForm !* Sun, 12 Jun 2005 04:22:18, Brad Appleton, Forget the example, real code can set the standard! Hi James! I would go even one step further than your "coding example as coding standard" and have one or more of the "core" modules actually BE the standard! Usually every system I wrote ends up having to have some basic functions for things like diagnostics (errors and exceptions), logging/tracing, and some project-wide constants and/or utilities (or even code that is part of the overal Make/ANT "scheme" for the project). That basic code can define the coding standard. *! !* Fri, 8 Jul 2005 17:58:04, Tim Ottinger, Standards and creativity When pushing for the adoption of a python coding standard (pep8), I was once greated with the lament "but that will damage our creativity". Heh. Yes, it will. That's EXACTLY what standards are for. *! !* Fri, 13 Jan 2006 08:37:37, James Grenning, Standards and creativity Rather than damaging creativity, a standard can get the noise out of the way (varying personal standards) so that we can get apply our creativity in solving the design problems at hand. *! !* Fri, 13 Jan 2006 15:40:43, ${ottinger}, That's what I meant. Yes. The purpose of a standard is to stifle creativity in formatting and style so that more important things can be done with the code. That's what I meant, but I don't know that the irony of my answer came through in text. All standards exist to stifle creativity -- to provide a single way to do a thing for the sake of productive uniformity. It's a good thing when the standard is tolerable. *!
Hints:
Use alt+s (Windows) or control+s (Mac OS X) to save your changes. Or, tab from the text area to the "Save" button!
Grab the lower-right corner of the text area to increase its size (works with some browsers).