diff options
| author | Leitwolf11 | 2026-11-13 17:21:51 +0300 |
|---|---|---|
| committer | Leitwolf11 | 2026-11-13 17:21:51 +0300 |
| commit | cb3d4d6efce480ebbffeba5bf96c2f58396d7466 (patch) | |
| tree | 9fe78ed08ce098a9d9ba21a0f4f071d634331bfc | |
| parent | c0e89a73611f5730ff4d5172adb2a07be98d7b48 (diff) | |
| download | progmasoft-cpp-cb3d4d6efce480ebbffeba5bf96c2f58396d7466.tar.gz | |
Lay the guide out as Common, Cpp17 and Cpp20 with an index
The guide moves to Progmasoft_Core_Cpp_Coding_Guidelines/:
- Common/ holds the thirteen chapters, which apply to C++17 and C++20
alike. Where a chapter used a C++20 feature, it now states the rule
for both versions and links to the page of each.
- Cpp20/LANGUAGE.md holds the rules that use C++20 features: building
with -std=c++20, designated initializers, consteval and constinit,
std::bit_cast, concepts, defaulted comparisons, ranges, std::span,
std::format, the smaller features, and the C++20 features not used.
- Cpp17/LANGUAGE.md holds what a C++17 project does instead: building
with -std=c++17, the C++17 form of each C++20 rule (the former
section 5.13) and the checks a C++17 library needs for later
standards.
- index.json lists every document with its chapter, its path and the
versions it applies to, and names the default version.
No rule changed. The README lists the version pages and the layout,
ADOPTING.md points to the page of the chosen version, and the changelog
says where Guide/ went. Every relative link and heading anchor was
checked.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
19 files changed, 222 insertions, 80 deletions
diff --git a/ADOPTING.md b/ADOPTING.md index 370f181..19667c3 100644 --- a/ADOPTING.md +++ b/ADOPTING.md @@ -21,15 +21,17 @@ Deviations of this project are listed below. Do not copy the rules into the project. A copy drifts from the guide, and two documents that disagree are worse than one. -State the language version in the same section, C++20 or C++17; [the guide says how to choose](Guide/LANGUAGE.md). +State the language version in the same section, C++20 or C++17; [the guide says how to choose](Progmasoft_Core_Cpp_Coding_Guidelines/Common/LANGUAGE.md), and +the project follows the page of its version ([C++20](Progmasoft_Core_Cpp_Coding_Guidelines/Cpp20/LANGUAGE.md), +[C++17](Progmasoft_Core_Cpp_Coding_Guidelines/Cpp17/LANGUAGE.md)). ## 2. Copy the profiles | File | From | | --- | --- | -| `.clang-format` | [Formatting: the profile](Guide/FORMATTING.md#the-profile) | -| `.clang-tidy` | [Tooling: the clang-tidy profile](Guide/TOOLING.md#132-the-clang-tidy-profile), with the path filters set to the project's directories | -| compiler flags | [Tooling: flags](Guide/TOOLING.md#flags), in the build files of the project | +| `.clang-format` | [Formatting: the profile](Progmasoft_Core_Cpp_Coding_Guidelines/Common/FORMATTING.md#the-profile) | +| `.clang-tidy` | [Tooling: the clang-tidy profile](Progmasoft_Core_Cpp_Coding_Guidelines/Common/TOOLING.md#132-the-clang-tidy-profile), with the path filters set to the project's directories | +| compiler flags | [Tooling: flags](Progmasoft_Core_Cpp_Coding_Guidelines/Common/TOOLING.md#flags), in the build files of the project | The profiles are in the public domain (see [LICENSE.md](LICENSE.md)), so they can be copied under the project's own license. Note the guide revision they come from in a comment at the top of each file, so a later update can be compared. diff --git a/CHANGELOG.md b/CHANGELOG.md index 43b58b5..ddd82cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,13 @@ A revision of the guide is named by its date. A change that requires existing co ## Unreleased +The guide moved to `Progmasoft_Core_Cpp_Coding_Guidelines/`. `Common/` holds the thirteen chapters, which apply to both versions; +`Cpp20/` and `Cpp17/` hold what applies to one version: the rules that use C++20 features, and their C++17 form with +the checks a C++17 library needs. `index.json` lists every document with its chapter and versions. No rule changed; +links to `Guide/` move to `Progmasoft_Core_Cpp_Coding_Guidelines/Common/`. + The guide covers C++17 as well as C++20. A project picks one; C++20 stays the default, and C++17 is for libraries meant -for other people's projects. [Section 5.13](Guide/LANGUAGE.md#513-c17-projects) lists the C++17 form of each C++20 +for other people's projects. [The C++17 page](Progmasoft_Core_Cpp_Coding_Guidelines/Cpp17/LANGUAGE.md) lists the C++17 form of each C++20 feature the guide uses. Existing C++20 projects need no change. ## 2026-10-18 diff --git a/Guide/C-INTERFACES.md b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/C-INTERFACES.md index 2def35e..2def35e 100644 --- a/Guide/C-INTERFACES.md +++ b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/C-INTERFACES.md diff --git a/Guide/COMMENTS-AND-DOCUMENTATION.md b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/COMMENTS-AND-DOCUMENTATION.md index 7cdffb7..7cdffb7 100644 --- a/Guide/COMMENTS-AND-DOCUMENTATION.md +++ b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/COMMENTS-AND-DOCUMENTATION.md diff --git a/Guide/CONCURRENCY.md b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/CONCURRENCY.md index 9099de3..9099de3 100644 --- a/Guide/CONCURRENCY.md +++ b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/CONCURRENCY.md diff --git a/Guide/ERRORS.md b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/ERRORS.md index 20c524f..20c524f 100644 --- a/Guide/ERRORS.md +++ b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/ERRORS.md diff --git a/Guide/FILES-AND-HEADERS.md b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/FILES-AND-HEADERS.md index 579be87..579be87 100644 --- a/Guide/FILES-AND-HEADERS.md +++ b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/FILES-AND-HEADERS.md diff --git a/Guide/FORMATTING.md b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/FORMATTING.md index 239ee9f..0b383af 100644 --- a/Guide/FORMATTING.md +++ b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/FORMATTING.md @@ -62,7 +62,7 @@ CI, because two versions can format the same file differently. --- Language: Cpp BasedOnStyle: LLVM -Standard: c++20 # c++17 in a C++17 project +Standard: c++20 # c++17 in a C++17 project; see the page of the version UseTab: Never IndentWidth: 4 diff --git a/Guide/LANGUAGE.md b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/LANGUAGE.md index 7858103..246bdc6 100644 --- a/Guide/LANGUAGE.md +++ b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/LANGUAGE.md @@ -5,14 +5,12 @@ The guide covers two language versions, and a project picks one: -- **C++20** is the default, for applications and for libraries used inside one product. -- **C++17** is for a library meant for other people's projects, because C++17 is still the widest common target and code - written for it also works in every later standard. A C++20 library would leave those users out. +- **[C++20](../Cpp20/LANGUAGE.md)** is the default, for applications and for libraries used inside one product. +- **[C++17](../Cpp17/LANGUAGE.md)** is for a library meant for other people's projects, because C++17 is still the + widest common target and code written for it also works in every later standard. -A project builds with exactly its version, `-std=c++20` or `-std=c++17`, and no compiler extensions (`-Wpedantic` is on). -Its deviations do not change the version: a C++17 project uses no C++20 feature, even where a compiler accepts one. -A feature of a later standard is used only through a library that provides it, such as `llvm::Expected` in place of -`std::expected`. [Section 5.13](#513-c17-projects) gives the C++17 form of what the other sections write in C++20. +A project builds with exactly its version and no compiler extensions. Its deviations do not change the version. This +chapter holds the rules of both versions; the page of each version adds what uses its own features. ## 5.1 Functions @@ -68,8 +66,9 @@ Never `auto` for a variable that is meant to be a different type from the initia - **Braces for initialization, parentheses for a constructor that braces would hide.** `std::vector<int> values(3, 0)` makes three zeros; `std::vector<int> values{3, 0}` makes the list `{3, 0}`. Use parentheses only where braces would select an `std::initializer_list` constructor you do not want. -- **Designated initializers** name the fields of an aggregate: `Edge{.from = a, .to = b}`. Use them whenever an aggregate - has more than two fields or two fields of the same type. +- **Aggregates with many fields** are initialized so that each value names its field: designated initializers in + [C++20](../Cpp20/LANGUAGE.md#initialization), assignments after default construction in + [C++17](../Cpp17/LANGUAGE.md#the-c17-form-of-the-c20-rules). - **No narrowing.** Brace initialization refuses it; with `=` and parentheses, write the conversion out. ## 5.4 `const` and `constexpr` @@ -77,7 +76,7 @@ Never `auto` for a variable that is meant to be a different type from the initia - **`const` by default.** A local that does not change is `const`; a member function that does not change the object is `const`; a pointer or reference parameter that is only read points to `const`. - **`constexpr` for every value known at compile time**, and for functions that can be evaluated at compile time without - contortion. `consteval` when a function must only run at compile time. + contortion. C++20 adds [`consteval` and `constinit`](../Cpp20/LANGUAGE.md#compile-time-code). - **`inline constexpr`** for a constant in a header. `constexpr` on a static data member already implies `inline`. - Do not `const`-qualify a by-value parameter in a declaration; it means nothing to the caller. It may be `const` in the definition. @@ -87,7 +86,8 @@ Never `auto` for a variable that is meant to be a different type from the initia ## 5.5 Casts and conversions - **No C-style casts and no functional casts** for anything but constructing a value of class type. Use - `static_cast` for a value conversion, `std::bit_cast` to reinterpret the bits of a trivially copyable value, and + `static_cast` for a value conversion, the version's way to reinterpret the bits of a trivially copyable value + ([C++20](../Cpp20/LANGUAGE.md#casts), [C++17](../Cpp17/LANGUAGE.md#the-c17-form-of-the-c20-rules)), and `reinterpret_cast` only at a byte or ABI boundary, with a comment saying why the cast is valid. - **No implicit conversion between integer widths or signedness.** `-Wimplicit-int-conversion` and the clang-tidy profile reject them; write the `static_cast` and make sure it cannot lose a value, or check that it cannot. @@ -113,8 +113,8 @@ Never `auto` for a variable that is meant to be a different type from the initia - Text is UTF-8 in `std::string`. Take a read-only string parameter as `std::string_view` (or `llvm::StringRef` in code built on LLVM). Do not use `std::wstring`, `wchar_t`, or `std::u8string` in an interface. - Convert to a platform encoding only at the platform boundary, such as a Windows wide-character API. -- Format with `std::format`-style or `llvm::formatv`-style functions, or by streaming into a string builder; never with - `sprintf` into a fixed buffer. +- Format with a formatting library ([`std::format` in C++20](../Cpp20/LANGUAGE.md#formatting-text), `fmt` in C++17) or + `llvm::formatv`, or by streaming into a string builder; never with `sprintf` into a fixed buffer. - A view parameter must not be stored. A member that keeps text owns a `std::string`. ## 5.8 Enumerations @@ -134,15 +134,13 @@ A `switch` over an enumeration lists every enumerator and has no `default`, so t enumerator needs. When a value may come from outside the program, convert it to the enumeration with a function that validates it. -## 5.9 Templates and concepts +## 5.9 Templates -- **Constrain every template** with a concept or a `requires` clause instead of SFINAE: `template<std::integral Id>`. - The error then names the requirement, not a substitution failure deep inside. -- **Name a concept for a requirement used more than once.** Concepts are `PascalCase` nouns or adjectives: - `Hashable`, `ByteRange`. +- **Constrain every template**: with a concept in [C++20](../Cpp20/LANGUAGE.md#templates-and-concepts), with a named + trait in [C++17](../Cpp17/LANGUAGE.md#the-c17-form-of-the-c20-rules). The error then names the requirement. - **Keep templates thin.** Move the part that does not depend on the template parameters into a non-template function, to keep compile times and code size down. -- **No template metaprogramming where `constexpr` code would do.** An `if constexpr` branch or a `consteval` function is +- **No template metaprogramming where `constexpr` code would do.** An `if constexpr` branch or a `constexpr` function is easier to read than a recursive specialization. ## 5.10 Lambdas @@ -159,16 +157,14 @@ validates it. - **No `goto`.** - **Range-based `for`** whenever the index is not needed. When it is, iterate with `std::size_t`. - **`[[fallthrough]]`** marks every intended fall-through in a `switch`; `-Wimplicit-fallthrough` rejects the rest. -- **Ranges and algorithms** where they state the intent better than a loop: `std::ranges::sort(items)`, - `std::ranges::any_of(...)`. A plain loop is better than a pipeline of five views that needs a comment to explain. -- **`[[likely]]` and `[[unlikely]]`** only where a measurement showed a difference. +- **Algorithms** where they state the intent better than a loop; C++20 adds + [ranges](../Cpp20/LANGUAGE.md#ranges-views-and-algorithms). A plain loop is better than a chain that needs a comment + to explain. ## 5.12 Features not used | Feature | Instead | | --- | --- | -| C++20 modules | headers; build-tool support is not yet uniform across the supported compilers | -| Coroutines | explicit state machines or callbacks, until a project adopts a coroutine library deliberately | | RTTI: `dynamic_cast`, `typeid` | `std::variant` and `std::visit`, a kind enumeration, or LLVM-style `isa`/`cast`/`dyn_cast` | | Exceptions | see [Errors](ERRORS.md) | | `std::bind` | a lambda | @@ -177,34 +173,4 @@ validates it. | `alloca`, `setjmp`/`longjmp` | ordinary allocation and returns | | User-defined literals | named functions or constructors, except the standard ones such as `std::chrono` suffixes | -## 5.13 C++17 projects - -The rest of this guide applies unchanged. Where a section uses a C++20 feature, a C++17 project writes this instead: - -| C++20 | C++17 | -| --- | --- | -| Concepts and `requires` | a named trait, `kIsInteger<Type>`, used by `std::enable_if_t` in a defaulted template parameter; `static_assert` when no overload depends on it | -| `operator==` and `operator<=>` `= default` | written out over `std::tie` of all fields, with a test that fails when a field is left out | -| Designated initializers | default construct, then assign the fields; a factory function when this repeats | -| `std::span` | `const std::vector<T> &`, or a view such as `llvm::ArrayRef` where the project has one | -| `std::ranges` | the iterator algorithms: `std::sort(items.begin(), items.end())` | -| `std::bit_cast` | `static_cast` between integers, `std::memcpy` into a value otherwise | -| `consteval`, `constinit` | `constexpr` | -| Template lambdas | a function template in a `Detail` namespace | -| `std::erase_if` | erase-remove: `items.erase(std::remove_if(...), items.end())` | -| `contains`, `starts_with`, `ends_with` | `find`, `compare` or `substr` | -| `std::remove_cvref_t`, `std::identity` | one small header of the project, with a comment naming what it stands for | -| `std::format` | `fmt`, or streaming | -| `std::jthread` | `std::thread`, joined explicitly | -| `using enum` | qualified enumerators | -| `[[likely]]`, `[[unlikely]]` | nothing | - -Code written for C++17 is promised to later standards too, so its checks have to reach them: - -- **CI builds and tests in C++17 and in the newest standard the project supports.** A rule of C++20 can reject C++17 code, - such as the rewritten comparison operators. -- **CI builds with each compiler the project supports, not only Clang.** Clang accepts designated initializers in C++17 - and warns; MSVC rejects them. Clang's `-Wc++20-extensions` shows most of these before CI does. -- **Headers compile under the default modes of the compilers.** MSVC is permissive in C++17 unless `/permissive-` is - given, and a user of the library may not give it. A non-capturing lambda that reads a local `constexpr`, for instance, - is an error there. +The features C++20 adds that the guide does not use are on the [C++20 page](../Cpp20/LANGUAGE.md#not-used). diff --git a/Guide/NAMING.md b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/NAMING.md index d575ec8..d575ec8 100644 --- a/Guide/NAMING.md +++ b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/NAMING.md diff --git a/Guide/OWNERSHIP-AND-MEMORY.md b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/OWNERSHIP-AND-MEMORY.md index 94d32da..94d32da 100644 --- a/Guide/OWNERSHIP-AND-MEMORY.md +++ b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/OWNERSHIP-AND-MEMORY.md diff --git a/Guide/PRINCIPLES.md b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/PRINCIPLES.md index 777de88..2dbc64f 100644 --- a/Guide/PRINCIPLES.md +++ b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/PRINCIPLES.md @@ -49,7 +49,7 @@ design, names, ownership, and whether the change does what it says. A project that adopts this guide over existing code converges gradually. A file follows the guide where it is touched; a mechanical sweep is its own change, never mixed into a behavior change. Where a project deliberately deviates, it -writes the deviation down once (see [Adopting the guide](../ADOPTING.md)), and then the project's rule wins in that +writes the deviation down once (see [Adopting the guide](../../ADOPTING.md)), and then the project's rule wins in that project. ## 1.8 Small, explicit dependencies diff --git a/Guide/TESTING.md b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/TESTING.md index f35013f..f35013f 100644 --- a/Guide/TESTING.md +++ b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/TESTING.md diff --git a/Guide/TOOLING.md b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/TOOLING.md index 5852cd4..b6fe3c2 100644 --- a/Guide/TOOLING.md +++ b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/TOOLING.md @@ -15,7 +15,7 @@ as errors, in a CI job of its own. ### Flags ```text --std=c++20 # -std=c++17 in a C++17 project +-std=c++20 # -std=c++17 in a C++17 project: see Cpp20/ and Cpp17/ -fno-exceptions -Wall -Wextra -Wpedantic -Werror -Wshadow diff --git a/Guide/TYPES-AND-CLASSES.md b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/TYPES-AND-CLASSES.md index 93bf064..9902814 100644 --- a/Guide/TYPES-AND-CLASSES.md +++ b/Progmasoft_Core_Cpp_Coding_Guidelines/Common/TYPES-AND-CLASSES.md @@ -100,8 +100,8 @@ A type that must not be copied deletes the copy operations explicitly instead of - Overload an operator only when it means what it means for built-in types: `+` adds, `==` compares for equality, `[]` indexes. A reader must never wonder what `a << b` does to `a`. -- **Default comparisons**: `operator==` and, when there is an order, `operator<=>`, both `= default` where memberwise - comparison is right. [C++17 has neither](LANGUAGE.md#513-c17-projects). +- **Comparisons** compare every field: `= default` in [C++20](../Cpp20/LANGUAGE.md#comparisons), written out over + `std::tie` in [C++17](../Cpp17/LANGUAGE.md#the-c17-form-of-the-c20-rules). - Binary operators that are symmetric are hidden friends or non-members, so both operands convert the same way. - No overloads of `&&`, `||`, `,` or unary `&`. diff --git a/Progmasoft_Core_Cpp_Coding_Guidelines/Cpp17/LANGUAGE.md b/Progmasoft_Core_Cpp_Coding_Guidelines/Cpp17/LANGUAGE.md new file mode 100644 index 0000000..034f639 --- /dev/null +++ b/Progmasoft_Core_Cpp_Coding_Guidelines/Cpp17/LANGUAGE.md @@ -0,0 +1,52 @@ +<!-- SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> --> +<!-- SPDX-License-Identifier: CC-BY-4.0 --> + +# C++17 + +C++17 is for a library meant for other people's projects, because C++17 is still the widest common target and code +written for it also works in every later standard. A C++20 library would leave those users out. + +A C++17 project follows every chapter of [Common](../Common/PRINCIPLES.md) unchanged. It uses no C++20 feature, even +where a compiler accepts one; a feature of a later standard is used only through a library that provides it, such as +`llvm::Expected` in place of `std::expected`. + +## Building + +- The compiler flag is `-std=c++17`, and `/std:c++17` with `clang-cl`, next to the [flags](../Common/TOOLING.md#flags) + every project uses. +- The [clang-format profile](../Common/FORMATTING.md#the-profile) says `Standard: c++17`. +- No compiler extensions: `-Wpedantic` is on. + +## The C++17 form of the C++20 rules + +Where the [C++20 page](../Cpp20/LANGUAGE.md) uses a C++20 feature, a C++17 project writes this instead: + +| C++20 | C++17 | +| --- | --- | +| Concepts and `requires` | a named trait, `kIsInteger<Type>`, used by `std::enable_if_t` in a defaulted template parameter; `static_assert` when no overload depends on it | +| `operator==` and `operator<=>` `= default` | written out over `std::tie` of all fields, with a test that fails when a field is left out | +| Designated initializers | default construct, then assign the fields; a factory function when this repeats | +| `std::span` | `const std::vector<T> &`, or a view such as `llvm::ArrayRef` where the project has one | +| `std::ranges` | the iterator algorithms: `std::sort(items.begin(), items.end())` | +| `std::bit_cast` | `static_cast` between integers, `std::memcpy` into a value otherwise | +| `consteval`, `constinit` | `constexpr` | +| Template lambdas | a function template in a `Detail` namespace | +| `std::erase_if` | erase-remove: `items.erase(std::remove_if(...), items.end())` | +| `contains`, `starts_with`, `ends_with` | `find`, `compare` or `substr` | +| `std::remove_cvref_t`, `std::identity` | one small header of the project, with a comment naming what it stands for | +| `std::format` | `fmt`, or streaming | +| `std::jthread` | `std::thread`, joined explicitly | +| `using enum` | qualified enumerators | +| `[[likely]]`, `[[unlikely]]` | nothing | + +## Checks for later standards + +Code written for C++17 is promised to later standards too, so its checks have to reach them: + +- **CI builds and tests in C++17 and in the newest standard the project supports.** A rule of C++20 can reject C++17 code, + such as the rewritten comparison operators. +- **CI builds with each compiler the project supports, not only Clang.** Clang accepts designated initializers in C++17 + and warns; MSVC rejects them. Clang's `-Wc++20-extensions` shows most of these before CI does. +- **Headers compile under the default modes of the compilers.** MSVC is permissive in C++17 unless `/permissive-` is + given, and a user of the library may not give it. A non-capturing lambda that reads a local `constexpr`, for instance, + is an error there. diff --git a/Progmasoft_Core_Cpp_Coding_Guidelines/Cpp20/LANGUAGE.md b/Progmasoft_Core_Cpp_Coding_Guidelines/Cpp20/LANGUAGE.md new file mode 100644 index 0000000..4dcab83 --- /dev/null +++ b/Progmasoft_Core_Cpp_Coding_Guidelines/Cpp20/LANGUAGE.md @@ -0,0 +1,69 @@ +<!-- SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> --> +<!-- SPDX-License-Identifier: CC-BY-4.0 --> + +# C++20 + +C++20 is the default version: for applications and for libraries used inside one product. A C++20 project follows every +chapter of [Common](../Common/PRINCIPLES.md) and the rules below, which use what C++20 adds. The +[C++17 page](../Cpp17/LANGUAGE.md) gives the C++17 form of each of them. + +## Building + +- The compiler flag is `-std=c++20`, and `/std:c++20` with `clang-cl`, next to the [flags](../Common/TOOLING.md#flags) + every project uses. +- The [clang-format profile](../Common/FORMATTING.md#the-profile) says `Standard: c++20`. +- No compiler extensions: `-Wpedantic` is on. + +## Initialization + +**Designated initializers** name the fields of an aggregate: `Edge{.from = a, .to = b}`. Use them whenever an aggregate +has more than two fields or two fields of the same type. + +## Compile-time code + +`consteval` when a function must only run at compile time, and `constinit` for a static variable that must be +initialized before the program starts. Everything else known at compile time is `constexpr`, as +[Language](../Common/LANGUAGE.md#54-const-and-constexpr) says. + +## Casts + +`std::bit_cast` reinterprets the bits of a trivially copyable value. `reinterpret_cast` stays for byte and ABI +boundaries. + +## Templates and concepts + +- **Constrain every template** with a concept or a `requires` clause instead of SFINAE: `template<std::integral Id>`. + The error then names the requirement, not a substitution failure deep inside. +- **Name a concept for a requirement used more than once.** Concepts are `PascalCase` nouns or adjectives: + `Hashable`, `ByteRange`. +- **Template lambdas** where a generic lambda needs to name its parameter type. + +## Comparisons + +`operator==` and, when there is an order, `operator<=>`, both `= default` where memberwise comparison is right. The +other rules of [Operators](../Common/TYPES-AND-CLASSES.md#66-operators) hold as they are. + +## Ranges, views and algorithms + +- **Ranges and algorithms** where they state the intent better than a loop: `std::ranges::sort(items)`, + `std::ranges::any_of(...)`. A plain loop is better than a pipeline of five views that needs a comment to explain. +- **`std::span`** for a parameter that reads a contiguous sequence it does not own. +- `std::erase_if`, and `contains`, `starts_with` and `ends_with` of the standard containers and strings. + +## Formatting text + +`std::format` and its relatives, never `sprintf` into a fixed buffer. + +## Smaller features + +- `std::jthread` for a thread that stops and joins when it goes out of scope. +- `using enum` inside a function or a `switch` that names many enumerators of one enumeration. +- `std::remove_cvref_t` and `std::identity` from the standard library. +- **`[[likely]]` and `[[unlikely]]`** only where a measurement showed a difference. + +## Not used + +| Feature | Instead | +| --- | --- | +| C++20 modules | headers; build-tool support is not yet uniform across the supported compilers | +| Coroutines | explicit state machines or callbacks, until a project adopts a coroutine library deliberately | diff --git a/Progmasoft_Core_Cpp_Coding_Guidelines/index.json b/Progmasoft_Core_Cpp_Coding_Guidelines/index.json new file mode 100644 index 0000000..7bb0c28 --- /dev/null +++ b/Progmasoft_Core_Cpp_Coding_Guidelines/index.json @@ -0,0 +1,37 @@ +{ + "title": "Progmasoft Core C++ Coding Guidelines", + "license": "CC-BY-4.0", + "standards": [ + { + "id": "cpp20", + "name": "C++20", + "default": true, + "for": "applications, and libraries used inside one product", + "documents": ["Cpp20/LANGUAGE.md"] + }, + { + "id": "cpp17", + "name": "C++17", + "default": false, + "for": "libraries meant for other people's projects", + "documents": ["Cpp17/LANGUAGE.md"] + } + ], + "documents": [ + { "id": "principles", "chapter": 1, "title": "Principles", "path": "Common/PRINCIPLES.md", "standards": ["cpp17", "cpp20"] }, + { "id": "formatting", "chapter": 2, "title": "Formatting", "path": "Common/FORMATTING.md", "standards": ["cpp17", "cpp20"] }, + { "id": "naming", "chapter": 3, "title": "Naming", "path": "Common/NAMING.md", "standards": ["cpp17", "cpp20"] }, + { "id": "files-and-headers", "chapter": 4, "title": "Files and headers", "path": "Common/FILES-AND-HEADERS.md", "standards": ["cpp17", "cpp20"] }, + { "id": "language", "chapter": 5, "title": "Language", "path": "Common/LANGUAGE.md", "standards": ["cpp17", "cpp20"] }, + { "id": "types-and-classes", "chapter": 6, "title": "Types and classes", "path": "Common/TYPES-AND-CLASSES.md", "standards": ["cpp17", "cpp20"] }, + { "id": "errors", "chapter": 7, "title": "Errors", "path": "Common/ERRORS.md", "standards": ["cpp17", "cpp20"] }, + { "id": "ownership-and-memory", "chapter": 8, "title": "Ownership and memory", "path": "Common/OWNERSHIP-AND-MEMORY.md", "standards": ["cpp17", "cpp20"] }, + { "id": "comments-and-documentation", "chapter": 9, "title": "Comments and documentation", "path": "Common/COMMENTS-AND-DOCUMENTATION.md", "standards": ["cpp17", "cpp20"] }, + { "id": "c-interfaces", "chapter": 10, "title": "C interfaces", "path": "Common/C-INTERFACES.md", "standards": ["cpp17", "cpp20"] }, + { "id": "concurrency", "chapter": 11, "title": "Concurrency", "path": "Common/CONCURRENCY.md", "standards": ["cpp17", "cpp20"] }, + { "id": "testing", "chapter": 12, "title": "Testing", "path": "Common/TESTING.md", "standards": ["cpp17", "cpp20"] }, + { "id": "tooling", "chapter": 13, "title": "Tooling", "path": "Common/TOOLING.md", "standards": ["cpp17", "cpp20"] }, + { "id": "cpp20", "title": "C++20", "path": "Cpp20/LANGUAGE.md", "standards": ["cpp20"] }, + { "id": "cpp17", "title": "C++17", "path": "Cpp17/LANGUAGE.md", "standards": ["cpp17"] } + ] +} @@ -23,27 +23,38 @@ left to the author. | Chapter | Covers | | --- | --- | -| [1. Principles](Guide/PRINCIPLES.md) | what the guide optimizes for, and how to decide a case it does not cover | -| [2. Formatting](Guide/FORMATTING.md) | the `.clang-format` profile, and what formatting leaves to the author | -| [3. Naming](Guide/NAMING.md) | types, functions, variables, constants, namespaces, files, macros | -| [4. Files and headers](Guide/FILES-AND-HEADERS.md) | extensions, `#pragma once`, includes, file size, the order inside a file | -| [5. Language](Guide/LANGUAGE.md) | Language versions and features: `auto`, initialization, `const`, casts, integers, templates, lambdas | -| [6. Types and classes](Guide/TYPES-AND-CLASSES.md) | structs and classes, special members, inheritance, operators, enums | -| [7. Errors](Guide/ERRORS.md) | exception-free code, `Expected` and `Error`, invariants, fatal defects | -| [8. Ownership and memory](Guide/OWNERSHIP-AND-MEMORY.md) | RAII, smart pointers, views and their lifetimes, containers | -| [9. Comments and documentation](Guide/COMMENTS-AND-DOCUMENTATION.md) | Doxygen, what a comment is for, TODOs | -| [10. C interfaces](Guide/C-INTERFACES.md) | C11 headers, `extern "C"`, prefixes, ownership across the boundary | -| [11. Concurrency](Guide/CONCURRENCY.md) | threads, locks, atomics, thread-safety annotations | -| [12. Testing](Guide/TESTING.md) | unit tests, fuzzing, sanitizers, benchmarks | -| [13. Tooling](Guide/TOOLING.md) | compiler flags, the `.clang-tidy` profile, CI checks | +| [1. Principles](Progmasoft_Core_Cpp_Coding_Guidelines/Common/PRINCIPLES.md) | what the guide optimizes for, and how to decide a case it does not cover | +| [2. Formatting](Progmasoft_Core_Cpp_Coding_Guidelines/Common/FORMATTING.md) | the `.clang-format` profile, and what formatting leaves to the author | +| [3. Naming](Progmasoft_Core_Cpp_Coding_Guidelines/Common/NAMING.md) | types, functions, variables, constants, namespaces, files, macros | +| [4. Files and headers](Progmasoft_Core_Cpp_Coding_Guidelines/Common/FILES-AND-HEADERS.md) | extensions, `#pragma once`, includes, file size, the order inside a file | +| [5. Language](Progmasoft_Core_Cpp_Coding_Guidelines/Common/LANGUAGE.md) | Language versions and features: `auto`, initialization, `const`, casts, integers, templates, lambdas | +| [6. Types and classes](Progmasoft_Core_Cpp_Coding_Guidelines/Common/TYPES-AND-CLASSES.md) | structs and classes, special members, inheritance, operators, enums | +| [7. Errors](Progmasoft_Core_Cpp_Coding_Guidelines/Common/ERRORS.md) | exception-free code, `Expected` and `Error`, invariants, fatal defects | +| [8. Ownership and memory](Progmasoft_Core_Cpp_Coding_Guidelines/Common/OWNERSHIP-AND-MEMORY.md) | RAII, smart pointers, views and their lifetimes, containers | +| [9. Comments and documentation](Progmasoft_Core_Cpp_Coding_Guidelines/Common/COMMENTS-AND-DOCUMENTATION.md) | Doxygen, what a comment is for, TODOs | +| [10. C interfaces](Progmasoft_Core_Cpp_Coding_Guidelines/Common/C-INTERFACES.md) | C11 headers, `extern "C"`, prefixes, ownership across the boundary | +| [11. Concurrency](Progmasoft_Core_Cpp_Coding_Guidelines/Common/CONCURRENCY.md) | threads, locks, atomics, thread-safety annotations | +| [12. Testing](Progmasoft_Core_Cpp_Coding_Guidelines/Common/TESTING.md) | unit tests, fuzzing, sanitizers, benchmarks | +| [13. Tooling](Progmasoft_Core_Cpp_Coding_Guidelines/Common/TOOLING.md) | compiler flags, the `.clang-tidy` profile, CI checks | + +Each version has a page of its own with the rules that use its features: + +| Version | Covers | +| --- | --- | +| [C++20](Progmasoft_Core_Cpp_Coding_Guidelines/Cpp20/LANGUAGE.md) | the default: concepts, designated initializers, defaulted comparisons, ranges, `std::span`, `std::format` | +| [C++17](Progmasoft_Core_Cpp_Coding_Guidelines/Cpp17/LANGUAGE.md) | for libraries used by other projects: the C++17 form of each C++20 rule, and the checks for later standards | + +The guide is in `Progmasoft_Core_Cpp_Coding_Guidelines/`: `Common/` holds what applies to both versions, `Cpp20/` and `Cpp17/` what +applies to one. [`index.json`](Progmasoft_Core_Cpp_Coding_Guidelines/index.json) lists every document with its chapter and the versions it +applies to, for tools that show or check the guide. [Adopting the guide](ADOPTING.md) explains how a project points to this guide, which files it copies and how it records a deviation. [Contributing](CONTRIBUTING.md) |