[[TOC(inline)]] == Introduction == The goal of these coding conventions is to encourage consistency within the code, rather than claiming some particular style is better than any other. As such, the main guideline is: * When modifying a piece of code, try to follow its existing style. In particular: * Primarily, try to match the style of the functions that you're editing (assuming it's at least self-consistent and not too bizarre), in order to avoid making it less self-consistent. * Secondly, try to match the style of the files that you're editing. * Then, try to match the style of the other code in the subdirectory you're editing. * Finally, try to match the global guidelines discussed on this page. Our code is currently not entirely consistent in places, but the following guidelines attempt to describe the most common style and are what we should converge towards. (Obviously we always want clean, readable, adequately-documented code - lots of articles and books already talk about how to do that - so here we're mostly describing minor details.) == Common == * Use the US variant of English when choosing variable names. For example, use 'color' instead of 'colour'. See also the [[EnglishStyleGuide#Englishspelling|English style guide]]. == C++ == === Creating new files === * All source files (.cpp, .h) must start with the following GPL license header, before any other content: {{{ #!cpp /* Copyright (C) 2018 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ }}} replacing `2018` with the year that the file was last updated. '' Exception:'' Code in `source/lib/` (and a few other files) should use the MIT license instead: {{{ #!cpp /* Copyright (C) 2018 Wildfire Games. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ }}} * Wrap header files in include guards, using the name `INCLUDED_`''filename'', e.g. the file `Foo.h` should say: {{{ #!cpp #ifndef INCLUDED_FOO #define INCLUDED_FOO ... #endif // INCLUDED_FOO }}} * All source files must have the `svn:eol-style` property set to `native` === Formatting === * Use tabs for indentation, not spaces. * For any alignment within a line of code (as opposed to indentation at the start), use spaces, not tabs. * Indent braces and use whitespace like {{{ #!cpp int CExampleObject::DoSomething(int value) { if (value != 0) { Prepare(); m_Value = value; } for (int i = 0; i < value; ++i) DoSomethingElse(i, (unsigned int)value); return value; } }}} ''Exception:'' Code in `source/lib/` omits the space before the '`(`' in statements like "`if(...)`", "`while(...)`", etc. * Try to avoid very wide lines. Typical wrapping points are 80 characters, or 120, or 132, etc. (There's no strict limit - aim for whatever seems most readable.) * Write switch statements like {{{ #!cpp switch (n) { case 10: return 1; case 20: foo(); // fall through to next case [this should be explicit if you don't end with break or return] case 30: bar(); break; case 40: { int i = n*2; // [need the extra {...} scope when declaring variables inside the case] return i; } case 50: { int z = n*2; n = foobar(n, z); break; // [place breaks inside the brackets] } default: debug_warn(L"invalid value for n"); // [only do this kind of warning if this case is an engine bug] } }}} === Error reporting === See [wiki:Logging] for more info. * For engine bugs (that is, error cases which need to be fixed by C++ developers, and which should never be triggered by users or modders (even if they write invalid data files)), use "`debug_warn(L"message")`" to report the error. (This pops up an ugly dialog box with stack trace and continue/debug/exit buttons.) * For error cases that could be triggered by modders (e.g. invalid data files), use {{{ #!cpp LOGERROR("Failed to load item %d from file %s", i, path); }}} This gets display on screen in red, and saved in the log files. `LOGERROR` takes `printf`-style format strings (specially [http://cppformat.readthedocs.org/en/stable/reference.html#printf-formatting-functions cppformat]). ''Exception:'' Code in `source/lib/` can't use `LOGERROR` (it can only use things defined in `source/lib/`). * The engine should try to cope gracefully with `LOGERROR` cases, e.g. abort loading the current file; it should never crash in those cases. === Documentation === * Use [http://www.doxygen.org/ Doxygen] comments (explained [http://www.doxygen.nl/manual/docblocks.html here] as !JavaDoc style), e.g. {{{ #!cpp /** * A dull object for demonstrating comment syntax. */ class CExampleObject { /** * Sets the object's current value to the passed value, if it's non-zero. * * @param v the new value to set it to, or 0 to do nothing. * * @return the value that was passed in. */ int DoSomething(int v); /// Current value (always non-zero) int m_Value; }; }}} * Try not to repeat class names or function names in the descriptions, since that's redundant information. * Try to avoid very wide lines. As wrapped block comments are extremely unreadable. Always try to keep each line of those below 80 chars (counting tabs as 4 chars). * Don't need to bother documenting every line of code or every member function or every member variable; only when it'll add to a competent reader's understanding of the program. === Strings === * Use `CStr` instead of `std::string`. Use `CStrW` instead of `std::wstring`. (These are subclasses of `std::[w]string` with various extra methods added for convenience.) * ''Exception:'' `source/lib/` and `source/simulation2/` and `source/scriptinterface/` tend to prefer `std::[w]string` instead. * `CStr8` is an alias for `CStr`. Prefer to use `CStr`. * Compare strings using `==` (not e.g. "`a.compare(b) == 0`"). Compare to literals directly (e.g. "`someCStrVariable == "foo"`", not "`someCStrVariable == CStr("foo")`"). * For portability, use the following formats for printf-style functions: {{{ #!cpp printf("%s", "char string"); printf("%ls", L"wchar_t string"); wprintf(L"%hs", "char string"); wprintf(L"%ls", L"wchar_t string"); }}} * In AtlasUI, you should prefer the wxWidgets API and construct strings like this (see the [http://wiki.wxwidgets.org/Converting_everything_to_and_from_wxString wxWiki] for more examples): {{{ #!cpp wxString str = _T("SomeUnicodeSTRING"); // Don't use wxT() directly because it breaks OS X build wxString translatedStr = _("A string that may be translated"); // Translated string for UI purposes // Sometimes you have to pass messages to the engine and need C++ strings: std::string cppStr = str.c_str(); std::wstring cppWStr = str.wc_str(); // Don't use c_str() because it breaks OS X build }}} === Preprocessor instructions === The `#include` instructions, placed after the license notice, should follow a consistent order (which is currently not the case). Between each section in the following list, we usually add an empty line. 1. The first non-comment line of **any** source file **must** be `#include "precompiled.h"` 1. For `.cpp` files that contain function implementations, headers that declare those functions should be included here. For instance, `#include "Simulation2.h"` for `Simulation2.cpp`, or `#include "ICmpPosition.h"` for `CCmpPosition.cpp`. 1. Include the rest of the files that come from 0 A.D. Write the complete path relative to `source/`, for instance `#include "simulation2/Simulation2.h"`. Use case-insensitive alphabetical order. 1. Include system libraries, in case-insensitive alphabetical order. Example, for the file `source/simulation2/Simulation2.cpp` (this is not the actual list, but an example that should cover all cases): {{{ #include "precompiled.h" #include "Simulation2.h" #include "graphics/Terrain.h" #include "lib/file/vfs/vfs_util.h" #include "lib/timer.h" #include "maths/MathUtil.h" #include "ps/Loader.h" #include "ps/lowercase.h" #include "ps/Profile.h" #include "ps/XML/Xeromyces.h" #include "scriptinterface/ScriptInterface.h" #include "scriptinterface/ScriptRuntime.h" #include "simulation2/components/ICmpAIManager.h" #include "simulation2/components/ICmpCommandQueue.h" #include "simulation2/components/ICmpTemplateManager.h" #include "simulation2/MessageTypes.h" #include "simulation2/system/ComponentManager.h" #include "simulation2/system/ParamNode.h" #include #include }}} === Miscellaneous === * In header files, avoid `#include` and use forward declarations wherever possible. * Class names are !UpperCamelCase and prefixed with `C`, e.g. `CGameObject`. Member functions are !UpperCamelCase, e.g. `CGameObject::SetModifiedFlag(...)`. Member variables are !UpperCamelCase prefixed with `m_`, e.g. `CGameObject::m_ModifiedFlag`. Files are named e.g. `GameObject.cpp`, `GameObject.h`, usually with one major class per file (possibly with some other support classes in the same files). Local variables and function parameters are lowerCamelCase. Structs are treated similarly to classes but prefixed with `S`, e.g. `SOverlayLine`. * Write pointer/reference types with the symbol next to the type name, as in {{{ #!cpp void example( int* good, int& good, int *bad, int &bad ); }}} * Use STL when appropriate. * Don't use RTTI (`dynamic_cast` etc). ''Exception:'' `source/tools/atlas/AtlasUI/` can use RTTI. * Avoid global state: global variables, static variables inside functions or inside classes, and singletons. * When a module needs access to objects from outside its own environment, prefer to pass them in explicitly as arguments when instantiating that module, rather than making the objects global and having the module reach out to grab them. * When unavoidable, global variables should be named with a `g_` prefix. * Prefer global variables over singletons, because then they're not trying to hide their ugliness. * Use `nullptr` for pointers (instead of `NULL`). * Don't do "`if (p) delete p;`". (That's redundant since "`delete nullptr;`" is safe and does nothing.) * If deleting a pointer, and it's not in a destructor, and it's not being immediately assigned a new value, use "`SAFE_DELETE(p)`" (which is equivalent to "`delete p; p = nullptr;`") to avoid dangling pointers to deleted memory. * Be sure to be aware of [CodeAndMemoryPerformance Code And Memory Performance] guidelines * Use "for range" loop instead of "std::for_each". {{{ #!cpp // Avoid std::vector anyVector; std::for_each(anyVector.begin(), anyVector.end(), [] (const T& element){ // code } // Better for (const T& element : anyVector) { // code } }}} * Reminding the default value of parameters, if any, in a function definition can be useful. {{{ #!cpp int Increase(int param, int optionalParam = 1); //... int Increase(int param, int optionalParam /* = 1 */) { return param + optionalParam; } }}} * In `c++11`, it is possible to use the `auto` type-specifier, wherein the appropriate data-type is determined at compile time. Although potentially useful, overuse can cause serious problems in the long-run. Therefore, this code feature should be used in moderation. Specifically: - It should be clear from reading the code what type the `auto` is replacing. (Add a comment if necessary.) - It should only ever be used as a replacement for an iterator type. - It should only be used if the data-type-specifier it is standing in for is long. As a rule of thumb, if the line would be shorter than your chosen suitable line width (see convention on avoiding wide lines under [#Formatting formatting] above) without it, don't use it. * Favour early returns where possible. The following: {{{ #!cpp void foo(bool x) { if (x) { /* lines */ } } }}} is better when written like: {{{ #!cpp void foo(bool x) { if (!x) return; /* lines */ } }}} == !JavaScript == * Use the same basic formatting as described above for C++. * Use roughly the same Doxygen-style comments for documentation as described above for C++. (But we don't actually run Doxygen on the JS code, so there's no need to make the comments use technically correct Doxygen syntax.) * When using Class syntax for JS: * Literals are stored in the prototype (e.g. `BuildingAI.prototype.MaxPreferenceBonus = 2;`). * The Schema is also a literal and hence is stored in the prototype. {{{ #!js class Example { Init() { this.value = 0; } } Example.prototype.Schema = ""; Example.prototype.MaxValue = 2; Engine.RegisterComponentType(IID_Example, "Example", Example); }}} * Don't omit the optional semicolons after statements. * Use quotes around the key names in object literals: {{{ #!js let x = 100, y = 200; let pos = { "x": x, "y": y }; }}} * Create empty arrays and objects with "`[]`" and "`{}`" respectively, not with "`new Array()`" and "`new Object()`". * Global variables and constants are named with a `g_` prefix and the rest of the name in !CamelCase, ie. `var g_ParsedData` or `const g_BarterActions`. * Non-standard !SpiderMonkey extensions to the JS language may be used (though prefer to use equivalent standard features when possible). Documentation: [https://developer.mozilla.org/en/JavaScript/New_in_JavaScript/1.6 1.6], [https://developer.mozilla.org/en/JavaScript/New_in_JavaScript/1.7 1.7], [https://developer.mozilla.org/en/JavaScript/New_in_JavaScript/1.8 1.8], [https://developer.mozilla.org/En/JavaScript/New_in_JavaScript/1.8.1 1.8.1], [https://developer.mozilla.org/en/JavaScript/New_in_JavaScript/1.8.5 1.8.5], [https://developer.mozilla.org/en/JavaScript_typed_arrays typed arrays]. * Omit zeroes after decimal point if possible. Use `1` instead `1.0` and `1.5` instead `1.50` * To convert a string to a number, use the "`+`" prefix operator (not e.g. `parseInt`/`parseFloat`): {{{ #!js let a = "1"; let b = a + 1; // string concatenation; b == "11" let c = (+a) + 1; // numeric addition; c == 2 }}} * Always check for undefined properties and/or invalid object references, if it's possible they could occur. * To test if a property or variable is undefined, use explicit type+value equality (`===`), instead of value equality (`==`) or `typeof()`: {{{ #!js if (someObject.foo === undefined) // foo is not defined else // foo is defined }}} * In general you don't want to explicitly check for `null` which has a distinct, often misunderstood, meaning from `undefined`. A few parts of the engine return a `null` object reference (for example, the component system when a component is not available for the specified entity, or the GUI when a requested object was not found), you can check for valid object references easily: {{{ #!js if (!cmpFoo) // Oh it's not a valid component, don't use it else // It is a valid component, we can use it }}} * Use `var` only when declaring variables in the global scope. When inside functions, ifs, loops, etc., use `let` or `const`. We prefer `let` because: - It is restricted to the scope it is declared in, preventing unintentional clobbering of values - It is stricter than `var`, and throws an error if it a variable is declared twice within the same scope {{{ #!js { var x = 1; var x = "foo" // No Error } { let x = 1; let x = "foo"; // TypeError: redeclaration of let x } }}} - We prefer `const` when the value is not intended to be changed. This allows easier readability and is considered good practice to prevent accidental changing of values. * "Do not use let or const in global scope unless strictly necessary." to allow easier moddability. refs. #6094 (mostly a GUI concern) == JSON == * Basically follow the JS formatting conventions. * When on the same line, insert spaces after `{`, and `:` and before `}`, but not after `[` or before `]`; e.g. `{ "foo": "bar" }` and `["foo", "bar"]`. * Use tabs for indentation, not spaces. * The files should have the MIME-TYPE `text/json` (not `application/json` since that may break diffing). == XML == * All XML files should start with {{{ }}} and be UTF-8 encoded (preferably without a BOM but that doesn't really matter). * Empty-element tags should be written without a trailing space: use `` or ``, not `` nor ``. * Indent using two spaces. * The files should have the mime-type `text/xml`. == GLSL == * Use the same basic formatting as described above for C++. * Use lowerCamelCase for names: {{{ #!glsl // Bad vec2 local_position; mat4 CalculateInvertMatrix(...); int _pixelcounter; // Good vec2 localPosition; mat4 calculateInvertMatrix(...); int pixelCounter; }}} * File extensions for shaders: * `.fs` - fragment shader * `.vs` - vertex shader * `.gs` - geometry shader * `.cs` - compute shader * All GLSL files should start with right [[https://www.khronos.org/opengl/wiki/Core_Language_(GLSL)#Version|version]]. {{{ #!glsl #version 120 }}} * Prefer to use own uniforms instead of built-in (`gl_ProjectionMatrix`, `gl_ModelViewProjectionMatrix`, etc). * Strictly follow specifications for the selected version. Some drivers ignore errors, that can fail others. * Prefer to use swizzle masks: {{{ #!glsl vec4 a; vec2 b; // Simple assign. a.x = b.x; a.y = b.z; // Assign with swizzle mask. a = b.xz; }}} * Prefer to use prefixes for global variables to distinguish them: `a_` - for attributes, `v_` for varyings, `u_` - for uniforms. {{{#!glsl uniform mat4 u_invertTransform; varying vec2 v_uv; attribute vec3 a_normal; }}}