Monday, 15 July 2024

C++ On Sea trip report

 

I went to C++ On Sea again this year, attending the main conference. I spoke about Swarm Optimisation algorithms. I wrote a brief blog post about these a while ago, if you want a quick overview. I also hosted the lightning talks.



On the first evening, we had six speakers and a last minute bonus from Jon Kalb:
  1. Jan Wilmans, A Magical chat about C++ with GPT3+
  2. Anders Schau Knatten, C++ Quiz
  3. Cassio Neri, My favourite UB - Part II Russell's Paradox in C++
  4. Sanket Singh, Unlocking the Power of Advanced C++ Compiler Flags: Boost Performance and Debugging Efficiency
  5. Amir Kirsh, delete [ ]
  6. Mark Williamson, Undoom - reviving a zombie with a time travel debugger
  7. Jon Kalb, Wisdom in Keywords
I was speaking directly beforehand, which was a challenge. People sign up and send me slides (unless they are doing a demo on their laptop), so I couldn't poll emails while I was talking. I had prepared a game of "Coding rockstars" beforehand - or at least had some pictures and ideas.  For example, a picture of Taylor Swift. See what I did there?

On Thursday, (almost) immediately before the speakers' dinner, we had another session of lightning talks, with more speakers this time:
  1. Andrew Drakeford, Variadic Reduction 
  2. Sandor Dargo, Do engineering teams really resemble sports teams?
  3. Roth Michaels, Spelling and Pronunciation in C++
  4. Daniel Jump, Replacing legacy string type
  5. Koen Poppe, Implicit conversion, friend or foe 
  6. Kris Jusiak, Static assert is almost all you need! 
  7. Alex Merry, Slide Core Guidelines
  8. Phil Nash, All the Defaults Are Backwards : Let's Fix That
  9. Ambrus Tóth, HyloDoc: Documentation Compiler for Hylo
Dave Abrahams gave the opening keynote about Hylo, one of many languages investigating safety in C++ and potential improvements. The focus was on value semantics, which he described as the "absence of entanglement".  Dave mentioned https://www.bemanproject.org/  My hand scribbled notes don't have any context, but their website says their aim is to "support the efficient design and adoption of the highest quality C++ Standard libraries."

Princess Bride quote: "That word you keep using, ..." in answer to the question "What does a reference mean?"

Next up, for me, was Jason Turner talking about constexpr. He started simple, and gradually added more details. I will need to relisten to this, because I know he covered a few edge cases I hadn't thought of, like lambda captures. I have noted constexpr doesn't change the lifetime of an object. On the face of it, that's obvious, but it's too easy to think it's some value baked in at compile time.

Next I listened to (some of) Björn Fahller's talk on cache friendly data structures. I was busy piecing together lighting talks, so hid up on the balcony to avoid being disruptive. He was talking through a  data structure with good locality of reference and showed efficient filters and deletes, and I did note down his github link. This says "Elements are kept packed towards the beginning of each column, but can be referenced using stable row_id type." This means tracking what has been deleted. One of many cases where a relatively easy to state problem leads to lots of thinking.

I spoke next, giving a high level overview of many nature inspired "swarm" algorithms, and then went into depth on the Cat Swarm algorithm.  You're right, cats don't swarm, but each swarm algo has agents with a position and velocity. The position is a potential numeric solution to a problem, and the velocity gives agents a trajectory, which changes over time, moving towards better places, for some definition of better. In our case, up mean the cat would be let out of a paper bag, so higher coordinates were more likely. 

After the lightning talks, I wanted to collapse in a heap. A small gang of us went to a Turkish restaurant just down the road, which serves a mix of vegetarian friendly and meaty food, in huge portions. Five stars.  

Thursday was less intense, which allowed me to concentrate on Cassio Neri's dragon talk. The full title was A new dragon in the den: fast conversion from floating-point numbers. Another easy to describe problem - print a numeric value. But how many digits, and how quick can you make it? He introduced a new algorithm called Teju Jagua which is a mythological monster: Cassio took great delight in telling us the tale. He's a great story teller. I'll have to go back slowly through the clever maths he did, but meanwhile here's a picture of the seven headed monster:




Next, I went to Jonathan Müller's talk An (In-)Complete Guide to C++ Object Lifetimes. There were so many C++ features I've either never used, like std::launder, or never heard of like std::destroy_at or std::start_lifetime_as. It's always good to discover new language features but my brain was melting by the end of this talk.

I went to see next, talking about Elevating Precision in C++: A journey below the surface of floating-point. Two floating point talks in one day might seem a bit much, but I've not heard him speak before,  and he asked me to sign a copy of my Learn C++ by Example book earlier, so I went to his talk. He talked about Dekker doubles, which led to an obligatory joke about double decker (buses). He asked if anyone knew what a bfloat was, and I made a fool of myself guessing the 'b' stood for binary - clearly not, on reflection. It's a brain float (bfloat16),   It's used for serious numerical computing, including neural networks, hence the name. Wikipedia tells me

It preserves the approximate dynamic range of 32-bit floating-point numbers by retaining 8 exponent bits, but supports only an 8-bit precision rather than the 24-bit significand of the binary32 format.
Daniela Engert then gave her keynote, called Not getting lost in translations. She was talking about Unicode, and how much you can do at compile time. Of course, constexpr got a mention again. I listened, while being mildly distracted by lightning talk emails, but made no notes. I know she showed some really interesting approaches, but I'll have to listen to the recording. It's nice to just listen and let your brain wander once in a while. 

We then had more lightning talks, being less ambitious for the in-between parts. I wasn't sure what to go for after coding rockstars, so I just asked "What's next?" For example,

1, 2, 3, ?

Yep. 4. Start simple. We went downhill from there though. 

I started Friday with Roth Michaels' How and when to write a template. This was part of the C++ fundamentals track labelled as for beginners. Again, we started simple. For example std::string is a template. Roth then considered various types of templates:
  1. class
  2. function
  3. alias
  4. variable
  5. lambda
As you might imagine, things got complicated, and Roth somehow covered EVERYTHING. I knew almost all the ideas he covered, such as std::visit, template template parameters and a question sparked a brief discussion about splitting templates into two header files, including an impl.h, to avoid all the things in one place. I'd forgotten about doing that. 

Next, I went to see Tristan Brindle talking about Practical Tips for Safer C++. This was also in the C++ fundamentals track, this time aimed at beginner or intermediary level. He talked about trapping or wrapping integer overflow, again starting with some simple slides, asking if code was potentially unsafe. Simple things, like sorting numbers can lead to trouble if your container has NaNs. So he suggested using std::is_lt for sorting. He mentioned his Flux library once or twice - it's worth looking at. One point he made, which cropped up several times at the conference, is constexpr can find UB.

I went to Peter Muldoon's talk, Dependency Injection in C++ : A Practical Guide after lunch. He considered various ways of injecting code
  1. link time
  2. virtual functions and inheritance
  3. templates including concepts
  4. type erasure
  5. null objects or stubs
He rejected the first approach quickly. It gets out of hand too easily. He then showed examples of the other approaches, which were all useful. The essence of his talk was approaches to take to make code testable. 

This led on nicely to the next talk by Steve Love, called Testable by Design. Steve built up an example of a thermostat and heating controller. Again, this started simple, and he showed various ways to chain together code, so the heating would go on or off at various times of day, without using the system time directly. If you do that, you are in danger of having a test that only passes at certain times of day. 

Finally, Klaus Iglberger gave the closing keynote, There is no Silver Bullet. He also talked about design choices, contrasting full on inheritance with templates and finally value-based OO, using type erasure and impls. He mentioned Ivan Čukić's Prog C++ talk, which was the closing keynote at MeetingCpp in 2023. He also contrasted the performance of each approach, but made us promise not to take the numbers too seriously. People can get hung up on performance, but extensibility is important too. The value based OO wasn't as quick as template tricks, but did make it easier to extend the internals or externals more simply.



It was a great conference, and I met new people as well as catching up with friends. Thanks to everyone who was brave enough to give a lightning talk, ask a question in a talk, or willing to attempt to answer a question from a speaker, even if they got it wrong. 


Monday, 8 July 2024

Learn C++ by Example: Chapter 9

I have been sharing some details about my latest book "Learn C++ by Example", and gave an overview of chapter 8 last time. There are 9 chapters, so this is the final blog about the book's contents.




You can buy my book directly here: http://mng.bz/AdAQ - or just go look at the table of contents. You can also buy it from Amazon: https://amzn.to/4dMJ0aG

The final chapter explains parameter packs and uses the std::visit pattern. Since we're on the last chapter, we also get opportunities to practice variants, std::format and ranges. We used a std::variant back in chapter 5, to hold either a Card or a Joker. Chapter 5 noted a variant's definition:

template <class... Types>
class variant;

We learnt that the dots are called a parameter pack, allowing us to use zero or more template arguments. We used a variant with two types, a Card or a Joker, back then, but we could have more than two types. We used std::holds_alternative<Joker> to detect if a card was actually a Joker. With more than a couple of types, this can get clumsy, so this final chapter shows another approach.

We explore how to write variadic templates, i.e. templates with at least one parameter pack, looking at fold expressions. Jonathan Müller's blog has some great examples if you want more details. The three dots in the template head indicate a parameter pack,  and we use three further dots to unpack the parameters, for example

template<typename... Ts>
auto add(const Ts&... tail)
{
    return (... + tail);
}

allows us to call add(1, 2) or add(1, 2, 3). You need to watch out for left or right association, for some operators. 

The mini-project in this chapter is building a slot machine. We build up three "reels" (vectors) of numbers, rather than more traditional fruits or similar for slot machines. In fact, we use the triangle numbers. If you can't remember what they are, read my book or find a good online resource.  Just think of arranging snooker balls in a triangle and counting how many you have: 1, 3, 6, 10, 15, 21, ...

If you consider the last digit of each triangle number, a pattern emerges, and repeats. The last digit will be 0, 1, 3, 5, 6 or 8 and nothing else. 8s and 3s are less likely than the others. If we set up three reels with these numbers and compare the final digits we can give a payout, as a slot machine would. Two matches gets a small payout, and all three gets more. In fact, we give a higher payout for 8s and 3s since they are less likely. Trying to calculate a fair payout was a challenge, so I spared the readers and used an approximation. 

The first game just lets the reels spin. How do we spin the reels? We shuffle the vectors first, so we don't know what's going to happen initially, but after that the reels want to move forward by a random amount. And yes, "That's a rotate".  (Ólafur Waage's blog "Everything is a rotate" will explain that meme if you've not heard it before).

Because the reels stay in order, we can display the previous and next numbers above and below the current line, and allow a hold or nudge something like a real slot machine. By adding three structs, 

#include <variant>
struct Hold {};
struct Nudge {};
struct Spin {};
using options = std::variant<Hold, Nudge, Spin>;

we can extend the game relatively easily.  One approach is using a struct with an overloaded method for each type:

struct RollMethod
{
    void operator()(Hold)
    void operator()(Nudge)
    void operator()(Spin)
};

This is far better than checking std::holds_alternative, for each type. The chapter goes on to explain the Overload pattern and how to use std::visit. We need a different function for each type, or double dispatch. Have an experiment, and see if you can code this up.

The chapter also briefly mentions C++17's execution policies, mutable lambdas and std::views::zip, as well as giving extra practice with algorithms and std::format

So, some questions for you
  1. Can you find the repeating pattern of the final digits of the triangle numbers? (Write code to generate them, and have a look).
  2. Can you rotate a vector by a random amount? (It seems a few people can't remember how to use std::rotate, so practice).
  3. Can you use std::visit for the three different options, hold, nudge or spin? 
I've now shared an overview of each chapter. Have a play around with the mini-projects and get in touch if you can think of other good learning examples. 

 


 

 



Monday, 1 July 2024

Learn C++ by Example: Chapter 8

I have been sharing some details about my latest book "Learn C++ by Example", and gave an overview of chapter 7 last time. There are 9 chapters, so this is the penultimate blog about the book contents.




You can buy my book directly here: http://mng.bz/AdAQ - or just go look at the table of contents. You can also buy it from Amazon: https://amzn.to/4dMJ0aG

Chapter 8 is predominantly about unordered maps and coroutines. The previous chapter used std::map, which has been in C++ for a long time but allowed us to learn several new features anyway. This chapter uses an std::unordered_map for contrast. The unordered containers were introduced in C++11. They use hashes to place elements in buckets, so the std::unordered_map is a hash map. There are many overloads of std::hash but you might need to write one to use the hash map, for example for your own class or std::tuple.

The mini-project in this chapter is a game of matching pennies. Two players secretly pick heads or tails on a coin, and simultaneously show their choice. One player is trying to get a match, and the other tries to avoid this. It's not hard to write code where the computer wins on a match, using random numbers for the computer's turn, giving a basic version of the game. 

Now, Claude E. Shannon wrote a short paper in 1953 called “A Mind-Reading (?) Machine” Yes, that Claude Elwood Shannon - the accomplished unicyclist and founder of information theory. The paper has a question mark in the title because the machine isn't really mind-reading. Shannon tracked whether the penultimate outcome was a win or lose, and if the player then changed from heads to tails (or vice versa) or played the same move and thereby won or lost. The three elements can form a lookup table and we can record whether the next choice was a change or not. If we just store the last two values  (change or not) for a given state, we can perform a lookup and if they match, make a prediction. If they don't match, the computer plays at random. The mind-reading machine is surprisingly effective, though you can outsmart it if you track the same state yourself. 

Coding it up gives the opportunity to practice all kinds of C++ features, as well as think about how to form a hash for the three part key. The first part of the chapter creates a MindReader class template, taking a generator and distribution as template parameters, making the code using randomness easy to test. Armed with a mind reader, we then see how to use this in a coroutine. Doing so makes no difference to the matching pennies game, but is an opportunity to learn about this new, much discussed feature of C++.  

You can pause a coroutine, say after yielding a result, and resume later. CppReference says 
This allows for sequential code that executes asynchronously (e.g. to handle non-blocking I/O without explicit callbacks), and also supports algorithms on lazy-computed infinite sequences and other uses.
Coroutines allow cooperative multitasking for example running some code, then suspending while awaiting a result. You need to write a lot of boilerplate to get a coroutine working, but the compiler will help you remember what to add. Again, the compiler will point out anything you need if you forget something. 

If you see a function with one of the following 
  • co_await
  • co_yield
  • co_return
the function is a coroutine. The return type must then fill in the boilerplate, including how to start and stop, and what to do with a co_await, co_yield or co_return

Ivan Cukic gave a keynote at MeetingCpp in 2023. He mentioned coroutines (and lots of other ways to style C++ code for various use cases and neatness). Go look at the slides: From slide 69, he talks about "C++ error handling, let’s abuse the co_await operator." An usual use of coroutines, but interesting.

So, some questions for you:
  1. Why are the C++11 containers called unordered?
  2. How would you write a hash function for a std::tuple, say of just three elements?
  3. Can you list all the parts a coroutine's return type needs?

Monday, 24 June 2024

Learn C++ by Example: Chapter 7

I have been sharing some details about my latest book "Learn C++ by Example", and gave an overview of chapter 6 last time.



You can buy my book directly here: http://mng.bz/AdAQ - or just go look at the table of contents. You can also buy it from Amazon: https://amzn.to/4dMJ0aG 

Chapter 7 is mainly about std::map and has a short section on loading a file. We build a game of "answer smash" which gives two clues to words that contain common letters at the start of one and end of the other. This means they can be overlapped. For example, a vector is a “sequential container supporting dynamic resizing,” and a torch could be defined as a “lit stick carried in one’s hand,” so smashing together the words vector and torch
[vector]  [torch] ->  [vec][tor][ch] -> vectorch
gives the answer vectorch.

Now, as you  probably know, std::map has been in C++ for a long time. In fact, C++11 introduced an unordered map, so why spend a chapter looking back rather than forwards? A reminder about some basics, like what the operator[] does (hint - it's not const and might do two things) are useful, and we'll explore a std::unordered_map in the next chapter. 

Using a map still helps us learn some new features. Many operations use a std::pair<const Key, T>, which can mean your code has 
it->first 
or 
it->second 
dotted around. Since C++17, we can use structured bindings to bind such pairs directly using more helpful names, for example:
for (const auto & [key, value] : dictionary)

We can bind to std::tuple and more besides. We can bind to arrays and even a structure’s non-static members too. For example, given

struct DataObject { int x{ 0 }; double y{ 1.23 }; };

we can write

DataObject data {};

auto [x, y] = data;

The chapter briefly looks at std::string_view, as a potentially more efficient way to concatenate strings. You need to be careful with lifetimes if you use this though. CppReference says 
It is the programmer's responsibility to ensure that std::string_view does not outlive the pointed-to character array


The chapter also includes a brief overview of big-O (order) notation, to help us think through potential inefficiencies, and this helps as a basis for unordered (hash based) lookup table in the next chapter. 

We also use a std::multimap, to allow duplicate values per key.  This allows us to load a dictionary from a file. There are several free dictionaries on the internet. Take your pick, but be warned my book only handles ASCII - other character sets would need a whole book, or at least a dedicated chapter or two! Loading a file doesn't need much work, once you've got the hang of streams. An input file stream is called std::ifstream, We can open a file, using its name, which may need to be fully pathed:

std::ifstream infile{ filename };

and use the stream in a Boolean context to see whether it is open:

if (infile)

// all good

The stream closes as it goes out of scope, which is sensible. 

To make a proper game, we need some randomness, so use std::sample to sample a few words. The challenging part is picking overlapping words.

So, some questions for you
  1. If we use operator[] for a std::map, say dictionary["word"], what are the two things that might happen?
  2. Can you remember how to find all the values for a given key in a std::multimap? (Clue: look up lower and upper bound)
  3. Have a go at finding words that can overlap a given word. (Maybe find a free dictionary so you can look up the words, and pick one at random to start with). 


Monday, 17 June 2024

Learn C++ by Example: Chapter 6

I have been sharing some details about my latest book "Learn C++ by Example", and gave an overview of the chapter 5 last time.



You can buy my book directly here: http://mng.bz/AdAQ - or just go look at the table of contents. You can also buy it from Amazon: https://amzn.to/4dMJ0aG 

Chapter 6 looks at smart pointers and dynamic polymorphism. We create a "Blob" class which moves forwards or possibly backwards. We end up with different Blob type, giving different possible movements, so can race the blobs.

The chapter starts with an abstract class, so we can write various derived classes for the race. The following might seem like a good starting point, but has several problems:

class Blob 

public:

    virtual void step() = 0;

    virtual int total_steps() const = 0; 

};

Did you spot any? 

Do we want to copy this class? Do we want a default constructor? It's worth being explicit about such things:

Blob() = default;

Blob(Blob const&) = delete;

Blob& operator=(Blob const&) = delete;

More importantly, a base class needs a virtual destructor.

virtual ~Blob() = default;

This has been the case in C++ since we've had classes, so probably isn't a surprise. Being able to say =default rather than {} might be though. C++11 introduced to ability to mark special member functions (and more besides) as defaulted or deleted.

We then look at the special member functions a class can have:

  • default constructor, X()
  • copy constructor, X(const X&)
  • copy assignment, operator = (const X&)
  • move constructor, X(X&&)
  • move assignment, operator = (X&&)
  • destructor, ~X()
Implementing one can block the others, and the table in Howard Hinnant's short blog is very useful if you can't recall what affects what.



The concrete classes move in various ways, for example a simple blob might move a fixed number of steps each time we call the step function. The book doesn't show how to use graphics libraries, so the reader can concentrate on learning newer C++ features, but if we use something like the SFML, we could display the marching blobs:


The book demonstrates how to use the console, with *s to represent a blob moving.

The book shows how to create other blobs using random distributions. This adds a bit more excitement, since you can't be certain which might win.

If we make a vector of blobs we need to use smart pointers, so that we can get the polymorphism we need. We can't make a std::vector<Blog> because blob is abstract. Even if it were not, we would slice derived classes, which is a bad thing.

In order to achieve polymorphism, we use a std::vector<std::unique_ptr<Blob>>. To add blobs, for example a StepperBlob which just marches at a set pace, we emplace a unique_ptr as follows:
blobs.emplace_back(std::make_unique<StepperBlob>());
We can add any other derived types too. 

Smart pointers are so much better than trying to handle raw pointers directly. The vector will call the destructor of each element when it goes out of scope, tidying up for us since a std::unique_ptr deletes the underlying raw pointer for us automatically. 

Some questions for you
1. Do you know which special member functions remain when you add a virtual destructor? (Cheat and look at Howard's table if you're not sure)
2. Can you list all the smart pointers in C++?
3. Which C++ distributions have you used? (If you would like some extra details, let me know and I'll write a short blog about these too).

Monday, 10 June 2024

Learn C++ by example: Chapter 5

I started to share some details about my latest book "Learn C++ by Example", and gave overviews of the first few chapters previously.



You can buy my book directly here: http://mng.bz/AdAQ - or just go look at the table of contents. You can also buy it from Amazon: https://amzn.to/4dMJ0aG

Chapter 5 is about arrays and objects. We create a card type and make a deck of cards using std::array. We use this to play a game of higher/lower. Simple, right? Yes, but an opportunity to learn several new C++  features.

 A card needs a suit and a value. We could use an int for each, but we would end up with a constructor taking two ints. Would we always remember which was which? If we use a scoped enum for the suit, we have a type, so the compiler will tell us if we make mistakes. We use the word class to make our new strongly typed enum:

enum class Suit {

    Hearts,

    Diamonds,

    Clubs,

    Spades

};

The chapter starts using this Suit and an int, but then introduces a class called FaceValue for the value. We use these in a Card class, and can make a constructor taking two different types:

Card(FaceValue value, Suit suit):

so we don't need to concentrate on which is which. We also look at non-static data members initialisation, allowing us to give default values to the Card's data members in place:

FaceValue value_{1};

Suit suit_{};

A deck of cards can then be an array:

std::array<Card, 52> deck;

The cards will then all use the default values, so we write a function to make 52 different cards. We can cycle around the suits using an initialiser list:

for (auto suit :

    {Suit::Hearts, Suit::Diamonds, Suit::Clubs, Suit::Spades})

and provide 13 of each with FaceValues from 1 to 13. The book also discusses various other approaches. 

In order to play higher/lower, we need to be able to compare cards, so we learn about the so-called spaceship operator or three-way comparison. Rather than hand writing operator<, or > or == for our Card type, we add

auto operator<=>(const FaceValue&) const = default;

to the FaceValue and 

auto operator<=>(const Card&) = default;

to the Card itself. The return type is auto, because types can have strong, weak or partial ordering. This would pull us into some rather cool mathematics, but at a high level, ordering numbers is easy enough, but some types, like a point in space are more difficult to agree on. Is (1, 3) greater then (4, 2) or not? The spaceship operator automagically compares all sub-objects recursively, which is why the contained FaceValue also needs a definition. This means a FaceValue of one is lowest. If one means an  ace, aces are low rather than high. You can have fun experimenting to make aces high instead if you want. 

If we shuffle the cards,  

std::random_device rd;

std::mt19937 gen{ rd() };

std::ranges::shuffle(deck, gen);

we can play the game. 

The book then shows how to add Jokers, by making a new struct:

struct Joker };

Nice and simple. We can then use a std::variant, containing a Joker or  Card

std::variant<Card, Joker>

and play the game again, letting a Joker give a free turn.

The book walks through details and alternatives for each of these steps. Here's some questions to think about

  1. Do you ever just use an int or other inbuilt type then get confused as to which parameter is which, or does you always use strong types?
  2. How would you generate the 52 cards? 
  3. Have a think about how to display the cards. When C++ has reflection, displaying the enum as a string will be easier. For now, we have to write some code ourselves.



Monday, 3 June 2024

Learn C++ by Example: chapter 4

I started to share some details about my latest book "Learn C++ by Example", and gave an overview of the chapter three last time last time.



You can buy my book directly here: http://mng.bz/AdAQ - or just go look at the table of contents. You can also buy it from Amazon: https://amzn.to/4dMJ0aG

Chapter 4 explores time points and durations and introduces literal suffixes. As with previous chapters, several other useful C++ features crop up too. We start by finding out the current date and time:

std::chrono::time_point now = std::chrono::system_clock::now();

This uses the chrono library introduced in C++11, and later in the chapter we use the calendrical types introduced in C++20. If you haven't met either of these features before, they feel very different to, say, using C's time_t. The chapter shows how to write countdowns to various dates, for example the end of a specific year or the end of the current year. 

For example, we can hard code a specific date, using C++20's year, month day, and transform it to time_point using sys_days:

auto new_years_eve = std::chrono::year_month_day(
    std::chrono::year(2022),
    std::chrono::month(12),
    std::chrono::day(31)
);
auto event = std::chrono::sys_days(new_years_eve);

If we subtract the current time, we obtain a duration:

std::chrono::duration dur = event - now;

That gives us our first countdown, but there are details behind the times, dates and durations. If we print out the duration, the result varies between compilers. Visual Studio 2022 gave

69579189669221[1/10000000]s

while clang and gcc both gives the result in "ns". This gives us a hint that durations are in units.

We can change hours to milliseconds with an implicit cast:

std::chrono::milliseconds ms = std::chrono::hours(2);

but going back to hours from a more fine grained duration requires an explicit cast:

auto two_hours_from_ms = duration_cast<hours>(ms);

The library tends to give compile errors if you get something wrong, which is a good thing. 

In order to fully understand how the duration casts work, we need to delve into std::ratio, which is used to define seconds, minutes and so on.

The chapter gives a brief introduction to requirements and concepts too, which is a big topic. At a high level, they provide constraints for template arguments, picking appropriate overloads or giving clearer compiler error messages. 

The chapter also covers literal suffixes, such as "Hello, world!"s to make a std::string rather than a char *. Chrono also provides operator""s, taking a number, so we can write 59s for 59 seconds. We could say seconds{59} instead, but in conjunction with the overloaded operator /, we can write dates clearly in code. For example, 

auto new_years_eve = 2022y / December / 31;

is hopefully obvious, even if you don't know C++ or the how the chrono library works.


So, some questions/challenges for you. 
  • Try to write various dates and times and find the durations between them.
  • How many seconds are there in a year? (Yes, a bit of a trick question!)
  • Write your own duration and corresponding literal operator.