CppCast - PVS-Studio Static Analysis
Episode Date: March 12, 2020Rob and Jason are joined by Yuri Minaev from PVS-Studio. They first discuss a blog posts on ISO's recent decision not to break the C++ ABI in C++23 and getting rid of volatile in the Qt codebase. Then... they talk to Yuri Minaev, one of the developers at PVS Studio working on the static analyzer. They discuss some of the forms of analysis that the tool excels at and how it's changed the way Yuri programs. News HOly Grail of Logging The Day the Standard Library Died The Performance Benefits of Final Classes Getting rid of 'volatile' in (some of) Qt Links Yuri Minaev Blog Posts Sponsors PVS-Studio. Write #cppcast in the message field on the download page and get one month license Read the article "Zero, one, two, Freddy's coming for you" about a typical pattern of typos related to the usage of numbers 0, 1, 2
Transcript
Discussion (0)
Thank you. In this episode, we discuss breaking the C++ ABI.
Then we talk to Yuri Muniv from PVS Studio.
Yuri talks to us about his work on PVA Studio Static Analyzer.
Welcome to episode 238 of CppCast, the first podcast for C++ developers by C++ developers.
I'm your host, Rob Irving, joined by my co-host, Jason Turner.
Jason, how are you doing today?
I'm doing okay, Rob. How are you doing?
Doing okay.
Keeping my hands clean, coughing into my arm. how are you doing in colorado are there a
lot of concerns about uh coronavirus uh it's like 18 confirmed cases here now our governor just took
a bunch of proactive measures to make sure that uh anyone who needs to take time off regardless
of whether they actually have sick leave will get paid sick leave. That's good. And to ensure that all testing is free
and just try to alleviate people's concerns
so that if they think that they need to get tested or whatever,
that they can do that without feeling like I'm going to lose my job
or something stupid.
That's very good.
I guess we should actually talk for a moment
just about how it is affecting the C++ community a bit.
I mean, I know it's not a C++ conference, but I was planning on going to the Microsoft MVP Summit next week, and that was canceled.
I believe Pacific++ was also canceled, right?
Oh, I don't know.
I think it was.
Are you aware of any other big cancellations or interruptions to anything
uh no i haven't seen um i mean you know e3 and stuff like the game conferences are canceled
which is no pacific plus plus didn't have dates announced for 2020 i thought i saw something get
canceled yeah um there's yeah there's there's like, we just don't know a lot right now still,
because let's see the next conference coming up is core C plus plus.
And as far as I know, they haven't announced anything.
Um, Israel is, uh, doing quarantine at their borders.
So that could affect something, but that's still more than two months away.
So yeah, hopefully, uh, in two months things will start to, uh, calm down.
Yeah. And, um um embo plus plus is
starting like right now and they are not canceled um but yeah i don't know i have no idea i don't
have anything really coming up until the end of may myself so uh i just kind of want to know
sooner rather than later if it's going to be canceled, right? Yeah, yeah, definitely.
Well, if you are attending any conferences,
definitely make sure that you are washing your hands and maybe don't shake as many hands.
Okay, well, at the top of every episode, I'd like your piece of feedback.
We got this tweet from Kobe Cohen-Razi responding to last episode,
and you, Jason, saying you're asking about
app crashing and losing data that's a reference to the logging the logging libraries asynchronous
logging asynchronous logging if the app crashes yeah so he's talking about hogel and saying it's
another async logging uh library and when the app crashed the unflushed buffers will be in the core file and
hoggle has a tool to parse the core dump and extract the unflushed data on flash data so yeah
i guess that is a concern that you can lose stuff with async logging but uh hoggle at least has a
tool for getting that data out for you i can say i've absolutely never tried to parse a core file, core dump, to try to find logging information.
No, I don't think I have either.
Yeah.
Okay.
Well, we'd love to hear your thoughts about the show.
You can always reach out to us on Facebook, Twitter, or email us at feedback at cbcast.com.
And don't forget to leave us a review on iTunes or subscribe on YouTube.
Joining us today is Yuri Manev.
Yuri is working on the PVS Studio
company as one of the developers
on the C++ Static Analyzer. His primary
responsibility is to keep low-level stuff
in order and add new features to the core module.
It's been almost two years since he joined
the team after about 12 years of
IT experience. Apart from that, he periodically
gives talks at various conferences, mainly
on topics related to static analysis and C++.
In his spare time, Yuri likes to program in C++, play guitar, and sometimes hike around aimlessly.
Yuri, welcome to the show.
Hi, everyone. Thanks for having me here, guys. It's a pleasure.
Certainly. You say hike around aimlessly. Do you live in an area that's good for that?
You've got hills or something nearby, a forest?
Yes, kind of.
We have a forest.
Well, a couple of forests. We have
a park here, so there are
a lot of places where you can go.
I actually
like to go around the town
surprisingly.
I'm sorry, my tone
is not cooperating.
No, that's nice.
I don't live in a, like, I mean, I run around my neighborhood,
but I don't live anywhere close enough to a town
to really enjoy walking around the town, unfortunately.
Okay, well, Yuri, we've got a couple news articles to discuss.
Feel free to comment on any of these,
and then we'll start talking more about the work you do at PBS Studio, okay?
Alright, so this first one
is a blog from
Corentin that he
wrote right after the Prague meeting
and it's the day the Standard Library
died.
Mostly just talking about his reaction
to the
ISO committee voting to
not break ABI in c++ 23 and why he thinks that
was a very very bad idea it's a lot of very good article this is a very good article he goes into
some of the things yeah he goes into some of the things that could be changed with an abi break
um and just you know uh why he thinks that we should be.
Sorry, you had something to say?
No, I just said that the article was kind of
interesting because on one hand
breaking ABI doesn't sound
like a great idea, but on the other
if we can squeeze performance boosts
from it, maybe it's worth it in the end.
I read it.
I wasn't sure about my opinion at that moment
because I think both positions on whether or not we need to break the ABI
are kind of valid.
So you can argue for both, I guess.
Well, his argument that if we're not willing to ever break the ABI, then we have to accept that anything that we add to the standard library tomorrow is just pretty much going
to be that way forever.
Yeah.
That's a pretty strong argument. Yes, it is. But, okay, if we break it, won't it break backwards compatibility with old projects, with old code, maybe?
So, yeah, I personally, I think that changes are good.
So I would break it, to be honest.
I would break it, to be honest. I would too.
I'm definitely in the camp of, yeah, we can be expected to recompile all of our code once every three or six years or whatever, personally.
But I've said this many times in the show, I've never had to work in code that relied on binaries from companies that went out of business and they have no chance but to, you know.
And one thing he brings up here is, you know, they had this vote about whether they would break
ABI and C++23, and then that was voted against. But then they had several votes to say, you know,
we will not promise stability forever, and they will consider proposals requiring an api break so you know if you're not
going to break abi in 23 maybe we should decide now when we will break the api because if you
just keep holding that same vote every three years like hey will this be the release that we break
the api you're probably always going to say no no one's ever the people who are not wanting to
break it now are going to not want to break it again three years from now and then six years from now.
But maybe if you propose, say, okay, are we going to break it now or are we going to break
it six years from now instead or nine years from now instead? Maybe you need to
kind of hold that vote now and hold everyone to it.
I liked his argument that if we don't do it
now, people will wait for longer and
longer and like they will lag behind the changes and code people right will
assume that the ABI stays the same and yeah so this one I liked that we
need to do it now
to make it maybe less painful
and force people
to migrate
to the new standard.
Yeah.
Also, I think for anyone
who's listening, who's in this boat
of saying, well, we can't
break ABI
compatibility because I rely on these old binaries. There's a sentence here says, let's be very clear
programs that rely on ABI probably violate ODR somewhere and probably using incompatible flags
that happen to work. So people who have old code that they're relying on ABI stability today,
I agree with them.
There's a very good chance that your code's actually broken.
You just don't know it yet.
So consider that.
Yeah,
that's true.
Okay.
A less controversial article,
although we all seem to be on the same side of that one.
Yeah,
it's no controversy here.
Yeah.
This is from the Microsoft C++ blog,
and it's a post from Cybrand about the performance
benefits of final classes. And this goes into, you know, if you declare final on your class,
you can de-virtualize the function calls, which gets you some performance improvements.
So it's kind of just a nice brief overview of the benefits
of using final. This is a fun one from my perspective, because it started as something
of a Twitter conversation resulted in Cy saying, I'm going to write an article about that. And at
about the same time, I was making my argument for where I use final and ChaiScript. And I can,
I'm going to probably make a C++ weekly episode about this, but I have an intermediate layer in my library. I'm using final. And if I remove the final right there,
I can easily trivially measure a 20% performance hit. Like I need it for the way that I designed
that portion of the API. So I'll probably put that in an episode also as a follow on to size article.
That's a big performance hit just by removing final yes 20 yeah 20 is what i last measured now to be fair that was like three or
four years ago and compilers are constantly changing so we'll see what it is today yeah
okay and then this last article is on kdab's blog uh fromppi, and it's getting rid of volatile in sum of Qt.
And it's basically a response to the proposal
that was accepted in C++20 to deprecate parts of volatile,
and the author decided to go through Qt's code base
and clean up instances of volatile
that are going to be deprecated with C++20.
And found some interesting cases where
volatile was being misused, but also some places where
volatile needed to be used, which I thought was interesting.
You mean long jumps?
Yeah. The long jumps part. Yeah, which is also disturbing to me.
Yes.
Could either of you maybe explain the set jump, long jump stuff a little bit better for our listeners?
I've never used it before.
I think I understood the article. I have not used it before either.
If Yuri, if you've got insight, I'll let you explain it.
Otherwise, I'll do my best. So what I was saying is volatile, I feel,
is one of the most misunderstood and misused features in the
language. And the article, it seems to be confirming that. For example, I liked
the part where volatile was used as an atomic.
And for synchronizations between threads.
That was kind of disturbing to me, to be honest.
Could you comment on the usage of Volatile as set jump, long jump,
which is the legitimate use of volatile,
and maybe explain that a little bit better for our listeners?
For set jumps and long jumps?
Yeah.
Well, that's, as I understand it,
it's an old, old piece of legacy code,
which came from C. volatile there, well it fends the compiler
from doing anything with your jumps between functions.
Those long jumps, they are kind of a poor man's exception handling mechanism. I guess along with no return functions in C
which you could use to kind of
handle some exceptions and maybe
terminate your program. Well
volatile there is... it seems legitimate
and it seems useful but I, but if you ask me, I would get rid of the long jumps and set jumps in this case instead of using this old mechanism.
I was curious about that also because this comes from libjpg usage, I believe.
And it seems that that's the prescribed way.
That's how they suggest that you handle the errors.
And I was wondering if they have some other error handling mechanism
where you don't have to use set jump and long jump.
Yes.
Well, as I understand, the library is old.
I never used JPEG, the libjpeg, personally.
Yeah, me neither yeah but it it looks like it looks like it came
from an old age you know from c and whatnot ah yes it's dated 1998 right um and he did a little
bit of googling and seems to have found that most libraries that use libjpg actually have a bug in their error handling routines.
Really?
Yeah.
Long jumps and set jumps with GTK, GDK, and ImageMagick all seem to have made the same mistake copying and pasting from libjpg's example code.
Nice.
I actually just had a class that I taught earlier this week where we discussed very briefly volatile.
It wasn't the point of the class, but it came up.
And they said they had old code that used volatile for thread synchronization,
which we know is not the correct thing to do.
And they had migrated that to Atomic,
but had not actually dropped the volatile,
so now they had volatile Atomics.
I said, you probably want to get rid of that volatile.
It's not doing what you think it's doing.
The Atomic is what you really need there.
Well, I guess in embedded systems,
it can be useful if you're waiting for some data from some register.
You can use them, volatile variables, kind of to connect them to hardware.
And you use volatile to prevent compilers from removing them. Like a compiler can look, for example,
at a loop where you're waiting on some value of a variable and decide, hey, this variable never changes,
so I'll just remove the whole thing from here.
So that's a good usage, I guess.
So out of curiosity, while we're still on the topic of volatile,
is this something that PVS Studio has checks around as well?
We consider volatile in cases like loop optimization and things like that. So if we see it, we know that the compiler won't kick it out.
And we also check for some cases of incorrect usage, but they are rare.
We use it mainly as a marker that anything can happen to the variable
and the compiler won't optimize it away properly.
You know, one interesting case is there is that cursed thing called memset.
People like to use it sometimes for zeroing memory.
If you have sensitive data, you want to zero it out, but compilers like to kick it
out of your
code just for reasons
because
I guess they like
to do that. But if you use
volatile pointers in
memset, compilers sometimes
skip it.
Interesting. So some people are using it
as a way to
for some level of security,
to stop the compiler from removing their call to MSAT. Yes, you can use it like that, but I
think it's still a hack. Sure. Okay. Well, why don't we start talking a little bit more about
the work you do? I guess maybe to start off, how did you get interested in stack analysis?
I was looking for a job as a programmer,
and I looked at different companies,
and I came across this one and said,
hey, something interesting is going on here.
And I started reading about that, and I decided,
okay, I want to go work there.
Because those guys, my reasoning, I guess reading about that and I decided okay I want to go work there because those guys they my reasoning I guess was that okay all those different companies they just use the language to do stuff but those guys
they the language is not only a tool to them it's the subject of the study if you want to call it like that.
So yeah, I just
came across the company
and I decided to
read more about
static analysis in general.
And I decided that, okay, I want to go
work there.
And you said you've been working there for
two years now? About two years, yeah.
Okay.
What is different about And you said you've been working there for two years now? About two years, yeah. Okay. Okay.
What is different about PVS Studio's analyzer compared to some other static analyzers out there?
You know, that's the question we get often.
And usually we say something along the lines of, you know,
we don't compare our tool to other tools because it's our tool and it will look better than others.
Blah, blah, blah, blah, blah. You know, the usual stuff companies do.
Well, actually, I don't have a definitive answer to that.
I guess every tool is good in its own area. Maybe some tools do some stuff better than others,
and it's been like that since forever, it looks like.
I would say we test our analyzer very thoroughly.
We have a test base of about 100 something open source projects.
And every time we do any change, we run tests against that base of projects. And we see if
anything broke. So we've got a very vast code base for tests.
I don't know if others do that.
I have no idea.
Yeah, that's a difficult question, but it's a good one.
Maybe if I could change the question,
the original question of what sets it apart is perhaps are there certain types of errors that
pvs studio is very good at catching copy paste oh okay uh copy paste uh also null pointers
if you are trying to dereference a null, we usually can catch it very well.
I can tell you what it's not so good.
It's multi-threading mutexes and stuff like that.
Oh.
So yeah, it's not very good at that.
I didn't even know that any static analysis tools
checked for that.
I just used thread sanitizer or something.
Run time. that any static analysis tools checked for that. I just used thread sanitizer or something.
Yeah, you will end up better with a sanitizer if you want to check that kind of stuff.
We have a couple of diagnostics for multithreading.
Mostly stuff like, oh, you locked a mutex
which was already locked, stuff like, oh, you locked a mutex which was already locked.
Stuff like that.
Oh, okay.
So like if in the same code block or something or in the same control flow.
Potential deadlock.
Okay.
So you said copy-paste errors.
How much trouble can you get in with copy-paste errors that you just said you will find?
Yeah. errors that you will find. It's surprising how
many copy-paste errors
we find
almost every day.
I don't know if you know that, but we
like to check some open
source project and
write an article about that.
Okay, so here is
this error and that error.
And copy-paste is usually a huge part of that.
People copy paste conditions inside their ifs and they like you do this and this and that and that
and you can screw up indexes in your arrays. You can screw up variable names.
I've seen a lot of that.
If you're initializing a block of
similar variables,
many errors occur there. So copy-paste
brings a lot of fun.
So you can find where you did a copy-paste
but only updated two out of the three variables
that needed to be updated or something.
Yes.
And we can also find when you have the same code
in two lines in a block.
So, for example, if you have a condition,
again, going back to conditions,
you decided to check different things
and use some logical operator like and, or, whatever.
And in this block, you have two lines with the same code,
two conditions with the same code.
We can find it very well. And we do find it constantly.
Reminds me of some refactoring work I've been doing recently where I have if and else blocks
in a client code that were just very, very large. And I started to refactor and clean things up.
And as I started refactoring, cleaning things up, I realized that the if and else were actually the same block of code.
So I did the same work twice, but I couldn't see it until I had reduced it.
Oh, I'm doing a heavy refactoring right now.
We decided to clean everything up.
And once I believe I made two functions that had exactly the same bodies.
I completely missed that.
I want to interrupt the discussion for just a moment
to bring you a word from our sponsor.
This episode of CppCast is sponsored by the PVS Studio Company.
The company produces the same name PVS Studio Static Code Analyzer,
that has proved to be great at the search for errors
and especially typos.
The tool supports the analysis of C, C++, C Sharp,
and Java code.
The article, 012 Freddy's Coming For You,
which has been recently posted,
clearly demonstrates the analyzer's outstanding abilities
of searching typos.
The article shows how easy it is to make a mistake,
even in simple code, and that no one is immune from making it. You can find a link to the article
in the podcast description. There's also a link to the PVS Studio download page. When requesting
a license, write the hashtag CppCast and you'll receive a trial license for a full month instead
of one week. Since you're talking about refactoring the PVS Studio code, could we ask you a little bit about how you write the actual static analyzer?
I mean, is it written in C++?
Yes, it's written in C++.
We have our own parser and our own AST.
And that's not...
How to say it better, that was a hard decision because the thing is kind of old and dated and it has a lot of legacy code in it.
And we considered to use clan at some point, you know, just to make things easier.
Right.
But then we thought, oh, wait a minute.
There is that thing called Windows and that thing called Microsoft.
And Microsoft has a thing called CLI and CX.
And we support that. Okay.
And we looked at Clang and thought,
there's no way we can make Clang support that.
Yeah, so we decided to keep our own parser
and our own system
and just upgrade it to new standards
and add new features.
So how hard is... Oh, sorry.
That's why I'm doing refactoring right now,
because we've decided to add more abstraction levels.
Let's say that.
It was lacking.
Okay.
How hard has it been to keep up with the changing C++ standard with your parser?
Depends on the change.
I can tell you that things like if with initializer, it was easy.
Structured binding was not too easy, but not difficult.
But we are looking at concepts now, and they are kind of scary right now
because that's a new thing entirely.
And we need to understand everything about them
from their declaration to usage.
Please go ahead.
No, go ahead.
I was just going to ask about that.
Are you trying to get ahead?
Are you supporting concepts and coroutines, that kind of thing?
We are starting to do that, yes.
We are trying to do it ahead of the standard release.
But if we do it too early,
we can end up supporting something which will change in the next standard version, in the release version of the standard.
Right.
If it makes sense.
So we try not to do it too early, but we try not to do it too late.
Right.
So you think you should have support for most of the C++20 features
Pretty close to the release of C++20?
I'm not sure about most features
But we won't choke on the C++20 code for sure
Good, that's good
One question I have is
Obviously it's a professional tool
But are there options for open source
developers or students?
Yes, if you have
GitHub, you can
get a free license
which you can use
with your account.
Also there is a kind of
trollish free license
where you enter a special
key, it's not really a key.
You just enter free, free, free, free,
free everywhere in the
registration form. But you have to
add some comments in your code
which say,
that's an open source
or school or something, project
BVS Studio, please check it.
I think it was trolling.
Kind of trolling.
So, if I have
GitHub and I want to use it with
one of the continuous build
environments like Travis or something,
is that possible?
It's just a command line tool.
You can do it.
And we have a bunch of
tutorials on Travis
and things like that.
Okay.
So it's totally possible to do it in the cloud environment in your build system.
Okay.
What are some of the more interesting things you've discovered while working on the static analyzer?
Oh, one thing. Once I was doing...
Well, I was upgrading one diagnostic rule about noexcept functions.
The thing was going like that.
We have a noexcept function, and inside of that function we look for stuff which can throw.
So, for example, if you have new there, it can throw.
If you have, I don't know,
some weird cast between... If you dynamic cast a reference somewhere, it can throw. And functions
which don't have the noexcept specifier, they also can throw. But if we complained about every function not marked as noexcept, it would be too noisy.
Right.
So, yeah, we decided to look deeper inside those functions and check if they actually throw and so on and so forth.
And it worked very well. But at some point our tests went
crazy and decided to tell us, okay, this noexcept function calls something which
can throw potentially. Okay, I go there, look at the tests and see Windows API
calls there. It wasn't a no except function it
was DLL main but basically it's the same thing you cannot throw from DLL main
okay so I see API functions there and I go like what's going on API functions
don't throw anything they are C yeah. They're not C++, really.
Okay, so I'm in Visual Studio
pressing F12,
and I get into the code of
this API function
which was used in that
DLL main function, and I'm like,
hmm, an API function,
and I have its source
code, the definition,
the body, everything.
All right, I look further down.
It still doesn't throw, doesn't look like.
And at some point, I get to another quote-unquote API function,
which calls new.
So it was, I don't remember what project it was,
but I guess it was some project
which decided to overwrite standard Windows API functions
and rewrite them in C++ to place hooks
or do something like that.
So, yeah.
It doesn't make sense, Jake.
No, I've seen code, yes,
where you, you know, yeah, create your own version of,
I don't know what's a good one, open.
Open, I've seen.
And so that you can hook any open call
and try to intercept that
and open something that's been embedded in your library
instead of loading from the file system or whatever. Also, people use this technique in stuff like,
I don't know, do you know about sweet effects for games? I don't. So basically, there are tools which you can use to add more fancy stuff in your game.
You launch an executable, and you use some shaders and maybe even some different germ
tree.
So yeah, people often use this technique.
They just rewrite standard DirectX functions, put them in a DLL,
and place it near your game's executable.
And the game, when it calls DirectX library functions,
it gets into your DLL and ends up calling your code.
So yeah, that's a widespread thing.
Yeah, I've also seen that for game overlays
to tell you your current FPS or whatever.
Yeah, yeah, yeah.
And interestingly, when I first released ChaiScript
many, many years ago,
when I was Googling around to see
how people were using it,
I think one of the early users
was someone who was making game cheats
that did a similar thing,
hooking into dll calls to
cheat yeah yeah oh yeah i am curious now at this point after you've spent a couple of years
working at pbs studio how has this changed the way you write code personally um i became paranoid paranoid. Really. You start to look at code from the point of view like, what is
this thing going to find there to complain about? And then it's not like
I'm afraid of it or anything, but
some things just... when you see them a lot of times, they just get into you
and you try to avoid them. I'm not good at that, I admit, because, for
example, dereferencing an all pointer, it happened for me more than once since then.
But definitely I became more careful about what I write.
And it's kind of surprising how many things you don't even think about
before you take a closer look at what some tool can find in your code.
So it sounds like it's definitely made you a better programmer,
albeit a more...
I don't know if it's better.
Well, it also sounds like you still see a place for runtime analysis
like Valgrind or the sanitizers
for catching the kinds of things that static
analysis isn't best at.
Yeah, sure.
Dynamic analysis
is a huge part, actually, of
reliability.
I think you
should use sanitizers
or stuff like that
if you can.
Because they just add to your application security stability and stuff like that.
Right.
And plus, dynamic analysis can catch things that no static analyzer will be able to catch.
For example, memory leaks.
Let's say memory leaks are challenging,
especially if you allocate something in one function
and then it goes around in your program
between calls, between functions,
and you delete it somewhere else in the destructor or something.
Sometimes static analysis can choke on that, let's say.
Right, like you would almost effectively have to execute the entire program
during static analysis to know if that was ever deleted.
Yes, and the most difficult part of that is data flow,
because you have to track the state of every variable for the entire control graph.
And if your control graph is branchy, it can become a difficult task. Is it fair to say that simpler,
more readable code is
also something that the static analyzer
can work with better?
Yes, to some extent.
Okay.
You mentioned in your bio that you do
give talks at conferences.
Are there any talks
online that listeners should
maybe look for?
Well, the thing here is the conferences I've been to are in Russian.
Oh.
Well, we have Russian listeners.
We do have Russian listeners, yeah.
Yes.
I've been to, well, I don't know if I should just list them, but the link, Rob, I sent you to our site.
There are videos there with me,
and those are not all of the videos, but some of them.
And one of them is in English.
It was recorded here just to show off, maybe.
Okay, I'll make sure to get those links into the show notes.
Yeah.
Okay, well, is there anything else you wanted to go over
before we let you go today, Yuri?
Well, I just want to thank you.
It was an interesting experience for me,
and it was a pleasure to meet you guys.
Okay, well, it was great having you on the show today, Gary.
Thanks for coming on.
Thanks so much for listening in as we chat about C++.
We'd love to hear what you think of the podcast.
Please let us know if we're discussing the stuff you're interested in,
or if you have a suggestion for a topic, we'd love to hear about that too.
You can email all your thoughts to feedback at cppcast.com.
We'd also appreciate if you can like CppCast on Facebook and follow
CppCast on Twitter. You can also follow me at Rob W. Irving and Jason at Lefticus on Twitter.
We'd also like to thank all our patrons who help support the show through Patreon.
If you'd like to support us on Patreon, you can do so at patreon.com slash cppcast.
And of course, you can find all that info and the show notes on the podcast website
at cppcast.com. Theme music for this episode is provided by podcastthemes.com.