Miniature/Development/CodingStyle

Miniature - Play chess everywhere you go!
.: Home : Releases : Wish list : Development : Coding style :.

Contents

General

We mostly try to follow the offical Qt Coding Guidelines, but with some exceptions. follow mikhas' personal coding style for Qt:

  • We use whitespaces over tabs.
  • Max. length of a source code line is 120 characters.
  • We use (nested) namespaces.
  • Type names are in CamelCase
  • Method names are in mixedCase (= lowerCamelCase).
  • Opening braces for code blocks after newlines, *except* opening braces for classes and methods.
  • Variables use under_scores and are lower_cases, to make them stand out from other symbols.
    • Member variables are prefixed with "m_*" if there is no private data class (PIMPL). In the latter case, "d->" already serves as a prefix. In the same sense, member variables in *Private classes have no prefix at all.
  • Use initializer list and call construct each member explicitly.
  • No boolean method parameters, unless it's a boolean property. Use enums instead.
  • Use Q_SLOT, Q_SIGNAL as a method prefix.
  • Impl files have a .cc extensions, header files use .h
  • Use Qt containers over std containers.
  • No explicit delete statements. Use QScopedPointer or C++'s inbuilt resource cleanup of member variables.
  • Avoid QObject parent ownership - with C++ inbuilt mechanism, smart pointers and the concept of RAII, there is rarely need for it. It makes memory management more explicit and avoids another constant source of bugs.
    • Try to use copyable value types instead of full-blown QObjects.
  • Avoid singletons
    • Singletons often become hidden dependencies of a class, making the class less reusable, as it increases coupling.
    • Prefer dependency injection instead, this also increases testability.
  • Public methods should be virtual.
    • Makes classes easier to reuse, increases testability.
    • Does not apply for structs.

Naming conventions

  • Methods *do* something, that's why their names should always contain a verb (slots and signals count as methods, too).
   connect(this,  SIGNAL(someSignalChanged()),
           other, SLOT(onSomeSignalChanged()));
  • typedef'd types *might* hint at their composed types (to emphasize their intended use), but avoid directly including the type information in the name:
 typedef QSharedPointer<SomeClass> SomeSharedClass; // Good! Sharing a resource is a concept.
 typedef QSharedPointer<SomeClass> SomeClassPtr; // Bad! What is a "SomeClassPtr *some_variable" now?
 typedef QList<SomeClass> SomeClassList; // still OK, since "List" is a concept, too.
  • Avoid hungarian variable names.
  • If in doubt, name slot "onSomeSignalChanged". This makes for nice connection code:
 connect(some_signal_source, SIGNAL(theSignalWasEmitted()),
         some_receiver,      SLOT(onTheSignalWasEmitted()),
         Qt::UniqueConnection);
    • Use normalized signal/slot signatures (no const keyword, no &).

Advanced

  • Method parameters should be easy to read:
 class SomeClass
 {
     void someMethod(SomeType arg1,
                     SomeOtherType arg2,
                     ...);
    • Details:
      • Move each argument to a new line. This serves two goals: header files will be easier to scan (for humans), and you'll spot those ugly methods that scream "refactor me!" faster because they have more than 3 arguments. Also, because it *is* ugly, you might just go and refactor it, which will help everyone.
  • Use const refs for arguments when possible, even if the type is COW (copy-on-write, for example QString).
  • Don't return const refs for value types. Leave the decision to the caller.
  • Conditionals
 if ((something && somethingElse)
      || orPerhapsThis) {
     ...
 }
    • Details:
      • Conditionals often contain bugs. Especially when the surrounding code gets refactored, those bugs simply slip in.
      • If a conditional needs to be wrapped, it starts to look ugly. Good! It might lead you to refactor the code, moving the condition itself into a predicate function (which has the nice effect to serve as a comment, if it has a good name).
      • Nested conditionals quickly start to look ugly with all those line separators, but you probably guessed it already: It's just another sign that this code might need some cleanup! Nested conditionals should be used rarely or hidden within dedicated methods.
  • One space after keywords that could be mistaken as functions (if, for, ...).
  • If you store a pointer to an object over which you do not intend to take ownership, then wrap this pointer in a QWeakPointer:
 class SomeOtherClass
 {
     QWeakPointer<SomeType> m_member;
 };
    • Details:
      • QWeakPointers keep track of QObject deletion and set all referring QWeakPointers to zero, preventing dangling pointers. Especially when using signals & slots, this is an often (and sometimes hard to spot!) source of bugs. With QWeakPointers, you can wrap your code with conditional guards like so:
 if (SomeType *var = m_member.data()) {
     // Do sth with var
     // This block *never* gets executed even if m_member was deleted elsewhere.
 }
  • Assign 0 to pointers after deletion (or use QPointers).
  • Always initialize type primitives (e.g., pointers) in the constructor's initializer list.
  • Don't use forward declaration unless necessary. It is not your task to optimize the compiler. Also, Qt Creator gets confused by them.

How to keep code testable (and hopefully, mostly bugfree)

We want to have testable code, therefore we also want to follow some advanced coding standards.

For that, I will try to list some really really useful advice from this document: http://www.research.att.com/~bs/JSF-AV-rules.pdf, a coding style document for C++. Bjarne Stroustrup worked on that one, yes.

  • Keep methods at a reasonable length. In general: If it doesn't fit on a screen page it's probably too long. More precise: Every method longer than 200 lines of code (including comments, yes) is too long.
  • Within a method, avoid a cyclomatic complexity higher than 20 (see Appendix A regarding AV Rule 3, pp. 65, "Cyclomatic complexity measures the amount of decision logic in a single software module.", and the example).
  • Avoid cyclic dependencies between classes/modules (Rationale: it's mostly an indicator for layer violations).