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?