Code Standards

From FreeOrionWiki
Jump to: navigation, search

General

C++ code should follow modern safe practices wherever possible:

  • Use STL containers whenever appropriate, and avoid C-style arrays.
  • Use std::string rather than c-strings.
  • Use static_cast and dynamic_cast rather than c-style (type) casts.

Boost library code is available, and should be used whenever appropriate. boost::lexical_cast is quite frequently used, for example.

Class member function declarations should be const-correct.

Structs may be used, with all public data, to group that data for readability or ease of passing around.

If a group of data is sufficiently complicated to have its own functions to act on that data, it may be more appropriate to make it a class, with private data and getter and setters for data acess. Do not have public member data in classes.

Logging and Debug Output

Use Logger().debugStream() for log or debug output that will appear in the FreeOrion log files. Use Logger().errorStream for error output, such as when a function aborts due to invalid input or inability to access required resources.

std::cout and std::cerr can be used, but will generally only work in the human client, and not server or ai clients, as the latter generally don't have an open terminal window to display this output. Logger() streams are (almost) always later accessible from the log files, so are generally preferred.

Use std::endl to add linebreaks when usuing std::cout or std::cerr not c-style "\n".

Logger().debugStream() and .errorStream() don't work with std::endl so if you need to break a line in logged debug output, you can use "\n" in that case.

Pointers

boost::shared_ptr and similar smart pointers are available, and may be used when possible to pass data by pointer. shared_ptr is appropriate when returning a shared resource such as a texture that multiple UI elements might display.

UI class private member variables are generally stored and passed by raw pointer, due to the inteface of the GUI library FreeOrion uses, GiGi, which expects raw pointers and passes ownership of the pointed-to objects when doing things like attaching child windows or drag-dropping.

If a function produces a lot of output and/or populates a data structure that needs to be returned to the caller, consider passing by reference the structure to be populated as a parameter to the function. This avoids the need to return a large amount of data by value on the stack (as temporary variables in a function cannot be returned by reference or pointer).

Parameters and Return Values

Whenever possible and appropriate, function parameters and return values should be passed:

  • For simple built-in data types or very small structures or, for example, std::pair, by value
  • For larger data structures or class instances:
    • By reference when possible, const if appropriate (ie. the passed value should not be modified)
    • By pointer when necessary, const if appropriate.

Headers

Avoid adding unnecessary things to headers. Private globals can be defined in an anonymous namespace in a source file, and classes used internally do not need to be exposed in the header. Hiding internal details makes for code easier to understand for others and makes implementation changes easier to accomplish without wide-spread editing.

Line Endings

Use LF (unix style) line endings, not CRLF (Windows style).

Style

Formatting Code

In general, when editing an existing source file, keep the style of code consistent with the rest of the file.

Section braces (at the start of a loop or function definition) can be on the same line as or the line after the function declaration or loop keyword, depending on your preference, unless you are editing an existing file that already has one convention established (in which case you should be consistent with the rest of the file, as above).

Indentation should be done with four spaces per indentation level. Do not use tabs or another number of spaces.

Naming

Do not prepend "C" before class names eg. CNode; use just Node. Use as descriptive and lengthy a class name as appropriate; clarity and specificity are priorities.

Do not prepend "p" before pointer variable names, eg. pEmpire; use just empire.

Classes should be given UpperCamelCase names, with a leading capitalized letter and words delimited by subsequent capital letters.

Class member functions should also have UpperCamelCase names. eg. PopGrowthProductionResearchPhase()

Class member variables should have m_ prepended to their name, with all lower-case letters and underscores_separating_words. Again, use as long a name as needed to convey the purpose of a variable. eg. m_fleet_id

Class static variables should have s_ prepended before their name, and otherwise be named like member variables. eg. s_current_system_id

Struct members should be named without a leading m_. eg. struct Guy { std::string name; };

Local variables and method parameters should use lower-case letters and underscores to separate words.

Use long and descriptive names when appropriate, although occasional single-letter variables names when use and intention is obvious are acceptable, such as loop counters.

Internal (Code) Documentation

To add documentation internally with the code usually goes without saying, nevertheless, this project being "open source," seems to make it ever more important to document well.

Generous code comments should have a synergistic effect, and make the entire project better.

Each code block, module, or minor routine, should contain *clear* and descriptive comments within it, describing what the block does.

This is just as important for the coder as it is for others who will view the code, as each programmer knows that he/she might return to correct or refine their code later.

You may want to consider reading Code Complete (a code construction handbook... very good). You may also want to read a short article on PDL which stands for Program Design Language and provides an easy to use methodology to self documenting code.

Doxygen

Class definitions should contain doxygen-compatible comments for public method and type definitions, and for the classes themselves.

Individual member functions and type declarations may be preceeded or followed-on-the-same-line with comments

class Example {
    /** An explanation of a type definition on the line before */
    typedef QueueType::iterator iterator;

    ....
    int     ReturnValue() const;   ///< a comment following a function declaration on the same line
    ....
    /** A function explanation on the lines
      * prior to the declaration, which is
      * useful for long descriptions.
      */
    void    DoSomething();
}

Groups of public functions and definitions should be wrapped in group comments.

    /** \name Structors */ //@{
    Ship(); ///< default ctor
    Ship(int empire_id, int design_id);     ///< general ctor taking ship's empire and design id; from this the design can be looked up and used to create the ship
    //@}

Multiple function declarations and comments in a header file should generally line up to make reading easier. The function names should line up, as when possible, the comments on the same lines should line up vertically.

    /** \name Accessors */ //@{
    const ShipDesign*   Design() const;     ///< comment

    int                 FleetID() const;    ///< another comment
    Fleet*              GetFleet() const;   ///< yet more comments, all lined up


    virtual double      ProjectedCurrentMeter(MeterType type) const;    ///< the function name was too long, so this comment had to be moved right.  This is fine, but try to be consistent when possible.
    //@}

Self-documentation

Aside from clear and consistently-named variables, code may be self-documenting in a few specific situations.

When creating a class which overrides virtual functions inherited from another class, mark the new class's functions virtual as well.