Skip to content

Commit

Permalink
JCF: add changes to the Style Guide in light of the discussion at thi…
Browse files Browse the repository at this point in the history
…s week's Software Coordination meeting, with a focus on the switch to C++20
  • Loading branch information
John Freeman committed Aug 9, 2024
1 parent a41c508 commit c1c2e34
Showing 1 changed file with 34 additions and 9 deletions.
43 changes: 34 additions & 9 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,7 @@ familiar with the language.

## 1. C++ Version

Currently, code should target C++17, i.e., should not use C++2x
features.
Currently, code should target C++20, i.e., should take advantage of C++20 features (unless otherwise indicated as described in this document) and not use C++23 features.

## 2. Naming Conventions

Expand Down Expand Up @@ -407,6 +406,10 @@ symbols of `Bar.hpp`.

If a header comes from the C++ Standard Library (e.g., `<vector>`, `<cstdlib>`) it should be enclosed in angle brackets. All other headers should be enclosed in quotes.

### 3.7 Modules

Currently, use of C++20 modules is disallowed.

## 4. Scoping


Expand Down Expand Up @@ -748,7 +751,7 @@ interface.

### 6.7 Trailing Return Type Syntax

The only time it's OK to use a trailing return type (when the return type is listed after the function name and the argument list in the declaration; C++11) is when specifying
The only time it's OK to use a trailing return type is when specifying
the return type of a lambda expression. In some
cases the compiler is able to deduce a lambda's return type, but not
in all cases.
Expand All @@ -768,6 +771,9 @@ that is pointed to.

- When using raw pointers, prefer `void*` to point to generic memory over a pointer to a specific type (such as char); this is because you can use a `static_cast` instead of a `reinterpret_cast` on `void*` to cast it to a pointer to the desired type. Of course, use of generic memory should be rare and only in low-level code where knowledge of the type really is absent.

### 6.9 Coroutines

Writing coroutines is not _formally_ disallowed. However, be aware that unlike in C++23, in C++20 coroutines require either the writing of a generator (which will require a good deal of expertise and effort) or the use of a generator from a third-party library (which will require consultation with Software Coordination). Ask yourself whether using a coroutine to solve the problem at hand is worth the difficulty.

## 7. Other C++ Features

Expand Down Expand Up @@ -821,8 +827,7 @@ release resources correctly?

Never throw exceptions out of a destructor

Only use `catch(...)` directly inside of `main()`, and then only to clean up
resources before terminating the program
The swallow-all-exceptions construct `catch(...)` should only rarely be used since in general it's better to have the failure behind an unexpected exception become blatantly obvious than hidden due to the exception being swallowed. However, there _are_ scenarios where `catch(...)` is appropriate. One example of this would be to use it directly inside of `main()`, to clean up resources before terminating the program. Another would be in a thread. An uncaught exception escaping an `std::thread` or an `std::jthread` would cause a crash, and if the crash would have such negative consequences that it outweighs the argument against the prohibition, then it's OK to swallow all exceptions.

Catch by const reference, unless you plan to add info to the exception
before rethrowing it, in which case you should a non-const reference.
Expand Down Expand Up @@ -894,6 +899,9 @@ the user-visible value, not any implementation details.
Take care that a given print statement not print so often that it
obscures the output of other equally (or even more) important messages

When constructing a string that contains variables, rather than
streaming them into a `std::stringstream` object, prefer to use
C++20's `std::format` function.

### 7.10 Increment and Decrement

Expand All @@ -913,16 +921,19 @@ in its function signatures. While it's more common for developers to underuse ra
If a class method alters the class instance's physical state but not its logical
state, declare it const and use "mutable" so the compiler allows the physical changes.

`constexpr` is even better than `const`; use it when you can. constexpr is described [below](#Constexpr) .
Compile-time constant initialization tools are even better than `const`; use them when you can as described in the next section.


<a name="Constexpr"></a>

### 7.12 Use of constexpr
### 7.12 Compile-time initialization: constexpr, constinit, and consteval

If a variable or function's return value is fixed at compile time and
you don't see this ever changing, declare it constexpr. I say "don't
see this ever changing" since similar to "const" or "noexcept", changing this later will likely break other people's code.
you don't see this ever changing, use
`constexpr`/`consteval`/`constinit` when possible. The phrase "don't see
this ever changing" is used since similar to "const" or "noexcept",
downgrading (e.g., changing a function from `consteval` to
`constexpr`, or dropping one of these qualifiers entirely) can break other people's code.


### 7.13 Integer Types
Expand Down Expand Up @@ -989,6 +1000,20 @@ While a function template can deduce the type of the argument, making
this explicit will typically make it clearer to both the code's reader
and to the compiler what it is you're trying to do.

### 7.18 Concepts

Use a concept if you believe a set of conditions on a type will be applicable in multiple circumstances. This would be as opposed to if a set of conditions on a type will only apply for a single function of class, in which case a concept will only add additional boilerplate by restating the implicit conditions in the function or class. Prefer the `requires(Concept<T>)` syntax over the `template<Concept T>` syntax. Use pre-existing concepts from the STL when available rather than reinventing the wheel. E.g., rather than writing
```
template<typename T>
concept MyEqualityComparable = requires(T a, T b) {
{ a == b }
};
```
just use the existing `std::equality_comparable` concept from the STL.

### Ranges

The use of ranges is encouraged where it will make code safer and more legible. For example, rather than using the traditional [erase-remove idiom](https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom) to filter elements out of a container, just pipe the elements through `std::view::filter`. Or rather than passing the first and last iterators of a container to `std::sort`, just pass the container itself to `std::ranges::sort`.

## 8. Comments

Expand Down

0 comments on commit c1c2e34

Please sign in to comment.