Skip to content

Commit

Permalink
Merge pull request #492 from manshreck/master
Browse files Browse the repository at this point in the history
Export of internal doc changes to C++ Tips:
  • Loading branch information
manshreck authored Nov 7, 2024
2 parents be41df8 + 4393c31 commit d5de2e4
Show file tree
Hide file tree
Showing 4 changed files with 650 additions and 5 deletions.
8 changes: 3 additions & 5 deletions _posts/2024-03-21-totw-224.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,9 @@ provably safe. The `at()` method is a bad approximation of this solution.
Instead, we encourage users to stick with `operator[]` and reduce exposure to UB
by other means, including:

* If your project can afford it, we recommend enabling bounds check in
production using
[HARDENING_ENABLE_SAFE_LIBCXX](http://go/cc_hardened_binary). Current
measurements suggest the [macrobenchmark](https://abseil.io/fast/39) cost of
this hardening is just barely statistically significant.
* If your project can afford it, we recommend also enabling bounds check in
production in other libraries where available.

* If you run your code with [ASAN][asan] you'll *also* get diagnostics if you
access an element out of range.

Expand Down
94 changes: 94 additions & 0 deletions _posts/2024-09-30-totw-231.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
---
title: "Tip of the Week #231: Between Here and There: Some Minor Overlooked Algorithms"
layout: tips
sidenav: side-nav-tips.html
published: true
permalink: tips/231
type: markdown
order: "231"
---

Originally posted as TotW #231 on March 7, 2024

*By [James Dennett](mailto:[email protected])*

Updated 2024-09-30

Quicklink: [abseil.io/tips/231](https://abseil.io/tips/231)


## Overview

In recent C++ versions the Standard Library has added a few functions whose sole
job is to supply some (specific!) point somewhere between two other points `x`
and `y`: [`std::clamp`](https://en.cppreference.com/w/cpp/algorithm/clamp) (from
C++17), and
[`std::midpoint`](https://en.cppreference.com/w/cpp/numeric/midpoint) and
[`std::lerp`](https://en.cppreference.com/w/cpp/numeric/lerp) (from C++20).

Adding these function templates to the Standard Library serves two main
purposes. First, it establishes common terminology (vocabulary) for these
operations, that is likely to be widely recognized. Second, and particularly in
the case of `std::midpoint` and `std::lerp`, it ensures the availability of high
quality implementations that
[avoid common pitfalls](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0811r3.html).

All of these operations are `constexpr`, meaning that they can be used both at
runtime and at compile time. The types that can be passed to them depend on the
specific operation; they all support floating point types, and `std::midpoint`
and `std::clamp` offer additional flexibility. Read on for the details.

### <code>std::clamp</code>

`std::clamp(x, min, max)` “clamps” x to the range [`min`, `max`]. More
explicitly, if `x` is already in the range `min` to `max` (inclusive) then
`std::clamp(x, min, max)` returns `x`, and for `x` outside of that range
`std::clamp` returns whichever of `min` or `max` is closest to `x`. This is
equivalent to `std::max(std::min(x, max), min)` except for being a more direct
way to express the intent (and much less of a puzzle for readers).

> Warning: While `std::clamp` returns a *reference*, code depending on that is
> subtle and unusual, and would warrant a comment to alert readers. It is easy
> to accidentally create a dangling reference by passing a temporary to
> `std::clamp` and binding the result to a temporary:
>
> <pre class="prettyprint lang-cpp bad-code">
> // `std::clamp(1, 3, 4)` returns a reference to a temporary int initialized
> // from `3`, which must not be used beyond the lifetime of the temporary.
> // See [Tip #101](/tips/101).
> const int& dangling = std::clamp(1, 3, 4);
> </pre>
`std::clamp` works for any type that can be compared with `<` (or with a
user-supplied comparator passed to `std::clamp(x, min, max, cmp`).

### <code>std::midpoint</code>

There are no surprises in what `std::midpoint` does: `std::midpoint(x, y)`
returns a point halfway between `x` and `y` (rounding towards `x` when `x` and
`y` are of an integral type).

`std::midpoint(x, y)` works for values `x`, `y` of any floating point or
integral type (not including `bool`). As a bonus, `std::midpoint(p, q)` also
works for pointers `p`, `q` into an array.

### <code>std::lerp</code>

The `lerp` in `std::lerp` is short for “linear interpolation”, and `std::lerp(x,
y, t)` returns a value some fraction `t` of the way from `x` to `y`. For
example, `std::lerp(x, y, 0)` returns `x`, `std::lerp(x, y, 1)` returns y, and
`std::lerp(x, y, 0.5)` can be simplified to `std::midpoint(x, y)`.

Note: In spite of the name, `std::lerp` can also be used for extrapolation if we
pass a value of `t` outside of the range `[0, 1]`. For example, `std::lerp(100,
101, -2)` evaluates to 98, and `std::lerp(100, 101, +2)` is 102.

`std::lerp` works on floating point types.

## Recommendations

1. One of the main benefits of these library functions is that they provide a
common vocabulary. As always, prefer to use these standard facilities
instead of writing them from scratch.
1. Prefer `std::midpoint(x, y)` over `std::lerp(x, y, 0.5)` when applicable.
1. Avoid declaring a reference to the result of `std::clamp`.
220 changes: 220 additions & 0 deletions _posts/2024-09-30-totw-232.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
---
title: "Tip of the Week #232: When to Use <code>auto</code> for Variable Declarations"
layout: tips
sidenav: side-nav-tips.html
published: true
permalink: tips/232
type: markdown
order: "232"
---

Originally posted as TotW #232 on June 20, 2024

*By [Kenji Inoue](mailto:[email protected]) and Michael Diamond, Google Engineer*

Updated 2024-09-30

Quicklink: [abseil.io/tips/232](https://abseil.io/tips/232)


The style guide says in the
[Type Deduction (including auto)](https://google.github.io/styleguide/cppguide.html#Type_deduction)
section:

> Use type deduction only if it makes the code clearer to readers who aren't
> familiar with the project, or if it makes the code safer. Do not use it merely
> to avoid the inconvenience of writing an explicit type.
Ironically, overuse of `auto` often leads to code becoming less clear. Over
time, however, several patterns have emerged where using `auto` can improve code
clarity and safety, such as:

* In situations where specifying the type correctly can be difficult and
specifying the wrong type can lead to performance or correctness issues,
e.g., range-based `for` loops over a map.
* In situations where the type information is truly redundant and
specification of the full type is distracting, e.g., commonly-used templated
factory functions and some iterator uses.
* In generic code where the type itself is not important as long as it is
syntactically correct.

We'll discuss each of those cases below, with an eye toward clarifying the cases
in which `auto` makes code safer or clearer.

## Range-Based For Loops Over a Map

The following code has a problem that each element in the map is unintentionally
copied:

<pre class="prettyprint lang-cpp bad-code">
absl::flat_hash_map&lt;std::string, DogBreed&gt; dog_breeds_by_name = ...;
// `name_and_breed` is copy-constructed for each element of the map.
for (const std::pair&lt;std::string, DogBreed&gt;& name_and_breed :
dog_breeds_by_name) {
...
}
</pre>

The unintended copy happens because the `value_type` of associative containers
is `std::pair<const Key, Value>` and `std::pair` allows implicit conversions
between pair objects if their underlying types can be implicitly converted.
Because `std::pair::first_type` here is `std::string` and the map entry here has
`std::pair::first_type` of `const std::string`, the pairs are not the same type
and an implicit conversion occurs, copying the contents of the pair despite
`name_and_breed` being declared as a reference.

Using `auto`, possibly in conjunction with structured bindings
([Tip #169](/tips/169)), can make the code safer and more performant:

<pre class="prettyprint lang-cpp code">
absl::flat_hash_map&lt;std::string, DogBreed&gt; dog_breeds_by_name = ...;

// `auto` with structured bindings - if the element types are clear from local
// context.
for (const auto& [name, breed] : dog_breeds_by_name) {
...
}
</pre>

Sometimes, the element types are not obvious from local context. In that case,
you can do this:

<pre class="prettyprint lang-cpp code">
// `auto` without structured bindings - allows specifying the element types.
for (const auto& name_and_breed : dog_breeds_by_name) {
const std::string& name = name_and_breed.first;
const DogBreed& breed = name_and_breed.second;
...
}
</pre>

## Iterators

The names of iterator types are verbose and often provide redundant type
information when the type of the container is visible nearby.

Here is an example code snippet that assigns an iterator to a local variable.

<pre class="prettyprint lang-cpp code">
std::vector&lt;std::string&gt; names = ...;
std::vector&lt;std::string&gt;::iterator name_it = names.begin();
while (name_it != names.end()) {
...
}
</pre>

All containers expose `begin()` and `end()` functions which return iterators,
and these iterators have type `ContainerType::iterator` or
`ContainerType::const_iterator`.

When the type of the container is visible nearby, calling out these types would
only have a small benefit of differentiating `iterator` and `const_iterator`
because the container type part (e.g., `std::vector<std::string>`) is the same
as that of the container. In this case, we can use `auto` to remove redundancy
without hiding helpful information:

<pre class="prettyprint lang-cpp code">
std::vector&lt;std::string&gt; names = ...;
auto name_it = names.begin();
while (name_it != names.end()) {
...
}
</pre>

When the container type is not visible locally, prefer to spell out the full
iterator type or element type:

<pre class="prettyprint lang-cpp code">
std::vector&lt;std::string&gt;::iterator name_it = names_.begin();
while (name_it != names_.end()) {
...
}
</pre>

<pre class="prettyprint lang-cpp code">
auto name_it = names_.begin();
while (name_it != names_.end()) {
const std::string& name = *name_it;
...
}
</pre>

## <code>std::make_unique</code> and Other Google-wide Factory Functions

In the following code snippet, `std::make_unique` and
`proto2::MakeArenaSafeUnique` specify the types to be instantiated.

<pre class="prettyprint lang-cpp code">
std::unique_ptr&lt;MyFavoriteType&gt; my_type =
std::make_unique&lt;MyFavoriteType&gt;(...);

proto2::ArenaSafeUniquePtr&lt;MyFavoriteProto&gt; my_proto =
proto2::MakeArenaSafeUnique&lt;MyFavoriteProto&gt;(arena);
</pre>

It is widely known throughout Google that `std::make_unique<T>` returns
`std::unique_ptr<T>` and `proto2::MakeArenaSafeUnique<T>` returns
`proto2::ArenaSafeUniquePtr<T>`. In particular, the important part of the
resulting type `T` is specified on the right-hand side (RHS) expression, and it
is company-wide knowledge rather than project-specific knowledge. We can use
`auto` here to remove redundancy without hiding helpful information:

<pre class="prettyprint lang-cpp code">
auto my_type = std::make_unique&lt;MyFavoriteType&gt;(...);

auto my_proto = proto2::MakeArenaSafeUnique&lt;MyFavoriteProto&gt;(arena);
</pre>

## Generic Code

In some circumstances when writing generic code, such as templates or GoogleTest
matchers, the type may be impossible or very difficult to specify (e.g., a type
written with template metaprogramming or `decltype`). In these cases `auto` may
also be appropriate. However, these situations should be rare.

## Otherwise: Avoid Using <code>auto</code>

While it can be tempting to use `auto` in situations where the type is long and
seems obvious to *you*, remember that future readers of the code may not be
familiar with your project and the types it uses
([why](http://go/readability#why)). For example, consider a common pattern of
nested proto access.

<pre class="prettyprint lang-cpp bad-code">
// Of course `breed` has type `const DetailedDomesticCatBreed&`!
const auto& breed = cat.pedigree().detailed_breed();
</pre>

`auto` may also hide basic semantics like constness, whether a type is a
pointer, and whether a copy is being made ([Tip #44](/tips/44)).

<pre class="prettyprint lang-cpp bad-code">
// Did the author mean to make a copy here?
// It is not obvious to all readers that `breed` is not a reference even though
// `detailed_breed()` returns a reference!
auto breed = cat.pedigree().detailed_breed();
</pre>

<pre class="prettyprint lang-cpp code">
// Type and semantics are clear.
const DetailedDomesticCatBreed& breed = cat.pedigree().detailed_breed();
</pre>

## Summary of Recommendations

* Use `auto` when manually writing out a more specific type would incur a high
risk of correctness or performance problems.
* Use `auto` to remove redundancy without hiding helpful information when the
useful type information is visible locally.
* For some generic code where the type is impossible or very difficult to
specify, `auto` may be appropriate; these situations should be rare.
* **Avoid using `auto` in other situations**: while it may make it easier for
you to write the code or allow you to avoid a line-break, it probably makes
the code harder to understand for someone unfamiliar with your project.

## See Also

* https://google.github.io/styleguide/cppguide.html#auto for the authoritative
guidance
* [Tip #4](/tips/4): Tip of the Week #4: Automatic for the People
* [Tip #44](/tips/44): Tip of the Week #44: Qualifying auto
Loading

0 comments on commit d5de2e4

Please sign in to comment.