1# Open Screen Library Style Guide
2
3The Open Screen Library follows the [Chromium C++ coding style](https://chromium.googlesource.com/chromium/src/+/master/styleguide/c++/c++.md)
4which, in turn, defers to the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html).
5We also follow the [Chromium C++ Do's and Don'ts](https://sites.google.com/a/chromium.org/dev/developers/coding-style/cpp-dos-and-donts).
6
7C++14 language and library features are allowed in the Open Screen Library
8according to the [C++14 use in Chromium](https://chromium-cpp.appspot.com)
9guidelines.
10
11In general Open Screen follows [You Aren't Gonna Need
12It](https://martinfowler.com/bliki/Yagni.html) principles.
13
14## Disallowed Styles and Features
15
16Blink style is *not allowed* anywhere in the Open Screen Library.
17
18C++17-only features are currently *not allowed* in the Open Screen Library.
19
20GCC does not support designated initializers for non-trivial types.  This means
21that the `.member = value` struct initialization syntax is not supported unless
22all struct members are primitive types or structs of primitive types (i.e. no
23unions, complex constructors, etc.).
24
25## Modifications to the Chromium C++ Guidelines
26
27- `<functional>` and `std::function` objects are allowed.
28- `<chrono>` is allowed and encouraged for representation of time.
29- Abseil types are allowed based on the allowed list in [DEPS](
30  https://chromium.googlesource.com/openscreen/+/refs/heads/master/DEPS).
31- However, Abseil types **must not be used in public APIs**.
32- `<thread>` and `<mutex>` are allowed, but discouraged from general use as the
33  library only needs to handle threading in very specific places;
34  see [threading.md](threading.md).
35- Following YAGNI principles, only implement comparison operator overloads as
36  needed; for example, implementing operator< for use in an STL container does
37  not require implementing all comparison operators.
38
39## Code Syntax
40
41- Braces are optional for single-line if statements; follow the style of
42  surrounding code.
43- Using-declarations are banned from headers.  Type aliases should not be
44  included in headers, except at class scope when they form part of the class
45  definition.
46    - Exception: Using-declarations for convenience may be put into a shared
47      header for internal library use.  These may only be included in
48      .cc files.
49    - Exception: if a class has an associated POD identifier (int/string), then
50      declare a type alias at namespace scope for that identifier instead of using
51      the POD type.  For example, if a class Foo has a string identifier, declare
52      `using FooId = std::string` in foo.h.
53
54## Copy and Move Operators
55
56Use the following guidelines when deciding on copy and move semantics for
57objects:
58
59- Objects with data members greater than 32 bytes should be move-able.
60- Known large objects (I/O buffers, etc.) should be be move-only.
61- Variable length objects should be move-able
62  (since they may be arbitrarily large in size) and, if possible, move-only.
63- Inherently non-copyable objects (like sockets) should be move-only.
64
65### Default Copy and Move Operators
66
67We [prefer the use of `default` and `delete`](https://sites.google.com/a/chromium.org/dev/developers/coding-style/cpp-dos-and-donts#TOC-Prefer-to-use-default)
68to declare the copy and move semantics of objects.  See
69[Stroustrup's C++ FAQ](http://www.stroustrup.com/C++11FAQ.html#default)
70for details on how to do that.
71
72Classes should prefer [member
73initialization](https://en.cppreference.com/w/cpp/language/data_members#Member_initialization)
74for POD members (as opposed to value initialization in the constructor).  Every POD
75member must be initialized by every constructor, of course, to prevent
76(https://en.cppreference.com/w/cpp/language/default_initialization)[default
77initialization] from setting them to indeterminate values.
78
79### User Defined Copy and Move Operators
80
81Classes should follow the [rule of three/five/zero](https://en.cppreference.com/w/cpp/language/rule_of_three).
82
83This means that if they implement a destructor or any of the copy or move
84operators, then all five (destructor, copy & move constructors, copy & move
85assignment operators) should be defined or marked as `delete`d as appropriate.
86Finally, polymorphic base classes with virtual destructors should `default` all
87constructors, destructors, and assignment operators.
88
89Note that operator definitions belong in the source (`.cc`) file, including
90`default`, with the exception of `delete`, because it is not a definition,
91rather a declaration that there is no definition, and thus belongs in the header
92(`.h`) file.
93
94## Passing Objects by Value or Reference
95
96In most cases, pass by value is preferred as it is simpler and more flexible.
97If the object being passed is move-only, then no extra copies will be made.  If
98it is not, be aware this may result in unintended copies.
99
100To guarantee that ownership is transferred, pass by rvalue reference for objects
101with move operators.  Often this means adding an overload that takes a const
102reference as well.
103
104Pass ownership via `std::unique_ptr<>` for non-movable objects.
105
106Ref: [Google Style Guide on Rvalue References](https://google.github.io/styleguide/cppguide.html#Rvalue_references)
107
108## Noexcept
109
110We prefer to use `noexcept` on move constructors.  Although exceptions are not
111allowed, this declaration [enables STL optimizations](https://en.cppreference.com/w/cpp/language/noexcept_spec).
112
113TODO(https://issuetracker.google.com/issues/160731444): Enforce this
114
115Additionally, GCC requires that any type using a defaulted `noexcept` move
116constructor/operator= has a `noexcept` copy or move constructor/operator= for
117all of its members.
118
119## Template Programming
120
121Template programming should be not be used to write generic algorithms or
122classes when there is no application of the code to more than one type.  When
123similar code applies to multiple types, use templates sparingly and on a
124case-by-case basis.
125
126## Unit testing
127
128Follow Google’s testing best practices for C++.  Design classes in such a way
129that testing the public API is sufficient.  Strive to follow this guidance,
130trading off with the amount of public API surfaces needed and long-term
131maintainability.
132
133Ref: [Test Behavior, Not Implementation](https://testing.googleblog.com/2013/08/testing-on-toilet-test-behavior-not.html)
134
135## Open Screen Library Features
136
137- For public API functions that return values or errors, please return
138  [`ErrorOr<T>`](https://chromium.googlesource.com/openscreen/+/master/platform/base/error.h).
139- In the implementation of public APIs invoked by the embedder, use
140  `OSP_DCHECK(TaskRunner::IsRunningOnTaskRunner())` to catch thread safety
141  problems early.
142
143### Helpers for `std::chrono`
144
145One of the trickier parts of the Open Screen Library is using time and clock
146functionality provided by [`platform/api/time.h`](https://chromium.googlesource.com/openscreen/+/refs/heads/master/platform/api/time.h).
147
148- When working extensively with `std::chrono` types in implementation code,
149  [`util/chrono_helpers.h`](https://chromium.googlesource.com/openscreen/+/refs/heads/master/util/chrono_helpers.h)
150  can be included for access to type aliases for
151  common `std::chrono` types, so they can just be referred to as `hours`,
152  `milliseconds`, etc. This header also includes helpful conversion functions,
153  such as `to_milliseconds` instead of
154  `std::chrono::duration_cast<std::chrono::milliseconds>`.
155  `util/chrono_helpers.h` can only be used in library-internal code, and
156  this is enforced by DEPS.
157- `Clock::duration` is defined currently as `std::chrono::microseconds`, and
158  thus is generally not suitable as a time type (developers generally think in
159  milliseconds). Prefer casting from explicit time types using
160  `Clock::to_duration`, e.g. `Clock::to_duration(seconds(2))`
161  instead of using `Clock::duration` types directly.
162
163### OSP_CHECK and OSP_DCHECK
164
165These are provided in [`platform/api/logging.h`](https://chromium.googlesource.com/openscreen/+/refs/heads/master/platform/api/logging.h)
166and act as run-time assertions (i.e., they
167test an expression, and crash the program if it evaluates as false). They are
168not only useful in determining correctness, but also serve as inline
169documentation of the assumptions being made in the code. They should be used in
170cases where they would fail only due to current or future coding errors.
171
172These should *not* be used to sanitize non-const data, or data otherwise derived
173from external inputs. Instead, one should code proper error-checking and
174handling for such things.
175
176OSP_CHECKs are "turned on" for all build types. However, OSP_DCHECKs are only
177"turned on" in Debug builds, or in any build where the `dcheck_always_on=true`
178GN argument is being used. In fact, at any time during development (including
179Release builds), it is highly recommended to use `dcheck_always_on=true` to
180catch bugs.
181
182When OSP_DCHECKs are "turned off" they effectively become code comments: All
183supported compilers will not generate any code, and they will automatically
184strip-out unused functions and constants referenced in OSP_DCHECK expressions
185(unless they are "extern" to the local module); and so there is absolutely no
186run-time/space overhead when the program runs. For this reason, a developer need
187not explicitly sprinkle "#if OSP_DCHECK_IS_ON()" guards all around any
188functions, variables, etc. that will be unused in "DCHECK off" builds.
189
190Use OSP_DCHECK and OSP_CHECK in accordance with the
191[Chromium guidance for DCHECK/CHECK](https://chromium.googlesource.com/chromium/src/+/master/styleguide/c++/c++.md#check_dcheck_and-notreached).
192