[[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.) == C++ == === Creating new files === * All source files (.cpp, .h) must start with the following GPL license header, before any other content: {{{ #!cpp /* Copyright (C) 2016 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 `2016` 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) 2016 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` * The first non-comment line of any source file must be `#include "precompiled.h"` === 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; } 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.stack.nl/~dimitri/doxygen/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 }}} === Misc === * In header files, avoid `#include` and use forward declarations wherever possible. * Sometimes it's nice to put `#include`s in alphabetical order. (It's a bit prettier, and makes it easier to find if some header is already in the list or not.) (But when `Foo.cpp` includes `Foo.h`, that should usually go before every other include (except for `precompiled.h` which must always be first).) * 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. * Don't do "`if (p) delete p;`". (That's redundant since "`delete NULL;`" 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 = NULL;`") 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; } }}} == !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.) * Don't omit the optional semicolons after statements. * Use quotes around the key names in object literals: {{{ #!js var x = 100, y = 200; var pos = { "x": x, "y": y }; }}} * Create empty arrays and objects with "`[]`" and "`{}`" respectively, not with "`new Array()`" and "`new Object()`". * 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]. * To convert a string to a number, use the "`+`" prefix operator (not e.g. `parseInt`/`parseFloat`): {{{ #!js var a = "1"; var b = a + 1; // string concatenation; b == "11" var 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 }}} == 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 whatever the similar existing XML files use. (Usually two spaces; sometimes four spaces, or tabs, etc.)