Search

SE Radio 617: Frances Buontempo on Modern C++

Frances Buontempo, author of the new book Learn C++ by Example, discusses the C++ programming language, a widely used general-purpose programming language. Host Philip Winston speaks with Buontempo about where C++ fits into the landscape of existing programming languages and how recent C++ standards have changed things. They talk about specific language features such as lambdas, templates, concurrency, ranges, concepts along with tips for learning and using C++.



Show Notes

Related Episodes

Other References

Frances Buontempo

C++

ACCU

C++ Ranges


Transcript

Transcript brought to you by IEEE Software magazine and IEEE Computer Society. This transcript was automatically generated. To suggest improvements in the text, please contact [email protected] and include the episode number.

Philip Winston 00:00:18 Welcome to Software Engineering Radio. My guest today is Frances Buontempo. She has been programming professionally since the 1990s in fields such as banking and quantitative finance. She’s the editor of ACCU’s Overload Magazine and has a PhD in data mining and machine learning. Her new book, which we’ll be discussing today is Learn C++ by Example. She previously published a book titled Genetic Algorithms and Machine Learning for Programmers. Welcome to the show, Frances.

Frances Buontempo 00:00:51 Thank you.

Philip Winston 00:00:51 In this episode, we’re going to discuss the C++ programming language. Most people think of C++ as an object-oriented language, but it’s perhaps more accurately called a multi-paradigm language. What paradigms does C++ enable or support?

Frances Buontempo 00:01:07 Loads and loads of things. I mean, I guess historically C++ started as what was referred to as C with classes. So a lot of people do think of C++ being as object-oriented and you can use it like that. But a lot of people nowadays, particularly with some things like ranges and some of the new features take on board a more functional approach to things. So that gives you a whole different perspective. And then of course at some point during the history of C++ evolution, we introduced templates and somebody observed that you could do a surprising amount of things that compile time with templates, you end up with crazy template meta programming things which are possibly a bit niche can be useful sometimes and they’re just kind of plain fun. But you can do OO, you can do straight up procedural stuff and end up with things that look like C instead, you can do a much more functional approach, which I personally quite like.

Philip Winston 00:02:10 C++ is a compiled language, and its execution speed is one of its trademarks. What are some of the pros and cons of being a compiled language?

Frances Buontempo 00:02:19 The compiler can find mistakes for you if you use the type system well, but that’s something you need to learn to do. It means that you are compiling two specific hardware so various optimizations can kick in and that can give you the speed if you’ve written your code well. I mean you can write bad slow code in any language, but because you are compiling to a specific target, you don’t have levels of indirection going on. So that speeds things up.

Philip Winston 00:02:51 So execution speed, and you’ve already mentioned sort of the type system and touched on generic programming. I think those are all strengths that you hear a lot about. What would you say some of the, the weaknesses might be of C++ or maybe I should say like the challenges of using it in a production environment or learning it?

Frances Buontempo 00:03:11 The usual quote at this point is from Spiderman with great power comes great responsibility. So you are in control and that means that you can do all kinds of silly things, you can get things wrong. Lots of people at the moment are talking about safety. There’s a big discussion going on around that. And I guess some languages like Rust are trying to challenge C++ because you can overrun bounds if you are not careful. If you rely on things like the vector type and various other parts of the language, you can avoid a lot of the problems with overrunning bounds and dereferencing invalid memory addresses and so on. But you can completely mess things up really easily. Some of my early programming was in using embedded devices, one of which was a barcode scanner and I overrun a buffer and it left the laser switched on, which was nearly ended up pointing straight at my face. So yes, you can break all kinds of things because you’ve got the power, and you can see what’s going on. I guess an analogy might be like if you’re driving a car, if you’ve got a manual car or a stick, however you word that you’ve got more control over what you’re doing versus sitting in a self-driving car that does what it thinks. And yeah, you can break things, but you can do things better as well. You’ve just got more power and more choice.

Philip Winston 00:04:44 Part of the safety question with C++ is that it’s a super set of C so you can kind of program in C style without using say smart pointers or standard array. Is that something that comes up when people are learning to program that you have to guide them towards these C++ features?

Frances Buontempo 00:05:07 I guess it depends where people are coming from to start with. I’ve seen so many training materials out there that do go straight into pointers and allocating manually with new and then having to remember to do delete because I think a lot of people are sort of age have come to this from maybe a C background and have learn the new things as they’ve cropped up. But there are just one or two people who’ve managed to go, look, let’s just use the sensible safer things, find out about the danger later on if you really need to. I think if somebody wants to pick up C++ now if you find a book or some teaching materials that have got chapters and chapters on pointers and pointer arithmetic, probably don’t read that first. Find something that tells you about vectors and standard array and doesn’t have the word new or delete anywhere in sight. Stick to the safe, sensible things.

Philip Winston 00:06:06 Yes, and I think your book definitely takes that path of trying to teach the more modern features. One sort of developer experience thing that I don’t think was discussed in the book is a package manager, so many languages I’ve used today, you have a centralized package system and you can sort of just type the name of any dependency and it gets kind of included. C++ to my knowledge, doesn’t have a package manager. What’s the Ö

Frances Buontempo 00:06:35 Correct, it doesn’t have a package manager. There are several rival package managers out there. There’s some Microsoft offerings and I won’t be able to list all of them, but there’s a V package I think Conan a lot of people have talked about, and I should probably be able to think about five or six others, but my mind is blanked at this point. There are lots, there’s no one standard thing people are using. Another thing I’ve not gone into in my book is modules, which gives you another way of packaging things up. I think this is going to evolve. I think we’re trying to do that as a community, but we haven’t settled on one standard way of doing things. Whether we ever do or not, we shall see, but there are package managers out there, there are just lots of them and some things are packaged in one place or another place. So yeah, you’ll frequently end up just cloning and building the libraries you need.

Philip Winston 00:07:31 You mentioned community. What sorts of communities tend to embrace C++ today if you’re at a C++ conference, who are the people you’re likely to run into? What industries are they perhaps from?

Frances Buontempo 00:07:45 My background, a lot of my work’s been in investment banking and finance, so you’ll get people working on maths libraries there to do the pricing and the work out values of things and credit risk and that kind of stuff. So definitely things around finance because of the maths under the hood, wanting to do that quickly. And then the games industry as well, they’re now rattle off loads of games, companies who use C++ and then another big area is embedded things, so TV set top boxes, mobile phones, that kind of thing. So yeah, embedded math-sy stuff and games are the main areas.

Philip Winston 00:08:35 How about open-source products? Are there any well known products that are based in C++ that come to mind.

Frances Buontempo 00:08:43 There are lots of background libraries which will be used by other things. A lot of the modern C++ has come from examples coded up in the Boost library, which has been around forever. But again, that’s a library level that’s not aimed at one specific area. And of course there’s lots of different approaches to graphics libraries, there’s several open-source things out there, open graphics libraries and skims over the top of that so you can play around and draw things yourself. Another area which I much about is audio. I know one or two people who working on some audio tools as well. So lots out there.

Philip Winston 00:09:23 I think a good example of that library phenomena is Python dominates machine learning, but at least parts of the libraries you use for that say TensorFlow and PyTorch, at least parts of them are in C++ I think.

Frances Buontempo 00:09:38 Yeah, because Python’s an interpreted language, you can swap out the interpreter and there’s the whole siphon project which gives you a different way of building up code and it’s quite easy to interoperate between Python and C++. There’s several different ways of doing it but that would allow you to speed up certain parts and like you said, a lot of the machine learning stuff under the hood is doing some C++.

Philip Winston 00:10:06 Okay, let’s start getting into talking about the standards a little bit and then we’re going to pivot into sort of the contents of the book, which is kind of touching on these different C++ features in the language itself and also in the standard library. But to start C++ was created by Bjarne Stroustrup, the first version was released in 1984 but the first standard was not until 1998. What happened after 1998 as far as standards and kind of where are we today? How often do new standards come out and how much of the language is changing from standard to standard?

Frances Buontempo 00:10:44 There’s a lot to cover there. High level, like you said, ë98 we got a standard, different compilers were compliant with different parts of it. So that made life really quite difficult to around ë98 and the early 2000, a lot of people were still using separate libraries to do some of the standard things and it took a long time for people to get nearer and nearer to GCC or visual studio tending to have most of the stuff in there. There are still lists of, well this doesn’t do this, and this doesn’t do that, but it gradually got better a more standard. There was then a, I might get the date wrong, I think 2003 we got a kind of update that had some slight improvements and people, well the compiler implementers and tool builders started taking some of that on board. Then it all went really quiet for a while and we geared up to having another new standard which started as C++ OX because we were hoping it might be like 2008, 2009 and then we just slapped an X on the end because C++ is standardized and there’s standard meetings and people vote on things and argue with each other and it can take quite a long while to come to an agreement.

Frances Buontempo 00:12:12 But he ended up being 2011. So C++ 11 was when the next big change happened and that brought on board loads of things, particularly from the Boost Library like smart pointers and things like this to just make it easier and safer and better. And at that point everyone was like, this has taken us so long to get round to let’s try and be more agile. Let’s aim for every three years and maybe the next release. So C++ 14, we’ll just be smaller changes, maybe slight improvements here and there. And we’ll save the big things for C++ 17, then you get C++ 20. So we are, we were aiming for every three years we have stuck with that after that long time coming for C+ 11.

Philip Winston 00:13:03 How about the split between the language which we’re going to start talking about in a second and the standard library, are those sorts of both developed kind of evenly or does the standards tend to emphasize the library?

Frances Buontempo 00:13:18 It’s both kind of in equal measure. If you go to ISOs working group 21, so WG 21 webpage, you can see a list of all the proposals there. I don’t have some stats on hand on the breakdown of where the different areas are. There’s a mixture of tinkering with uses of existing keywords, maybe introducing keywords or changing things, changing how the language works and also adding to the libraries. And under the working group there’re people focused on specific things. So maybe in C++ 26 we’re going to end up with reflection, which is something you can do quite easily in C# or Java, but you can’t in C++ really. And that’s one big new feature that has come in. Now whether that’s at the language level or a library level, we’ll see it’s going to involve some language changes because we need some new syntax for it. So that’s an example of one thing on the horizon. So a mixture of language and library.

Philip Winston 00:14:25 Yeah, I’ve done some C# programming recently and you don’t use reflection at all until you kind of need it and you maybe use it in a small area.

Frances Buontempo 00:14:34 Yeah, I mean you can get out of hand. I’ve worked on some C# libraries that everything was inside out and done by reflection and driven by external files and that can end up being hard to follow. If you’ve got a compelling case then you’ve got a compelling use case and if you use it where it’s sensible, it’s powerful but you can’t do that in C++ at the moment. Watch this space.

Philip Winston 00:15:00 Let’s get into some language features. Now your book is called † Learn C++ by Example . And you have a lot of great examples that are, I feel like they’re just the right size for a book in terms of being able to explain it and understand it. But that does not necessarily work well on a podcast. So we may or may not get into the examples, but let’s at least try to give a flavor of what’s in the book. So talking about keywords and the core language features, there’s a key word auto that I think has been around a while, but it may be still new-ish to some people. Can you say what auto is used for and when is a good time to use it?

Frances Buontempo 00:15:41 So that was introduced way back in C++ 11 loads of people went, oh no because everybody seemed to be putting auto instead of INT or something, you know, why are you doing this? And that turned into an argument like where do you put your braces and things? Is it a good thing or is it a bad thing? But he was introduced for a reason and good reason. We also had lambdas introduced, which are anonymous functions which loads of different programming language is support and you can’t name what type it actually is because each lambda is a different type. You could shovel it into a standard function but then you are copying things and that adds overhead. So in that circumstance, if you say auto, then the compiler knows what type it is and that makes things more efficient. So there was some maybe slightly niche edge cases where it’s definitely the right thing to do.

Philip Winston 00:16:41 How about the other uses with say templates? Is your feeling to use auto whenever you can or do you draw the line somewhere and decide it’s going to obfuscate things?

Frances Buontempo 00:16:53 When you do generics, previously you’d say type name T and then do your function or class to do whatever. You can now say auto instead, either is fine and doesn’t really make much difference. It does allow you to say auto as the return type. If you don’t do that you’ve got to then start digging into DECAL types and similar around expressions and you end up feeling like you’re stabbing yourself in the eye with pointy braces and it’s much easier to just write auto at that point and let the compiler work, it out. Bonus new C++ feature at that point you can constrain things with a concept slightly. So you can say type name or auto and say something is invocable or check see if it’s an integral type or a double type or a pointer or and so on. So you can constrain things and get better clearer error messages if you make a mistake. So don’t just use raw type name or auto at that point. Try and embrace the concepts as well to give yourself a helping hand.

Philip Winston 00:18:01 We’re going to talk about concepts a little bit more, but another feature related to auto is this trailing return type. So traditionally C++ had the return type before the name of the function, which is common in a lot of language, but now there’s an option to, I think you mentioned to have auto be the return type and then have sort of an arrow and that the return type at the end. Yeah. Can you say when would you use that form and what’s the advantage of that?

Frances Buontempo 00:18:32 That was something that was introduced quite early on putting auto at the beginning instead of say INT or double or whatever and then you can do that arrow so minus greater than sign and work out for yourself what’s going to be returned. And it might be the result of adding two things. If I add an INT and a double, well I’ll get the double back or I’ll get the higher precision back. Actually as it stands now, in a lot of circumstances you could just put the auto at the front and not bother with the return type because you might not need to be that specific and the compiler might well work it out for you, but that’s just an interim step on the way there. So if you see auto and then the arrow at the end in some C++ code, that might be a surprise if you’ve not looked for the last 10, 15 years, and that is allowing you to be precise about what’s being returned based on what input parameters you get.

Philip Winston 00:19:28 Okay. Jumping to another topic, how about constant expression, I’m not sure how you pronounce it, but C-O-N-S-T-E-X-P-R. How does it differ from just playing Const, which I think means the variable is not going to be changed, but Constexpr is different?

Frances Buontempo 00:19:47 Yeah, so I think a lot of people say Cons exp because that’s highly pronounceable, that’s one of the downsides of C++ we’ve ended up loads of unpronounceable things. If I declare a variable Const INT X equals 42, the compiler will give me an error if I try and change X at any point during the function. Fair enough. So const is to just stop me from mutating and changing things and that’s useful and that’s as old as the hills. What Constexpr does is it can, it might not, it can be worked out at compile time, which means if you wanted to do some complicated sums or do some trig or something, which maybe takes a while to work out because there might be a few operations going on, if you make it constexpr, it can work out the actual value at compile time. So then you’ve lost some runtime overhead. And I’ve seen people produce examples of some code that’s worked out some quite complicated mathematics and if you look at the assembler that’s generated, it’s just got buff, it just got the number in there without all the function calls and things. So sometimes the compiler will be able to work things out for you and then you’ve just got the value baked into your executable.

Philip Winston 00:21:11 Now if it can’t do that and your Constexpr just calls, makes a call on to the open internet or something. Does the compiler notice that or what happens in that case if it’s not really constant?

Frances Buontempo 00:21:23 Yeah, or if you had some user input or something, the compiler will go, you asked me to try, I’ve tried, pfft tough. And then if you look at the assembly you’ll see it is doing the function calls. So it’s like a request, help me out if you can Compiler, if you can’t, no problems.

Philip Winston 00:21:40 This might be a small one, but what is the uniform initialization syntax? It is where you use braces instead of parentheses, but what’s really the difference?

Frances Buontempo 00:21:53 Right, cool. So that was a neat little thing that was introduced that’s meant to mean that you can just put braces around anything. So my example of INT(x), well I can put INT(X) equals 42, and now I can put curly braces instead around a 42. So what? Who cares? Well if I had a class or a structure as well, I can put curly braces around the parameters to the constructor so we can start being more consistent and people, there’s lots of little edge cases where people got in a knot when they tried to initialize things. If I want to put two numbers in a vector like a naught and a one, if I do curved brackets around it, actually there’s a construction that takes a value and account. Whereas if I put curly braces round a naught and a one, it goes alright, no, no you want to put these two values naught and one into a vector and it’s more intuitive what’s happening there.

Frances Buontempo 00:22:57 It does mean that we tend to initialize everything with curly brackets. There was another annoying edge case that if I put INT(x) curve bracket, curve bracket and a semicolon, what I’ve done there instead of zero initializing the value X I’ve declared a function because the syntax is the same as a function declaration, but if I put the curly braces then that disambiguates. So it makes some things clearer. There are some cases where you can’t really use the curly braces and some surprising edge cases that happen because C++ with great power comes great responsibility. We’ve got rid of some horrible edge cases and introduced some new ones. So you win some, you lose some, but you can if you, you’ll tend to see a lot of modern code now just using the curly braces round initial values for stuff, then it does look more consistent. There are still edge cases to watch out for though.

Philip Winston 00:23:56 That speaks to what we’ve talked about before where you can program in a C style or you can adopt these features when you see another code base or working with another code base. Do you have to kind of assess what era of features it’s using and do you have to be consistent with that or how does that play out?

Frances Buontempo 00:24:17 So many code bases look so different. Some of them you can see the history of how old they are with all the different styles that go on other places will enforce certain coding styles. So when there’ve been updates we go back and we change things. I’m quite chilled. If things are slightly inconsistent I can get over it but I know some people absolutely hate that and they want all the braces to be the same and everything look the same. In some ways it doesn’t matter but if you are missing some opportunities to speed things up or avoid mistakes, then trying to on the side of making your life easier always. But yeah, there’s so many different projects out there and you will see some are old, very and you can’t tell who’s written which bit because they’re enforcing a way of writing things and others are slightly untidy. It depends. It varies.

Philip Winston 00:25:14 Okay, A couple more language ones including, we’ll talk about this idea of a zero cost abstraction and how that’s maintained. But here’s one I wanted to ask about. There has been a feature for a long time in C++ called type def or you can give a long type or any type, you can give it a shorter name or different name. And that’s particularly been useful with templates because templates can have long names, but there’s a new one called, I don’t know how new called the using statement and I think this is preferred. Can you say when you would use using versus typedef or anything about them?

Frances Buontempo 00:25:54 Essentially there’s no need to use typedef anymore. So some people are in fact one guy reviewed one of the chapters in my book was horrified, I even mentioned it, but it’s something that’s familiar if you’ve come to this from a C background or prior to C++ 11. Nowadays just say using where you would’ve said typedef before, using discuss another advantage. So if I’ve got a lookup table, so say a standard map or an unordered map from a specific key to a specific value, I might have a lookup of first name to address or surname to address. I could have a using statement for a lookup and I could say well I’ve got some key auto or typedef key, but it’s always going to an address as the value. So then I’ve got a little family of things that in the code say look up everywhere and I know what look up is but I might be what the key is. So it gives me a family of templates and you couldn’t do that with typedef.

Philip Winston 00:27:00 This is a small one but I noticed it because, I don’t remember seeing it in another language. You can have an ìifî statement that has an initialization portion, semicolon a condition. When would you use that? It wasn’t obvious to me.

Frances Buontempo 00:27:16 Something that happened. I think way back in C++ 98, if I’ve got a for loop that says i runs from zero up to whatever the scope of the variable i, my index leaked outside the for loop on some compilers which could cause all kinds of confusion. You kind of wanted it to be within those braces just in the scope of the for loop. So the if statements you can now do something similar with. So sometimes you kind of want to set something in if so you could set it before there and then check the value afterwards. But then you’ve put it in a wider scope If you put it inside the if statement, it’s just accessible from inside that if block a lot of times in C++ if you keep the scope really small and close, then you are less likely to accidentally change things or make mistakes that small and neat often better. And in other languages too, just having smaller things to look at. So it just constraints the scope of a variable to inside the if tiny little thing but you can avoid mistakes that way quite easily.

Philip Winston 00:28:30 You mentioned lambda already. One notable thing about C++’s lambda is it doesn’t include the keyword lambda or, or really any keyword at all. It seems to me like it’s square brackets with the parameters to the lambda in the square brackets and also it had some features I haven’t seen before as terms of capturing, you can capture even a variable by variable basis. Can you say if that gets used in practice?

Frances Buontempo 00:29:01 So lambda’s the mainstay of some functional programming approaches and if you look at other functional programming, we start talking about closures and then you end up in a world of mathematics and all kinds of things about moments but, whatever. But if we keep it focused here, so yes we’ve got square brackets first, then we’ve got round brackets as you’d have with a normal function declaration. So it might take an INT or string or no parameters, so it might be empty curved braces or some input parameters and then curly braces and the body of the anonymous function or lambda and layer. But inside that anonymous function you might want to use something from the wider context so you can close over that and send in through the square brackets one of the local variables for example. So then that’s captured in there and enclosed in that packaged up in that lambda. So that’s what the square brackets are for are the things I might want to pass by value or by reference into that enclosed closure if that’s a phrase.

Philip Winston 00:30:13 Are there certain types of programming where you feel like lambdas are more useful? Because I’ve seen them a lot in JavaScript because you have all of these handlers and callbacks. And you’re constantly like registering those, does that come up in C++ that kind of use?

Frances Buontempo 00:30:30 Yeah, I mean I guess you’re doing a really similar thing there and you could package something up and send it off to threads or whatever, but even in more straight up use cases, if you’ve got something that you want to happen in an algorithm for example, you want to do some kind of transformation, there’s the algorithm header which brings you all the standard library stuff and there’s a transform function in there. Now I could either send it an instance of a class that’s got a callable member function that does the work which is defined off somewhere else and then when you see the call to transform, you go, oh now it’s making one of these other classes and then you have to look somewhere else to see what that is. But if you put a lambda there instead that means you can see I’m going to transform this container using this function. It’s all just in one line, in one place to look. So it does save you jumping around just following the code and it’s probably more efficient as we aren’t making objects over here as well. It makes it neater to read sometimes if you’ve just got a one-liner that says add two to everything in sight, that’s be a tiny little square bracket, round brackets, curly bracket X plus two, just neat small focus again.

Philip Winston 00:31:48 I think you’re saying that a lot of the algorithms take functions as input and in those cases potentially clear.

Frances Buontempo 00:31:54 Yeah, there are loads of them take unary functions. So we’ll do something with one thing or maybe a predicate that tells you something’s true or false and you can build functions, you can build classes but just a one-shot lambda looks neat.

Philip Winston 00:32:07 I mentioned zero cost abstraction, this is kind of a term that comes up I think when you get into the language design or the standard. Can you tell us what that phrase means and maybe how difficult it is for C++ to maintain that promise of zero cost?

Frances Buontempo 00:32:27 A lot of the focus from the standards committee is trying to ensure that as new features are added, we don’t make things slower or worse. We succeed most of the time, but there’s some edge cases that hasn’t quite worked out and there’s some special cases we’ve built in to try and ensure that for example, things being returned from a function instead of being copied over, just go straight into the return values. So there are lots of places where we special case things to make things more efficient. Bjarne Stroustrup would always uses the phrase “pay for what you use,” which I think we’re talking about the same thing there. Just if we’re going to introduce some of these abstractions and these algorithms instead of for loops, the code that comes out is as efficient and actually sometimes it’s more efficient because the compiler can spot better optimization opportunities. What we’re trying to do is make the code easier for humans to read but keep the same efficiency.

Philip Winston 00:33:34 You mentioned returning from a function. I think that’s my next question in terms of move semantics and perfect forwarding, these are things that I’ve seen Rust people talk about, but most languages don’t have these types of concerns. Is that something you get into as an application writer or is it for libraries? When do you have to start caring about these move semantics?

Frances Buontempo 00:34:01 You can probably get away with it without thinking about it unless you start writing your own classes. In which case, well it’s fundamentally part of a class or your own type. So if you’re doing some basic stuff with some numbers and things, you’ll be all right as soon as you start writing your own class. If you don’t resort to polymorphism then you probably don’t need to think about it. Because by default the right thing will happen. But as soon as you start adding some OO style of programming, then you do need to think carefully about what do I need to be able to copy this? Does it make sense to copy this particular object or not? And then we’ve got the idea of moving as well, which it’s an efficiency. If there is no move then we’ll fall back to the copy instead. But yeah, you will start needing to think about what you need to implement at that point.

Frances Buontempo 00:35:01 That’s always the case. It’s very different for you from some other languages. I guess if you look at Java and .NET, because they use references everywhere, you don’t need to move stuff. The objects somewhere on the heat that’s garbage collected for you. But again, C++ great power, great responsibility moves, and perfect forwarding give you the opportunity to go, ìactually I don’t need to copy everything over and then get rid of the old one, I can just use the old oneî. So it allows you to re-work, doing things and find efficient ways to do things can get really complicated but you can keep it simple if you try and design your classes well and try and do things by default where possible, lots of the basic building blocks like the smart pointers do the right thing for you. So if you compose your structures out of sensible things then you’ll make your life easier, and your code would be quicker.

Philip Winston 00:35:59 One more question on this move versus copy and then we’ll get into templates more and then maybe back to some smaller concepts. One manifestation of this is maybe that vector has two methods and place back and pushback. Can you say when I would use either of those?

Frances Buontempo 00:36:20 So the pushback was always there since ë98. That will take an object which might just be something small like an INT or it might be a bigger class with lots of stuff going in. So it takes a whole object and then it needs to copy that into space in memory so it’s inside the vector. If I do in place back, that can then be moved in place. So that’s more efficient in terms there’s less operations you need to do. So that speeds everything up. Now it’s going to depend on the object itself and if you’ve got lots and lots of things in there and some of them won’t move properly, then the in-place back’s going to fall back to going pfft, can’t cope and have to copy things for you. So like anything else in the language, you can mess things up and though it’s supposed to be more efficient, it might be worse if you’ve got a horribly big class of loads in, but you shouldn’t do that, and you should never have done that. Keep things small, keep things focused and then things might be more efficient.

Philip Winston 00:37:26 I think we’re saying if you’re writing your own class you might have to provide that sort of in place back functionality, but you don’t necessarily have to.

Frances Buontempo 00:37:37 Yeah, so that’s relying on the move being there really. So yeah, these things are all tied in.

Philip Winston 00:37:44 Okay, let’s move on to going deeper into templates. So a number of languages have a feature called generics which is very similar but sometimes you see it limited to things like I have a list of integers versus a list of floats. That’s sort of the super simple example of generics templates get in C++ get a little more involved. But let’s start with one feature class template argument deduction that actually looks like it makes things simpler because it lets you declare a vector without necessarily saying what it’s a vector of. As long as the compiler can figure it out, it looks like it’s using those braces for initialization there.

Frances Buontempo 00:38:28 Yeah, so with the curly braces again, so when we were chatting earlier, talked about a vector and I talked about using uniform initialization syntax to send in a zero and a one in curly braces. Now you and I can see those are both INTs until we got the type deduction going on. You’d have to spell out STD, colon, colon for standard then vector, then angle bracket INT angle bracket and give it two INTs. But if you look at it you’re like, but I’ve given you two INTs, why have I got to do the angle bracket and INT? This is tedious and boring. So now you don’t need to say that angle brackets on the type it can see what you’re doing and that’s a good thing for two reasons. One, I’m lazy, I don’t know about you, that’s great, less to read. But also if I were to do naught one and 1.2 it would call me out and say now hang on a minute, you’ve got some INTs but you’ve got a double in there as well. Make your mind up, what do you want it to be? And it can actually catch some mistakes there where you are potentially losing precision or you are using more precision than you need. So that’s a good thing.

Philip Winston 00:39:37 You mentioned this one early on but I wanted to come back to it a little bit is DECAL type, I’m trying to pronounce it. Yep. I think this can be used with templates to deduce or determine the type of an expression, which is pretty neat when you see it. How does it, or I guess when would you use that?

Frances Buontempo 00:40:00 I mentioned that near the beginning of the book just to show some of how the syntaxes changed. I wondered if you’re going to ask this, and I was trying to think of a compelling use case nowadays. And to be honest, I can’t think of one because I think in most cases if you just say auto, it’ll figure things out for you. Now someone listening to this might be able to prove me wrong and I’m sure they’re letting me know. But I think nowadays you can tend to just say auto. I bet there are some niche edge cases I haven’t thought of though to be honest.

Philip Winston 00:40:30 I think I saw it’s used in certain types of templates. It might be related to my next question, which is type traits and, and I think you did touch on this, so you have a bunch of traits like is constructable, is invocable, is copy constructable. I wonder if under the hood those are using DECAL type.

Frances Buontempo 00:40:50 Yeah, if you would start looking at the implementation of some of those, there will be something going on. We used to have an idea of only compiling up one version of a template if it actually compiled, which was a way of overloading thing. Which had the stunning name of SFINAE which stood for something like Substitution Failure Is Not An Error. So it would just try all the different variants of templates and see which one works. Some of the things around Constexpr also allow us to do a similar trick with a bit less syntax. You can actually do if constant X and compile one way or an elm branch depending on types of things. And again, sometimes you don’t then need to dig into the DECAL types and the specifics you can just try adding two things together and see if it compiles or not. That’s something that’s been moving over the last few years and we’re finding new and neat ways of doing things.

Philip Winston 00:41:49 Actually, let’s jump ahead and bring up concepts here. So it took me a while to understand I think why concepts are important and I think it does relate to these type traits in the sense that concepts let you, I guess if I’m writing a template and the parameter of my template is a type, concepts let me prescribe what that type has to support? Is that what I’m saying?

Frances Buontempo 00:42:16 Yeah, exactly that. So if I’ve got a list of stuff, fine, but I might well say I just want some list of numeric types because I’m going to or something that supports addition or division or something. So then I can say it’s numeric and if I try and divide then it’ll be fine. Now I don’t need to do that if I just left it as auto or type there as soon as I used division or addition, I’d guess a compile error, I’d probably get several hundred compile errors and spend an hour going up and down trying to work out what the problem was. But if I put the concept at the top and say Right, I’m going to use addition so this type of better have an addition operator, as soon as you declare it the top compile error, I go nope, concept failed, can’t add this up. You’ll probably then have the hundreds of other compile error as well, but at the top it’ll be going, nah, that’s not going to work. So that’s good.

Philip Winston 00:43:14 Yeah, that leads to a question about compilers in general. I know some language if has spent a lot of effort trying to get their compilers to be understandable even I think some of them nowadays, you know it points out where in the line the error is coming from. How are C++ compile error these days? I did wrestle with some very long errors having to do with templates, so I’m wondering where that stands today.

Frances Buontempo 00:43:41 It is much, much better than it was. Again, if you start using concepts and things it’ll make your life lots easier. But there’s some other real simple things. Something that has tripped me up hundreds of times and I’ll keep doing is forgetting a semicolon somewhere and years ago you’d get thousands of compiler errors because it would try and treat the next several hundred lines as part of the same thing and eventually you’ll get internal compiler error and everything will crash around you. Now most of them will say, hmm, did you forget a semicolon? You might get all the spew afterwards, but they are starting to spot some little mistakes much more quickly. So they’re much better than they were and respect to the people who’ve spent a long time trying to make things better for us. Hard work but better, much better.

Philip Winston 00:44:30 And just to return to concepts for a second, what really surprised me, you mentioned does it support addition, does the type support, is it a numeric type? But what really surprised me is you can say something like the type must support the function draw or resize or something. So you really can give it any function that starts to sound like inheritance in a way, but it’s happening at compile time. Is that a choice you’re going to make, whether to push it all into compile time versus runtime?

Frances Buontempo 00:45:03 Absolutely. I mean it’s been the case since templates were there. You could always do dynamic polymorphism with pointers and swap things out that way and use virtual functions or you could do static polymorphism they call it. So the polymorphism would be happening at compile time and actually if you’ve got a template that relies on something with a draw method, then you can vary things out without having to build up a huge class hierarchy. And that’s back to what we were saying at the beginning about different paradigms, it’s still polymorphism but you are relying on templates to swap things out or you can have an overloaded functions to swap things out by type. There’s more than one way of doing polymorphism and it isn’t always everything derives from object and it’s all virtual functions. There’s more than one way to do this.

Philip Winston 00:45:55 That leads into some of my next questions, which I think are doing things more dynamically or kind of emulating what a dynamic language would have. So I’ll just mention standard variant and standard any. Those feel like things you would have in a more dynamic language? Are they done at compile time or is there a cost to those abstractions I guess?

Frances Buontempo 00:46:17 And there’s optional as well. So those three tend to fall together. That gives you some options, how hard to do things at runtime. That would’ve been much harder without these different flavors of one of the following or any old thing or possibly maybe a value. There is an overhead to them but not much because C++ tries to keep things as lean and neat as possible. I could come out of hundreds of different examples where you would use them, but they’re quite neat and they’re quite easy to use and they are useful sometimes. I’ve got an example in my book where I start with a deck of cards, just practice building a class and it’s got a suit and a value and then we up our stakes a bit and we add some jokers to the pack. Now I could have built a big class hierarchy and it’s a card and it’s either a card with a value or a joker, but it was much easier to go variant. It’s either a card or it’s a joker and then I could pass a variant with one of these two things around everywhere. Having a big class hierarchy and that wasn’t about dynamically swapping things out, it was just made the code neater and there was a chance to show how to use standard variant.

Philip Winston 00:47:40 You mentioned optional that kind of leads to something I’m not sure was in the book, which is exception. So language is like I think Rust and Go don’t have exceptions and everything has to be sort of an optional type in that you return a value but you can check if it’s an error. I’m wondering, when do you recommend exceptions are used and do you see them being used or do you see optional as being an alternative to exceptions?

Frances Buontempo 00:48:07 Well I mean the thing about optional is you could check whether it was okay or not and then what? So I did use optional to handle some user input in my book and if the user didn’t give a number when you asked for a number, well maybe they want to give up, so we’re stopped playing the game, that works. But that’s not exceptional like ah I’ve run out of memory now what do I do? Now some embedded systems don’t enable exceptions and you can compile up C++ code to not use exceptions because some people think it slows it down perhaps it depends on what you are targeting. Most things on a PC or less environment constrained will use exceptions but only use exceptions for exceptional situations. Right? Don’t use them for normal paths of things and then optional might be a better choice. I didn’t dig into exceptions loads and there’ve been subtle changes around exceptions and loads of people have written loads about them.

Frances Buontempo 00:49:09 You can throw an exception, you can catch an exception, but what are you going to do with it if you catch it? It depends on what’s gone wrong. Yeah, there are big topic loads of other people have spoken at length about this, they’re still there in C++. You can raise them yourself; you can catch them if you want to. But think about if you are going to catch an exception, think about why you are catching it. Maybe it’s better on being passed up to the top perhaps have a try catch block inside your main so as you can do something once at the top level if something’s gone horribly wrong but there’s still a thing.

Philip Winston 00:49:47 Yeah, exceptions are one of those features that can be used very differently from project to project based on the standards of that project or it can be a jumble if it’s not stated how to use them. I mentioned variant and standard any making it seem more like a dynamic language in a way. Another one that I really like to see is called structured binding. And I really like it because it reminds me of something I’ve always liked in Python where structured binding is you can return a pair or I don’t know what, a vector maybe, and you can pluck out the different values and assign them to variables?

Frances Buontempo 00:50:31 Or a tuple or however one pronounces the word tuple beyond pair. There are some circumstances where you want to return two things from a function and instead of building a struct and getting a struct at the other end, you couldn’t return a pair or a triple three things in a tuple. And then like you said, unpack them. A compelling use case there is if you are walking through a lookup table, so the maps we were talking about earlier, you’ve got a key and a value and those used to be the first part of the iterator and the second part of the iterator that just led to hard to read code. But now you can say auto key comma value equals find a thing in the map and then you straight away got them insensibly named variables of the key in the value. So that’s really neat.

Philip Winston 00:51:24 Yeah, I like that because then the caller can choose to keep the tuple or keep the pair if they want. But they can split it out if they want so it’s kind of the best of both. Let’s see, we did mention the ranges library. That is something that feels a little abstract like I’m not as clear. It sounds like these are algorithms and containers where you don’t pass begin and end, but it seems like it’s more than just that.

Frances Buontempo 00:51:53 Yeah, sure. So I mean traditionally if I want to do something of a vector and send it into an algorithm, I have to do my vector dot begin my vector dot end, which is tedious. If I use a range version instead, I can just send my vector in like you said. So what like surely there’s more to it than that? What we were trying to do in ranges, I think maybe partly inspired by link in .NET which allows you to filter or take while or drop while or walk over a collection of stuff maybe according to some criteria. And importantly there that supports lazy evaluation, right? That’s the deal. So if I’m filtering things or transforming things, it’s not until I crystallize it that I end up paying for things. So it’s trying to make things more efficient. Think Andre Alexandre Dresco started talking about ranges in C++ years and years and years ago and he was experimenting with them in D and that got everyone the C++ community interested in thinking about it. And like I said in Smile by link in .NET, that idea of lazy evaluation, if I send a whole vector into transform, in the standard library, I will get the whole thing transformed after that call. Whereas if I do the ranges version, there’s a subset of ranges called views and they’re the lazy evaluation. And it’s not until I actually start pulling things back out of the view that anything starts taking up RAM. It’s got some clever skullduggery going on with the iterators to make things work. It’s the lazy evaluation that’s the clincher.

Philip Winston 00:53:46 Yes, that’s helpful that that’s definitely something I didn’t pick up, but I can see how that’s super useful. Especially if you’re iterating over a very expensive container like the queries a remote database or something. I also saw ranges I think lets you use the pipe operator to chain together algorithms. That’s one of those C++ tricks I would say that sort of blows your mind a little bit because you’re like how did it repurpose this pipe operator? Can you say when you would use that and maybe how did they do that?

Frances Buontempo 00:54:22 C++ you can overload all kinds of things, including the pipe operator or the comma operator. Oh, you can come out of all kinds of skullduggery just with great power comes great responsibility though. Just because you can doesn’t mean you should, but if you use Linux or an OS based on that, you are used to like doing CAT and then piping it to grip and then piping it to something else. So you’ll do the pipe operator between all the things to chain them together. And I think that it does look neat, and you intuitively understand that it’s piping the things together. It’s the pipe operator and you can overload the pipe operator. So we did. But that allows you to say, right here’s my vector, I want to take the top five and I want to filter by wherever it’s order or even I could do this dot that the other. But with the pipe in between, you can just have separately on one line at a time what you’re doing and then it’s kind of almost readable or fluid. You can see what you’re doing neatly. But yes, skullduggery of operator overloading.

Philip Winston 00:55:31 Yes, I think it’s very readable. And another feature that comes to mind that’s related to sort of like how did they did that are, user-defined literals, I think it was in your book where you talking about time and you’re actually allowed to have suffixes to integers that are like MIN, M-I-N or MS millisecond or NS, Nanosecond. That kind of blew my mind. I don’t think I’d seen that. Again, how does that work? What are they overloading to allow for user-defined suffixes?

Frances Buontempo 00:56:05 I can’t remember the details; I should know this. It’s basically it’s function overloading. Again, in terms of what you read, you can read and see. Oh, 17 men, that means 17 minutes. You can introduce your own literals too. So if you had colors going on, you could talk about RGB values for example, or just use your imagination there to see what you can come out with. It starts from the idea of saying, well, if I want, say I’ve got an integer and it’s 42, I can write in 40 x equals 42. But if I want to specify unsigned, I put a U on the end, say it’s unsigned or I can do a long with an L or LL for long long and well, if I specify a string, I’ll put hello world in double quotes. What I’ve actually got there is a C style char array. But if I want to put it into a standard string, okay, but then it feels like there might be overhead if I just put an S on the end of the second quote that then finds the function, that gives me a string, a standard string back rather than the C style string. So tiny little feature and you can write your own if you want

Philip Winston 00:57:20 One more small one and then we’ll go on to concurrency and parallelism, which I wanted to talk about. I think those are really relevant to today, but there’s a C++ 20 spaceship operator, I looked it up and I think other languages have it. It’s not unique to C++ but tell us what the spaceship operator is for.

Frances Buontempo 00:57:44 It allows you to add comparison operators to your own type really quickly without doing any work at all as long as you haven’t got something really complicated with millions of fields in. So if I’ve got a struct, well, I mean back to the example of the playing cards, again early, I’ve got a struct that’s got a suit and a value in. I might want to work out if this card’s better than that card. So I kind of want a greater than operator or I could write one by hand, but I can use the spaceship operator to not just if I’ve race in a greater than, I then need a less than and an equal as well. If I put the spaceship operator in there and tell it to just use the value, we’ll ignore the suits, then I’ve instantly got the greater than, the less than and the equals as well. For me, it’s a neat way of being consistent. If you don’t do it, you can end up with incoherent greater than less than an equivalence operator and then you’ve broken all of mathematics and got yourself a headache. But it will consistently generate things for you. So it makes your life easier.

Philip Winston 00:58:55 And for people wondering what it looks like, it’s less than equals and then greater than?

Frances Buontempo 00:59:02 Yes, which looks exactly like a spaceship, right? .

Philip Winston 00:59:06 Now I mentioned concurrency and parallels and these are huge features for today’s hardware where you might have a hundred cores and if you’re using one out of 128 or something, you’re really leaving a lot on the table. So I guess two different features. One are cot routines and one are execution policies. And maybe under the hood they use cot routines or maybe threads, I’m not sure. But can you talk about the push towards more features for concurrency and parallelism?

Frances Buontempo 00:59:38 Yeah, sure thing. So the execution policies, we’ve mentioned algorithms like transform and there are plenty of other things there. Almost all of them now have an overload that takes an execution policy. So you can say do this in parallel. There are some circumstances where if there’s some sharing going on, it can’t tough. But if you’ve got things that are clean and immutable and it’s taking a range and list or vector of stuff and returning some stuff so there’s no shared state, it ought to be able to execute that in parallel. And yes, it will be using threads under the hood for you with very little change. You can speed things up and get several things running in parallel all at once just because of that one extra parameter in your function. Call codes is a huge topic. There are other ways of doing parallel things in C++.

Frances Buontempo 01:00:30 There’s lots of threading support there with futures and promises and async and all the rest of it. And Anthony Williams is the expert on that. Go read his book if you want to know more about this. He goes in detail there. COT routines are kind of slightly separate. They’ve been introduced in C++ 20. You need to do a lot of the work yourself to make them work. What it’s doing at a very high level is you write a COT, which is kind of a function with some supporting struts around it. And that in effect gets packaged up into stuff that can go on the heap. So it can run for a bit and then you can pause it so you would yield the value. You’ve mentioned Python just now. You can write a generator with the yield co-wording, and it’ll yield a value.

Frances Buontempo 01:01:24 And then when you call it, the next time it continues where it was. The same happens with a C++ COT routine. Essentially you can yield a value from it and when it hits that yield statement, which is spelt CO_yield and C++, of course it yields the value back to the calling code. And because the state’s packaged up on the heap, next time you call it, it then teleports back into where it was and continues. So it works very light yield in Python basically, but it, it’s all packaged up on the heap for you and that allows a COT routine to call a different COT routine when it’s finished. So you can compose all kinds of bits together and there’s really quite compelling use cases. I’ve seen Phil Nash talk about loading up lots of data for, well it was yield curves as an investment bank, but some of the input data needs more static data to fill in bits. So it wait until it’s got all the data it wants to make the next bit of the curve then return and do the next part. So you can piece together lots of things being drawn from a database and so on. And because it would just continue when it’s ready. You don’t need to decide who’s going when or what. It just happens as it happens and it can do things really quite efficiently. Once something’s ready, the next thing will then start. So you don’t need to think it through.

Philip Winston 01:02:46 To me, the real power of COT routines in various languages is not obvious at first, but it’s the fact that you can decouple the amount of concurrency from the number of threads. So right. You might have a thread pool is size of the number of cores or maybe it’s two x the number of cores. But COT routines, you could have thousands or tens of thousands and it’s really very efficient that way. And that you’re not overloading them.

Frances Buontempo 01:03:13 Exactly. Yes.

Philip Winston 01:03:14 Okay. I think we’re going to have to leave out about a third of the topics because we’re kind of running out of time, but this concurrency and parallelism talk reminds me of my overall impression reading about C++ in the last, what’s been added in the last say 10 years is that there is a lot of convergence between languages. So you can see a feature gets added to language A and then a couple years later it gets added to B. And I think that’s happening more than a lot of people realize where if you do switch from language to language, a lot of features just kind of come with you, I guess.

Frances Buontempo 01:03:47 Yeah, yeah, definitely. I mean you mentioned the structured binding looking exactly the same as you see in Python. I think C++ is learning from other languages and maybe other languages have learned about generics from C++. It’s yeah, goes around, comes round.

Philip Winston 01:04:04 So I wanted to real quickly mention three tools you point out in the book that I think would really help people if they’re wanting to learn more about C++. And I’m going to read all three of them here and then you can kind of say what they’re for. So one is C++ reference C++ insights and compiler explore. And I’ll put links to these in the show notes because I’m not reading the actual URLs, but I think you’ve mentioned all three of these. I went to all three. They seem really useful. Can you say when you would use either one of those?

Frances Buontempo 01:04:37 So if you’re trying to look something up, you can spend money on the C++ standard or find a preview of it somewhere, but it’s huge and you’ll get lost. CPP reference gives you kind of an executive summary of this to an extent. It’s kind of crowdsource, so you can edit this if you see mistakes and it’s converging on Correct. I think that’s by far and wide the best reference site. If you want to look something up, it’s got one or two missing bits and it hasn’t got the full legalese of the actual ISO standard, which makes it easier to read, but they might be missing a few details, but that’s the best place to check something that’ll probably keep you straight. That’s good. C++ insights, that’s Andreas designed this tool. If you put in some modern C++ code, we talked about the trading return type afterwards.

Frances Buontempo 01:05:31 How do you look up, what does this weird symbol mean? You can’t Google that very easily if you put the code in C++ insights. It’s uh, give you a suggestion of what might be happening on your compiler under the hood on what it’ll be equivalent to. So it will fill in all the gaps for the stuff that’s syntactic sugar that’s there to make your life easier. And then you’ll have more of a sense of some of the temporary unnamed structures that might be happening in the background. It might give you more of an understanding of what the code’s actually doing and how it’s implemented. And the final one Matt Gobot has written is Compiler Explorer. So loads of people refer to it as Gobot rather than compiler explorer. He’s a lovely guy and he spent ages on this. You put in your code and you can make it run and tell you what the output is.

Frances Buontempo 01:06:22 So what it’ll also show you the assembler, it drops out. So I gave you an example earlier of using context with something complicated looking and then you see the assembler and you could do this in Gobot and just see the number right there in what’s been compiled. And as you scroll over things, it will highlight the line of assembly you are looking at and point you back to where the C++ code came from. And people use it to argue about what’s more efficient sometimes if you want to see if something compiles on a different compiler, he supports loads of different versions of things. So if you get a compiler error you don’t understand locally, it might be worth seeing what it says on a different compiler and that’s save you having to set one up. So yeah, thanks to Andreas and Matt for those two tools, they’re really useful.

Philip Winston 01:07:10 And those bring to mind something which is these languages are getting bigger and more complicated, but tools are also getting more impressive and more featureful. I guess to sum up kind of what we’ve talked about, if I were starting a project today and considering C++, what would you say the attributes of that project I should consider? What would steer me towards using C++ or not? Kind of given what we’ve talked about?

Frances Buontempo 01:07:35 It’s fun to catch up and learn what you’ve missed out on for the last few years, so just go for it. There’ll be some cases where it’s not the sensible thing to do. If you just want to write a website, well, you could write it all in C++ and send it for own script and it generates some type script for you and it worked, but that’s probably over the top. But if you’re doing some number work or writing some simple games, or possibly you want a supporting library in the background because C++ is quite good at interoperating with other languages, go for it. Try it out. Write some tests whilst you go lean on the compiler. Use the type system, knock yourself out, have fun.

Philip Winston 01:08:16 And related to that, if you are learning C++ in addition to your own book, what other tips do you have for someone so they can get over that initial intimidation maybe of the size of the language.

Frances Buontempo 01:08:31 Just start small, put some numbers in a vector, take it from there. Listen to some podcasts to help you out. Maybe find someone to talk to. A friend of mine’s recently set up a Slack group and we’ve just been meeting up once in a while on the intellect and it’s really useful be able to just say, ah, well, does this compiler ever mean, or this code looks ugly, how could I make it better? So we mentioned that I edit at Cues Overload magazine. They’ve got an email group. Anyone can join, reach out to people, get some input from somewhere else, find a friend who’ll encourage you and help you out if you get stuck, don’t just talk to yourself or scream at your compiler, find some friends, chat to people about it.

Philip Winston 01:09:17 Anywhere else online you can mention either for the beginner or maybe someone that actually wants to get involved in the standards, whether contributing to them or just following along what’s going on?

Frances Buontempo 01:09:28 I’ll have to dig you out a link to share on the show notes, but ISO CPPs got a great overview of how to get involved in the standards and even if you just, if something strange happens in your compiler even so not as far as the standards, report bugs, you can easily find a list of outstanding bugs for Visual Studio Up vote ones. If you find things, speak out, don’t suffer in silence, talk to each other. There are loads of ways you can get involved.

Philip Winston 01:09:57 How about finding more about the book or following you online? Can you give any links to that? I’ll put them in the show notes.

Frances Buontempo 01:10:04 So yeah, sharing the show notes. I’m hanging out on Masterdon and Twitter and LinkedIn quite a lot, so I’ll share the links with you.

Philip Winston 01:10:13 And the book. Is there a page for the book or?

Frances Buontempo 01:10:15 Yep, so I’ll share that with you as well. There’s a page directly at Manning and it’s soon to be released on Amazon, it says in the next few weeks.

Philip Winston 01:10:23 Great. Thanks so much for joining me today, Frances.

Frances Buontempo 01:10:27 Thank you for having me.

Philip Winston 01:10:28 This has been Philip Winston for Software Engineering Radio. Thanks for listening.

Frances Buontempo 01:10:34 Cheers.

[End of Audio]

Join the discussion

More from this show