Meeting 136 (23 Sep 2021)
Powered by RedCircle
September mailing
The September committee mailing is out. This is what caught my attention.
move_only_function
This paper proposes a conservative, move-only equivalent of std::function
. It is intended to be the same as std::function
, with the following differences:
- It is move-only.
- It does not have the const-correctness bug of
std::function
. - It provides support for cv/ref/noexcept qualified function types.
- It does not have the
target_type
and target accessors (direction requested by users and implementors). - Invocation has strong preconditions.
The thing that’s unclear to me is that the paper mentions C++20 as the target. That ship has sailed, so presumably it’s now targeted for C++23 and the authors just forgot to update the paper accordingly.
std::hive
The std::hive
(or a bucket array) paper reaches revision 16! And there are still so many questions for the committee that I fear it’s not going to make it into C++23.
A Plan for C++23 Ranges
When Ranges was merged into C++20, it was knowingly incomplete. While it was based on the implementation experience in range-v3, only a small part of that library was adopted into C++20. <…> But now that the core of Ranges has been included, later has come and we have to figure out what to do for C++23.
Presentation: Plans for P2300 Revision 2
The slides present a plan for the second revision of the ‘final final’ std::execution
paper, P2300. There is a GitHub issue tracker for all the issues related to the proposal, and if you look at it you will see there is no chance of the proposal making in into C++23.
Support for #warning
Apparently the preprocessor macro #warning
that most compilers support is non-standard. This proposal is about fixing that.
The Asio asynchronous model
The C++ networking demigod Christopher Kohlhoff presents a high-level overview of the asynchronous model at the core of the Asio library.
The asynchronous operations in Asio support callbacks, futures (both eager and lazy), fibers, coroutines, and approaches yet to be imagined.
CppBooks
There is a huge list of C++ books on github, in a repo creatively called CppBooks. Most are paid, but some are freely downloadable. A very useful resource.
Using constexpr
std::vector
and std::string
constexpr
started small in C++11 but then, with each Standard revision, improved considerably. In C++20, we can say that there’s a culmination point as you can even usestd::vector
andstd::string
in constant expressions!
In addition to this, in C++20 there are constexpr
algorithms to use with vectors and strings.
For this to work, the C++ Committee had to allow the following changes:
constexpr
destructors;constexpr
dynamic memory allocation;- in-place construction using placement
new
; constexpr
try
/catch
blocks;- new type traits, like
pointer_traits
andchar_traits
.
The main limitation is that newly constructed vectors and strings cannot leave the constexpr
function:
Because vectors and strings use dynamic memory allocations, and currently, compilers don’t support so-called “non-transient” memory allocations. That would mean that the memory is allocated at compile-time but then somehow “passed” into runtime and deallocated. For now, we can use memory allocations in one constexpr context, and all of them must be deallocated before we leave the context/function.
Bungie coding guidelines
Bungie published their C++ coding guidelines. These guidelines are presented in the context of Bungie’s latest blockbuster game, Destiny.
There’s a lot of teamwork and ingenuity that goes into making a game like Destiny. We have talented people across all disciplines working together to make the best game that we can. However, achieving the level of coordination needed to make Destiny isn’t easy.
It’s like giving a bunch of people paintbrushes but only one canvas to share between them and expecting a high-quality portrait at the end. In order to make something that isn’t pure chaos, some ground rules need to be agreed upon. Like deciding on the color palette, what size brushes to use in what situations, or what the heck you’re trying to paint in the first place.
And this is a perfect analogy because, as we all know, Mona Lisa was famously painted by a bunch of guys with really good guidelines.
So, what are these guidelines that allowed Bungie to create a great game that is also completely bug-free? (Just kidding) It’s a mix of formatting rules (auto-formatting is coming) and peculiar requirements, which they enforce with code reviews. The codebase is 5.1 million LoC (MLoC?), and some of it is 20 years old. They can’t justify modernising the entire code base (because management, of course), so the guidelines aim to provide a balance between legacy and modern C++ coding practices. Additionally, they are working on multiple feature and patch branches at the same time, which adds complexity. Of the total 150 engineers, there are more 75 working on C++ code.
At Bungie they use so called razors — guidelines that ‘shave off’ complexity and ‘sharpen focus’. Any new guideline is expected to align with one or more of these ‘razors’ to be adopted.
Favour understandability over time-to-write
snake_case
naming (fair enough)- avoid abbreviations (sure)
- use helpful inline comments (agreed)
Avoid distinction without difference
- use American spelling (okay)
- use postincrement in general usage (now wait a minute — this is not just an arbitrary difference)
*
and&
go next to the variable name instead of the type name (this is just wrong — in C++ it’s part of the type, unless you are using “C with classes”)
Leverage visual consistency
- braces should be on their own line (sure)
- uppercase preprocessor symbols (that’s pretty common)
- no space left of the assignment operator, to distinguish from comparisons:
my_number= 42
vs.my_number == 42
(I’m sorry, but this is just hilariously stupid — nobody does that, it looks terrible and doesn’t make any sense) - leverage pointer operators (
*
/&
/->
) to advertise memory indirection instead of references (another weird guideline)
Favor patterns that make code more robust
- initialize variables at declaration time (good)
- follow const correctness principles for class interfaces (good)
- leverage asserts to validate state (good)
- avoid native arrays and use custom containers (ok)
- single
return
statement at the bottom of the function (wait what? this is bloody stupid and doesn’t make sense in C++ code where exceptions are enabled… oh wait, I bet they don’t use exceptions too; still, a single return statement is a stupid idea which increases indentation levels unnecessarily and makes the code harder to reason about)
Centralize lifetime management
- use engine-specific allocation patterns
- do not allocate memory from the OS directly
- avoid using STL for game code (this is often what game engines do, justified or not; EASTL seems like a good replacement tailored for games)
Oh, and have you noticed there is not a single mention of testing in the article?
This is from Reddit:
Based on some of these “standards”, and from the kinds of bugs I’ve seen playing the game, their code must be a real rat’s nest. I don’t know why they published this, but if I were an engineer looking for a job, this would send me looking elsewhere pretty quick.
Also:
They notably did not specify whether or not they use C++ exceptions, which is like one of the fundamental aspects of a C++ style guide.
And this:
Game developers are so consistently in their own bubble, refusing to pick up any advice from the industry around them. It‘s astounding. The amount of “we do it X way because it‘s fast” without ever having tested the performance is so large. The industry is full of wisdom based on no data and almost religious dedication to reinventing the wheel. Oh and of course terrible programmers. That single return policy is 100% a cover-up for a code base so smelly they have to have air fresheners in the server room.
There are four people in the Coding Guidelines Committee. They use their own experience to come up with new coding guidelines. I’m sure there are stories behind all the guidelines, and some of them must be a real hoot.
Bad practices in industrial projects
The article on the Belay the C++ blog discusses bad practices in C++ projects. The list includes:
- Overly long functions
- Creating classes unnecessarily (fully static classes instead of free functions in a namespace, publicly accessible classes instead of
struct
) - Implementing undefined behaviour (the list of UB is too long)
- Comparing signed and unsigned integers
- Premature optimization
- Premature pessimization
- “Just-in-case” programming (YAGNI - You Ain’t Gonna Need It)
The Reddit thread has some more discussion on the raised topics. The winner is the following comment:
I’m a Python user and I have no clue what I just read but it sounds cool
Regarding classes vs. free functions, the user staletic replies to the author’s point “Why would you use class at all, where a namespace suffices?” with this:
To shut down ADL and not have users of your library “steal” function calls with their functions.
Apparently Bloomberg C++ guidelines prohibit free functions altogether because of this.
Nice but not so well-known features of modern C++
A redditor asks, what are lesser-known features of modern C++. Some of the replies are:
- inheriting constructors
1class Derived : public Base {
2public:
3 using Base::Base;
4};
- structured bindings in
for
loops
1for (const auto& [key, value] : m) { ... }
std::exchange
is a handy utility function for implementing move semantics. It’s particularly useful for simultaneously transferring ownership and invalidating primitive types, wherestd::move
would just copy them #- The alias constructor of
std::shared_ptr
. You can convert astd::shared_ptr<T>
into astd::shared_ptr<U>
for some possibly unrelatedU
. For example,U
could be a member of some classT
#
STL says: reinterpret_pointer_cast()
(the function that performs a reinterpret_cast
on a shared_ptr
while respecting the control block) can be implemented with the aliasing constructor, and is in MSVC’s implementation. The aliasing constructor is more powerful as previously mentioned — given struct Point { int x; int y; int z; };
you can get a std::shared_ptr<int>
to any of its x
, y
, or z
.
reinterpret_pointer_cast()
will let you arbitrarily change the type, but not adjust the pointer’s value.
Use case: For example, you could be implementing a lock free queue, where each node is std::atomic<std::shared_ptr<Node<T>>>
but then for front()
, at()
, etc return std::shared_ptr<T>
created via the aliasing constructor. This way live handles to the T
contained in a Node
prevent the Node
from being freed until all handles are dead, so mutating changes to the queue don’t free references to stored objects out from under the other consumers. #
-
fold expressions in C++17
-
immediately-invoked lambda expression
-
constexpr if
as a replacement for conditional compilation using macros -
std::bit_cast
in C++20 allows to convert similar data types used by different libraries, like some imaginarylibA::point
tolibB::point
if they both have the same size. # -
bit constants:
0b[bits]
is amazing for writing masks, combined with the ’ separator.0b1100'0000 == 0xC0
-
raw string literals:
1auto s = R"json({
2 "key": {
3 "desc": "raw string literals are really useful for this kind of 'string'"
4 }
5})json";
FoundationDB
A seriously impressive open source distributed database from Apple:
FoundationDB is a distributed database designed to handle large volumes of structured data across clusters of commodity servers. It organizes data as an ordered key-value store and employs ACID transactions for all operations.
FoundationDB links:
- Home page
- Code - Apache licence, portable C++, language bindings
- Documentation
- Downloads
- Forums
This was the reaction on HackerNews to Apple open-sourcing FoundationDB after acquiring it:
This is INCREDIBLE news! FoundationDB is the greatest piece of software I’ve ever worked on or used, and an amazing primitive for anybody who’s building distributed systems.
The short version is that FDB is a massively scalable and fast transactional distributed database with some of the best testing and fault-tolerance on earth. It’s in widespread production use at Apple and several other major companies.
But the really interesting part is that it provides an extremely efficient and low-level interface for any other system that needs to scalably store consistent state. At FoundationDB (the company) our initial push was to use this to write multiple different database frontends with different data models and query languages (a SQL database, a document database, etc.) which all stored their data in the same underlying system. A customer could then pick whichever one they wanted, or even pick a bunch of them and only have to worry about operating one distributed stateful thing.
The thread contains more interesting replies from developers and users of FoundationDB. If you are into databases, give it a read.
Library: Neither
A functional implementation of Either in C++14 as a way to handle errors returned from functions. A bit like std::expected
or Boost.Outcome with a functional interface.
GitHub — MIT License, C++14, uses Buckaroo build system, which seems to use TOML for its configuration files.
1// a function that throws, sometimes we can't avoid it...
2auto unsafe = [] {
3 if (true) {
4 throw std::runtime_error("error");
5 }
6 return 1;
7}
8
9// let's lift the exception into the typesystem
10Either<std::exception, int> e = Try<std::exception>(unsafe);
11
12e.left()
13 .map([](auto const& e) {
14 return std::cerr << e.what() << '\n';
15 }); // print error if available
16
17int result = e
18 .leftMap([](auto) { return 42; })
19 // ^ do nothing with exception and map to 42
20 .rightMap([](auto x) { return x * 2; })
21 // ^ do further computation if value available
22 .join()
23 // ^ join both sides of either
24
25ASSERT_TRUE(result == 42);
The motivation section talks about how bad C++ exceptions are (spoiler alert: they aren’t). If you can’t or won’t use exceptions in your code, I suppose you could check this library out.
STL Visualizers on GitHub
The Visual Studio debugger has this neat feature where you can show the data in a custom human-readable format, defining the actual formatting rules in XML files called visualizers. Some 3rd-party libraries also have their own visualizers, often supported by the community (Eigen, Boost). And now Microsoft has open-sourced all the STL visualizers on GitHub, to allow everyone to contribute, so that they stay updated. They announced it on the Visual Studio blog.
Using visualizers has become much simpler in VS2017 and VS2019. Previously you had to put them in a certain directory in your Documents folder, of all places. Now you can include the XML files in a dummy container project in your solution, and the debugger will automagically use them to display the appropriate data. Very neat!
Someone saw a poster in a mall that read:
Lost children will be taught the C programming language