The open source OpenXR runtime
1# Code Style and Conventions {#conventions} 2 3<!-- 4Copyright 2021-2022, Collabora, Ltd. and the Monado contributors 5SPDX-License-Identifier: BSL-1.0 6--> 7 8<!-- 9 10NOTE to editors: To avoid stale references, make sure to mention relevant names 11using markup like @ref xrt_device so that Doxygen tries to parse and link names. 12This will result in Doxygen warnings if we change the name of something 13mentioned in these examples. 14 15--> 16 17[TOC] 18 19Here are some general code style guidelines we follow. 20 21Note that we aim to "code with respect", to avoid terminology that may limit our 22community or hurt those in it, as well as to conform with emerging industry 23standards. Good guidelines to look to include the 24[Android Coding with Respect][] policy and the 25[Write Inclusive Documentation][] page from the Google developer documentation 26style guide. The latter also links to a word list for clear documentation, 27which, while not binding on this project, is useful in making sure your code, 28comments, and docs are understandable by the worldwide Monado community. 29 30[Android Coding with Respect]: https://source.android.com/setup/contribute/respectful-code 31[Write Inclusive Documentation]: https://developers.google.com/style/inclusive-documentation 32 33## Changelog fragments 34 35In Monado we strongly prefer if all MRs merged into the main Monado repository 36also includes changelog fragments. A changelog fragment is a small file 37detailing the changes in the MR on a per area basis. They are slightly more 38detailed then a commit subject line, but usually just one or two lines, usually 39providing a little bit of context to the change. Sometimes for "big" changes 40they are more detailed and provide paragraph of description, such as for adding 41of new drivers, or large refactors. The changelog fragments are used to generate 42the @ref CHANGELOG file, updated on each release (and running on the CI). The 43changelog is generated with [proclamation][]. 44 45The changelog fragments are located in the `doc/changes` folder, organised into 46sub-categories in subfolders. There isn't a 1-to-1 mapping of changelog fragment 47to commit, but instead they are per change. A changelog fragment file is named 48`mr.` + MR number + `.md`, for MR 1234 it would be named `mr.1234.md`. If a MR 49has multiple changes for one sub-category a number is added between the MR 50number and file extension, example `mr.1234.1.md` and `mr.1234.2.md`. If a 51change spans multiple MRs, such as fixing a feature introduced in a earlier MR 52we imply the use of YAML headers to mark a changelog fragment applying to 53multiple MRs, can also be used to link issues. 54 55``` 56--- 57- mr.1234 58- issue.42 59- mr.2000 60--- 61 62Add cool feature X, it gives the answer to life, the universe and everything 63else. 64``` 65 66Generally the last commit in a MR adds the changelog fragments, as unfortunately 67MR numbers are allocated when opened, also provides a nice readable separation 68between MRs in the git history. Examples for commits adding changelog fragments 69can be seen [here][example1], [here][example2] and [here][example3]. 70 71[example1]: https://gitlab.freedesktop.org/monado/monado/-/commit/5e0f0866a6f74116acbc46c6e2447fdb8c716d02 72[example2]: https://gitlab.freedesktop.org/monado/monado/-/commit/98a5b18e0f90dab9f2ea5c2bbfd4ccd4998121c4 73[example3]: https://gitlab.freedesktop.org/monado/monado/-/commit/785e99f115df87dd4561fe6f88a7988b5834b650 74[proclamation]: https://gitlab.com/proclamation/proclamation 75 76## APIs 77 78Internal APIs, when it makes sense, should be C APIs. Headers that define 79general communication interfaces between modules (not only use of utilities) 80belong in the `xrt/include/xrt` directory, and should not depend on any other module outside 81that directory. (As a historical note: this directory gets its name from a 82compressed version of the phrase "XR RunTime", a generic term for Monado and an 83early development codename. Also, it's shorter than `monado_` and so nicer to 84use in code.) 85 86What follows are some basic API usage rules. Note that all the module usage 87relations must be expressed in the build system, so module usage should form a 88directed-acyclic-graph. 89 90- Any module can implement or use APIs declared in `xrt/include/xrt` 91- Any module (except the `xrt` interface headers themselves) can (and should!) 92 use APIs declared in `xrt/auxiliary/util`. 93- Any module except for `auxiliary/util` and the `xrt` interface headers 94 themselves can use APIs declared in other `xrt/auxiliary` modules. 95 96## Naming 97 98- C APIs: 99 - `lower_snake_case` for types and functions. 100 - `UPPER_SNAKE_CASE` for macros. e.g. @ref U_TYPED_CALLOC (which is how all 101 allocations in C code should be performed) 102 - Prefix names with a "namespace" - the library/module where they reside. e.g. 103 @ref u_var_add_root, @ref math_pose_validate 104 - Related: only things prefixed by `xrt_` belong in the `xrt/include/xrt` 105 directory, and nothing named starting with `xrt_` should be declared 106 anywhere else. (Interfaces *declared* in `xrt/include/xrt` are 107 *implemented* in other modules.) 108 - Generally, we do not declare typedefs for `struct` and `enum` types, but 109 instead refer to them in long form, saying `struct` or `enum` then the name. 110 The exception to not using typedefs is function pointers used as function 111 arguments as these become very hard to both read and type out. 112 - If a typedef is needed, it should be named ending with `_t`. Function 113 pointer typedefs should end with `_func_t`. 114 - Parameters: `lower_snake_case` or acronyms. 115 - Output parameters should begin with `out_`. 116 - Of special note: Structures/types that represent "objects" often have long 117 type names or "conceptual" names. When a pointer to them is passed to a 118 function or kept as a local variable, it is typically named by taking the 119 first letter of each (typically `_`-delimited) word in the structure type 120 name. Sometimes, it is an abbreviated form of that name instead. Relevant 121 examples: 122 - @ref xrt_comp_native_create_swapchain() is a member function of the 123 interface @ref xrt_compositor_native, and takes a pointer to that 124 interface named `xcn`. It creates an @ref xrt_swapchain, which it 125 populates in the parameter named `out_xscn`: `out_` because it's a 126 purely output parameter, `xscn` from @ref xrt_swapchain_native 127 specifically the letters `Xrt_SwapChain_Native`. @ref xrt_swapchain and 128 related types are a small exception to the rules - there are only 2 129 words if you go by the `_` delimiters, but for clarity we treat 130 swapchain as if it were two words when abbreviating. A few other places 131 in the `xrt` headers use `x` + an abbreviated name form, like `xinst` 132 for @ref xrt_instance, `xdev` for @ref xrt_device, `xsysd` sometimes 133 used for @ref xrt_system_devices. 134 - `create` and `destroy` are used when the functions actually perform 135 allocation and return the new object, or deallocation of the passed-in 136 object. 137 - If some initialization or cleanup is required but the type is not opaque and 138 is allocated by the caller, the names to use are `init` and, if needed, one 139 of `cleanup`/`fini`/`teardown`. (We are not yet consistent on these names.) 140 One common example is when there is some shared code and a structure 141 partially implementing an interface: a further-derived object may need to 142 call an `init` function on the shared structure, but it was allocated by the 143 derived object and held by value. 144- C++: 145 - Where a C API is exposed, it should follow the C API naming schemes. 146 - If only a C++ API is exposed, a fairly conventional C++ naming scheme is used: 147 - Namespaces: nested to match directory structure, starting with `xrt::`. 148 - There are no C++ interfaces in the `xrt/include/xrt`, by design, so this 149 is not ambiguous. 150 - Place types that need to be exposed in a header for technical reasons, 151 but that are still considered implementation details, within a 152 further-nested `detail` namespace, as seen elsewhere in the C++ 153 ecosystem. 154 - Types/classes: `CamelCase` 155 - Methods/functions: `lowerCamelCase` 156 - Constants/constexpr values: `kCamelCase` 157 - If a header is only usable from C++ code, it should be named with the 158 extension `.hpp` to signify this. 159- Math: 160 - For different types of transforms `T` between two entities `A` and `B`, try 161 to use variable names like `T_A_B` to express the transform such that `B = 162 T_A_B * A`. This is equivalent to "`B` expressed w.r.t. `A`" and "the 163 transform that converts a point in `B` coordinates into `A` coordinates". 164 `T` can be used for 4x4 isometry matrices, but you can use others like 165 `P` for poses, `R` for 3x3 rotations, `Q` for quaternion rotations, `t` for 166 translations, etc. 167 168## Patterns and Idioms 169 170This is an incomplete list of conventional idioms used in the Monado codebase. 171 172### C "Inheritance" through first struct member 173 174Despite being in C, the design is fairly object-oriented. Types implement 175interfaces and derive from other types typically by placing a field of that 176parent type/interface as their first element, conventionally named `base`. This 177means that a pointer to the derived type, and a pointer to the base type, have 178the same value. 179 180For example, consider @ref client_gl_swapchain 181 182- Its first element is named @ref client_gl_swapchain::base and is of type 183 @ref xrt_swapchain_gl - meaning that it implements @ref xrt_swapchain_gl 184- @ref xrt_swapchain_gl in turn starts with @ref xrt_swapchain_gl::base which is 185 @ref xrt_swapchain - meaning that @ref xrt_swapchain_gl **extends** @ref 186 xrt_swapchain. (Both @ref xrt_swapchain_gl and @ref xrt_swapchain are abstract 187 interfaces, as indicated by the `xrt_` prefix.) 188 189Structures/types that represent "objects" are often passed as the first 190parameter to many functions, which serve as their "member functions". Sometimes, 191these types are opaque and not related to other types in the system in a 192user-visible way: they should have a `_create` and `_destroy` function. See @ref 193time_state, @ref time_state_create, @ref time_state_destroy 194 195In other cases, an interface will have function pointers defined as fields in 196the interface structure. (A type implementing these may be opaque, but would 197begin with a member of the interface/base type.) These interface function 198pointers must still take in a self pointer as their first parameter, because 199there is no implied `this` pointer in C. This would result in awkward calls with 200repeated, error-prone mentions of the object pointer, such as this example 201calling the @ref xrt_device::update_inputs interface: 202`xdev->update_inputs(xdev)`. These are typically wrapped by inline free 203functions that make the call through the function pointer. Considering again the 204@ref xrt_device example, the way you would call @ref xrt_device::update_inputs 205is actually @ref xrt_device_update_inputs(). 206 207### Destroy takes a pointer to a pointer, nulls it out 208 209Destroy free functions should take a pointer to a pointer, performing null checks 210before destruction, and setting null. They always succeed (void return): a 211failure when destroying an object has little meaning in most cases. For a 212sample, see @ref xrt_images_destroy. It would be used like this: 213 214```c 215struct xrt_image_native_allocator *xina = /* created and initialized, or maybe NULL */; 216 217/* ... */ 218 219xrt_images_destroy(&xina); 220 221/* here, xina is NULL in all cases, and if it wasn't NULL before, it has been freed. */ 222``` 223 224Note that this pattern is used in most cases but not all in the codebase: we 225are gradually migrating those that don't fit this pattern. If you call a 226destroy function that does not take a pointer-to-a-pointer, make sure to do 227null checks before calling and set it to null after it returns. 228 229Also note: when an interface includes a "destroy" function pointer, it takes the 230normal pointer to an object: The free function wrapper is the one that takes a 231pointer-to-a-pointer and handles the null checks. See for example @ref 232xrt_instance_destroy takes the pointer-to-a-pointer, while the interface method 233@ref xrt_instance::destroy takes the single pointer.