Coding Standards


Over the years, I have developed coding standards that I am both comfortable with, and can defend on technical grounds. But my style has evolved to fit its niche (Linux, embedded and server), so it’s always subject to further evolutionary pressures.

Inputs

I’ve found it useful to read other coding guidelines. I may or may not agree with the logic, and moreover my situation may not match theirs so the logic may not even be applicable. But the key point is to read many guidelines, and understand where they are coming from.

If you read, and bother to understand the background, and figure how it might apply to you, then you will grow.

Some things I’ve read and been influenced by:

Style vs Best Practices

Here I have tried to separate it into “style” (that which is subjective) versus “best practices” (that which might have an immediately measurable impact on quality). But it’s not a clear dividing line, particularly where readability impacts the coder’s mental model. For example:

  • If the code has no single style (or too many styles), it takes more effort to read and understand, and therefore modifications are more likely to introduce bugs. Is it “style” to have a consistent style, or “best practice” to have a consistent style?
  • Excessive defines, typedefs, and casting numbs both the coder and compiler. Is this a problem of style or best practice?

So don’t stress the division too much. I’ll just say: It’s best practice to have some well-defined style.

Style

This is my preferred coding style. However, when contributing to an existing project with its own style, it is more important to match that style.

Critical vim plugin: YAIFA

Targets

Generally assume C99 support. (Backfill as necessary on Windows.)

I generally don’t care to support Windows (but if I must, at least not DLLs). Why?

  • Inlines can be pitfalls (heaps are per DLL)
  • Multiple incompatible CRTs (and they are per DLL, no less)
  • Export crap makes headers hard to read (although, admittedly, if you properly hide symbols on POSIX you’re back in the same boat)
  • Substandard C/C++ support (minimal C99 support 15 years later!)
  • Coping with UTF16 sucks (please use UTF8 everywhere)
  • Most smart people that I want to learn from aren’t on Windows.
  • Life is too short; I could fill a rant with this.

C++ Features

Minimal STL & templates; excessive STL bloats final binary.

Never use “using” in headers to avoid namespace pollution. Avoid “using” even in cpp unless there’s no chance of confusion.

Judicious use of exceptions:

  • judicious use of bad_alloc and others from std is okay (it’s the standard, after all)
  • only throw for truly exceptional conditions

Some embedded projects may prefer to ban exceptions entirely. There are pros and cons to this:

  • If you use classes but ban exceptions, you will need to deal with zombie classes. This isn’t such a crazy idea; the lovely BeOS / Haiku API does this.
  • Banning exceptions and explicitly handling errors, if done carefully (such as the Linux kernel’s use of gotos, even though they wouldn’t have used exceptions anyway) can significantly reduce the binary size. Unrolling multiple levels of construction with the careful use of gotos creates lovely binary code. Write some test programs and prove this to yourself.

Formatting

4 space K&R (unless project prefers tabs, such as Linux kernel).

No extraneous parenthesis around return val:

return 123;

No extraneous inner space padding around parens, but spaces around keywords and most operators, but not functions:

for (int i = 0; i < N; i++) {
    while (stuff) {
        fn(i);
    }
}

Documentation

Document public APIs in headers using Doxygen comments, in the /** */ Javadoc style.

Minimal commenting in implementation files. Block comments as necessary to explain why, but the what should be obvious (if not, rewrite). These are plain comments, not Doxygen, because these are implementation details. Don’t publish your implementation details, so users are less tempted to peak behind the curtain (and make your leaky abstractions even more leaky).

Naming

My naming scheme follows the Java standard: MyClass.h, MyClass, MyTypedef, myInstance->myMethod, MY_DEFINE, m_privateMember, ...

Unanswered by Java what a stand-alone C function would be: myFunction or MyFunction. But it seems that the logic is that contained items start with a lowercase. MyFunction is not contained.

I do note that the rest of the world seems to prefer MyMethod.

Debugging

Assert contract; minimal or no runtime code to enforce it (because you’re already running out of spec). Fail early, fail hard.

No debugging printfs; use logging which can be compiled out.

Best Practices

Many books have been written about best practices. Read the books for your language. I’m only hitting some big points.

Don’t Repeat Yourself

Books have already been written about this. But if you are repeating yourself, it’s a huge hint that it’s time to refactor.

Use The Tools

Compiler

Code should compile cleanly under -Wall -O.

On Windows, it should compile cleanly under at least /W3. /W4 adds some extra useful warnings (some around 64-bit IIRC which can actually be useful in some situations), but I find it necessary to disable the sillywarnings.

Valgrind

Code should run cleanly under Valgrind.

As a coworker once said, “I trust Valgrind further than I trust my own mother.”

Clang

Fix errors and warnings reported by clang --analyze.

When using CMake, it’s easy to apply Clang’s analyzer during the build:

scan-build cmake source-dir
make

cppcheck

Address any errors from cppcheck. Anything less than an error is often noise.

Unit Tests

Yes, please.

Headers

System headers are <>, local headers are “”. Local headers are included before system headers, to more quickly find and fix local headers that are not self-sufficient.

Include guards should be canonicalized form of the path. Internal to the header, never external. Prefer include guards over #pragma once.

Design

Then there is the bigger issue of design. Read some books…

Updated January 1, 0001