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.