Cookies   I display ads to cover the expenses. See the privacy policy for more information. You can keep or reject the ads.

Video thumbnail
- Hey good morning.
This is Applied Best Practices.
Hopefully you are in the right room.
If you are just now walking in
and I see you walking in,
there's gonna be a slide up here
telling you to move closer to the front.
If you've been to any of my talks ever,
you know that I like to interact with my audience.
So if you're back there in the back,
right now walking in, just come closer,
that'd be great.
All right, so my name is Jason Turner.
This is just my standard about me slide.
I first used C++ in about 1996
and I am host of C++ Weekly
and cohost of CppCast,
which is a podcast for C++ developers.
C++ Weekly is my YouTube channel.
Every single week I release a video on Monday
that is something random about C++
and I have been a Microsoft MVP
for my contributions in C++, since 2015.
Just for the record I am independent
available for training and contracting.
And these are my URLs
if you want to come back to that also.
As I said, don't be afraid to move closer to the front.
This is a very deep room.
Can seat like 1,300 people
and there's less than that in here right now.
So there's plenty of free spaces.
Please be sure to interrupt me and ask questions.
I have asked that the lights be a little bit dimmer
so that I can see everyone.
And just for the record,
this is approximately what my training looks like,
if you do have any interest in coming to hire me
to do training at your company.
So I have a C++ best practices class
that is a full two days.
Don't think that you're going to learn everything
about that two day class
in this one hour thing.
This is a little bit more fun,
certainly way less deep
than my two day class.
That's coming up starting tomorrow.
There's still time to sign up if you're interested.
And I got a class on constexpr coming up.
That's C++ on C in 2019 in Folkestone, England.
Is Phil here?
Well, Phil's hosting it.
It don't see him.
Also working on an idea for doing
a special three day training of that
with Matt and Charley Bay
if you're interested in that.
Before we move on
our camera man right here,
who is currently setting up,
just got married last week.
Let's congratulate him, whoo!
And took time out to still do this
before going on his honeymoon,
so pretty crazy.
(audience applauds)
all right, so best practices and me.
From 2007 to 2011 I wrote a series of blog posts
on the proper usage of language things like RAII.
I did not realize when I wrote those
first articles in 2007,
what I was getting myself into.
2015 C++ Now,
I did a talk called Thinking Portable,
the first conference talk that I did.
I make the argument in that talk
that portability as a best practice.
Every C++ project should be portable
across platforms, across compilers.
Same time I released cppbestpractices.com.
I mentioned this in one of the intro slides.
This is my forkable coding standards document.
It's on GitHub.
Go to this URL.
Basically this forwards you to GitHub.
You can go to GitHub.
You can fork it.
You can make changes that are appropriate for organization.
And then 2016 I published a learning C++
Best Practices video from O'Reilly.
And again in 2016 Practical Performance Practices.
And now I just say basically,
here are some practices that you can do
on every single line of code in C++.
It will make your code faster.
Arguably we're talking best practices here.
Then I taught my first actual best practices class
and then Practical C++ 17.
This is again, best practices.
How do we actually use C++ 17?
And then at Pacific++ 2017 I was talking about exceptions.
Where should exceptions be used appropriately.
Don't be afraid of them.
I started to realize at some point,
this has pretty much been the complete theme
of every single talk I've ever given
has something to do with best practices.
So basically what happens if I try to apply
all the things that I've been teaching
for the last 11 years, in a project?
So far, most of my material's been based
on my experience with ChaiScript.
You may have heard it mentioned.
It's a scripting engine for C++.
ChaiScript was originally created with C++ 03
in 2009 with cross compiler,
cross OS compatibility.
I have upgraded it from C++ 03 to 11 to 14
and the current master branch using C++ 17,
trying to follow my best practices.
Effort was worth it.
The code is more maintainable,
considerably faster than it used to be.
But trying to uniformly apply
all of these best practices to an aging
code base is very difficult.
Some of my early design decisions,
such as relying on shared_ptr
for reference counted objects
is really hard to get around.
That would mean fundamentally changing
the nature of my scripting engine if I did.
But just a quick poll.
Who thinks shared_ptr is something
you should use throughout your code base?
Yeah, right?
So if you started a project
before there was a unique pointer,
before there were R value references
and you used shared_ptr for keeping
track of your keep allocated things,
at least you didn't have memory leaks, probably.
Unless you accidentally got a cyclical dependency,
which I've done.
But it's really difficult to move away
from those design decisions.
So I felt like I needed a new project.
One where I could apply all of my best practices
from the start, without worrying about
breaking anyone's backward compatibility.
This is a question I've gotten asked many times.
How does one go about learning C++?
I always suggest that you need
a real project to work on.
And I know if you don't have a job
you have to pick your own project.
Specifically you want a project that sounds easy.
(audience laughs)
This is absolute key.
And it has to be something that interests you.
I will dig into this sounds easy,
throughout the course of this talk.
Projects that sound easy almost never are.
This is exactly what we want, right?
We want it to sound easy
so we get hooked before we know
just how hard the project actually is.
Okay, so I decided that I was going
to create a simple ARM emulator.
(audience laughs)
That sounds easy, right?
I'm like, well I have a cousin.
Well lots of people have cousins.
I have a cousin who, every time he learns
a new programming language,
he writes a NES emulator.
And the NES 6502 processor
which I've talked about in other talks,
really easy
and the NES hardware is not terribly complicated
until you get to all of the special things
that cartridges can do with mappers and whatever.
So he's done that a million times.
I'm like, "I'll just write an ARM emulator.
"That should be easy."
Well we'll talk more about that in a minute too.
But just out of curiosity who watches C++Weekly?
Did you see my episode on the constexpr ARM emulator?
Okay, well cool.
For those who are on YouTube watching this,
almost no one raised their hand for the second question.
Okay, so for the sake of this project
I thought let's start with strongly typed things.
I need strongly typed integers.
And I'm thinking specifically about CPU operations.
I've got Op and maybe some generic instruction.
In the actual code it's instruction
but it doesn't fit on the slide very well
so I shortened it to Op.
And then maybe some specific type of operation,
something that works with the ALU.
So I wanted this strongly typed
so that I can have more expressive codes.
So I can have a series of process functions
that do the correct thing
based on which type has passed in.
And it looks something like this.
I'm using some CRTP.
Don't worry about that right now.
Any questions about this?
So basically something like this.
I've got my strongly typed thing.
It has a data number called m_data.
This is whatever the value is in there.
It's nt 32 t or something.
Do we like this code?
Any comments?
It shouldn't be const?
Okay, you have to yell back at me.
There's a lot of people in here.
If you're in the back
you might have to sprint towards
one of the microphones if you want to yell at me.
That's why you should come closer.
Why should it be const?
- [Woman] Because you can't design to it.
- You can't design to it.
It reduces the actual real usability of this.
Putting this thing in a vector for example, is possible,
but then after that what are you going to do?
So it plausibly reduces our usability too much.
I needed an accessor.
These are my two options.
Should I return my accessed value by const ref or by value?
Who says by value?
NFO people.
Who says by const ref?
I feel like it's 50 50.
So there's no clear winner here
so I get to make the decision.
Okay, I'm returning by value.
This is intended for small, trivial types.
There's no reason to effectively
return a pointer to the thing.
These are small types.
Like I said, you went to 32 t.
These are instructions on a 32 bit ARM emulator.
So I can enforce this.
I can put in a static assert.
Make sure that only trivial types are used in this.
Then I know that this is gonna be okay.
Should my accessor be a const member function?
- [Man] Yes!
- Yes, thank you for yelling, yes.
Is it possible for my accessor to throw an exception?
- [Group] Not trivial type.
- Not for trivial types.
So it is no except, okay?
Yes Richard?
- [Richard] In a different network.
- One step at a time.
- [Man] Excuse me, can you give a definition
of what a trivial type is?
- Oh yes sorry.
A trivial type is trivially copyable,
trivially constructable,
does it require trivially default constructable?
It's a type trait.
I can't remember the exact specifics it requires
but it's definitely trivially copyable
and trivially moveable.
Effectively means we are not going to provide
our own copy constructor,
or move constructor, or copy assignment,
or move assignment, or anything like that,
and that the compiler can do
the like m copy easiest thing, yes?
- [Younger Man] Isn't there a type trait
for nothrow copy constructor
that we should use here?
- I'm okay with just asserting that the entire
thing is trivial here.
Does anyone want to disagree with me?
Because I am sure there is someone here
who knows these type traits better than I do.
Who is okay with saying, "Is trivial
"covers our bases for this?"
All right, that's like 75% of the people,
we'll go with it.
That's how you do code reviews at your company, right?
It's just voting.
(audience laughs)
So can this accessor be constexpr?
Yes, okay, so now it's constexpr.
Does Richard have anything else he wants to say?
Are you sure?
Is it an error to call this function
and not use the return value?
Is it a logical error in your code
if you call this member function
and discard the return value?
- [Group] Yeah. - yes, okay.
So now it is a no discard constexpr
no except member function.
Are we okay?
(audience laughs)
- [Man] It's a mouth full.
- It's a mouth full, yes.
Who's okay with this?
Like three people are okay with it.
(audience laughs)
There's a couple people.
Okay, since I know that not everyone votes,
who's not okay with this?
It's about the same people who said
they were okay with it.
The rest of you are waiting to make a judgment I guess.
These are our options for how we could
do the return type here.
We have our no discard const noexcept member function
and we have the option of just using
auto return type deduction, option a.
We have option b, which is using trailing return type.
And we have option c which is manually
putting the return type there in the front.
So option a full auto return type deduction, you vote.
Okay, option b.
Almost no one likes trailing return type.
Who likes option c?
Most people like option c, okay.
Just for the record
I pretty much fall in category a
as far as what looks best to me.
Because I'm like, do I care what the return type is?
If you saw the lightening talk,
if forget what it was
but it was about this kind of thing.
- [Man] Always Avoid Auto.
- Almost Always Avoid Auto, yeah.
I'm definitely not in the category
that fully agrees with that.
Let's make this slightly more complicated.
That was a trivial example.
Now we have trailing return types in group a
and we have our return types,
well not trailing return types in group b.
Does this change your opinion?
Now who says they prefer group a?
Okay, who still prefers group b?
I feel like I changed someone's mind.
There's a more even split this time.
So other people that I've talked to
who generally prefer trailing return types,
they basically point this out.
That by the time we're in keyword soup here,
it's hard to see what the name of the function is now.
So this is an argument for using trailing return types.
- [Man] Or syntax highlighting.
- Or syntax highlighting, yeah.
Well this is partially syntax highlighted, right?
The keywords are highlighted
but not in different colors.
I've had some discussion with people
that work on IDEs about the possibility
of graying out attributes.
So they're there but less like mentally,
less mental load I guess,
to look at them.
Yeah, I don't know.
You can definitely play with your ID settings.
Make sure we're on the right slide.
This is a short snippet of the actual code
that has been slightly trimmed up
to fit on the slide.
It looks like this.
I've got constexpr things.
I've got no discard,
on pretty much every non mutating member function.
So any member function that is const
and returns a value
there's nodiscard in this code.
Everything is noexcept.
Looks something like this.
I don't have any other insights or anything.
I just want to just throw that up there.
So constexpr.
What cannot be constexpr in my ARM emulator?
- [Man] Program counter.
- Program counter?
So the program counter,
let's try to go back to that.
So the pc right there, it's on line 19.
I'm incrementing the pc.
I'm setting up the pc to forward past
the memory location that I wanna
jump into in my run.
Because the pc is always four bytes ahead
of the instruction you're actually executing.
But this is a constexpr member function
that is manipulating the program counter.
The program counter itself,
as a member variable,
no we don't want that to be constexpr,
that's not static.
It's not a constant, that is.
But the rest of the system,
all of the member functions are constexpr.
So basically, there's nothing in my emulator
that can't be constexpr.
And if you think about it from a logical perspective
you think a Nintendo emulator,
or a Game Boy emulator.
You know at compile time exactly how much memory it has.
You know what it's video processors can do.
You can pass it a RAM at compile time.
You can give it anything at compile time.
You just wouldn't be able to interact
with it at compile time.
But there's really no part of this
that can't be constexpr.
So what are the downsides of constexpr?
Anyone wanna throw out any reason
why you are not currently using constexpr
in your code base?
- [Man] Compile time.
- Compile time, someone says.
Any other comments?
- [Younger Man] memory.
- Memory, compiler memory you suggest.
- [Younger Man] No muscle memory.
- Oh, muscle memory, I'm sorry.
Yes, you're just not used to typing constexpr.
I highly recommend doing conference talks
about constexpr and then it just flows
whenever you're typing C++.
Okay, so this is the real downside.
Anything that's constexpr must be in a header, effectively.
We must know it's definition at compile time
like a template,
otherwise the compiler would have no way
of executing it at compile time.
Constexpr though, being a header does not,
notice the qualifiers?
Not necessarily.
Means slow compilation times.
Compilation times generally speaking
come from very large symbol tables.
Constexpr doesn't necessarily
have to contribute to this.
Because we are not generating a bunch of new types.
We're not doing these crazy template
meta programming things with constexpr.
We don't have two pulls that have to generate
reams of code or anything.
These are just simple functions
that happen to be in a header file
with the constexpr key word in front of them.
Do you believe me?
Well I don't have a demo for this set up.
But if you go back and watch the YouTube
video where I first talk about this emulator
I can actually edit,
I can put it in compiler explorer
and I can edit it at approximately,
it can compile at approximately the speed
I can type, that is,
for the CPU emulator itself.
So it's pretty fast.
But with everything being constexpr,
I end up with my unit test looking like this.
I actually have created a static test thing here.
This is all wrapped up neatly
in Phil Nash's Catch 2 framework.
If you're not using that, I recommend it.
But basically if my unit test's compile
then I know that they succeeded.
(man applauds)
Thanks for the one hand clap.
We have full constexpr CPU emulation here.
That's not a problem.
Yes, Richard.
- [Richard] So actually then,
we can use the CPU function file
as a construct and local static?
We just have systems as one.
- Are you saying just even making
your local static functions constexpr if you can.
I mean, things that are specific
to a one translation unit,
not in the header file at all.
And just continue with that.
Yeah, I will talk a little bit more
about constexpr and some of the advantages too here.
Although I am moving too slowly.
We're going to have to speed up.
Okay, we now know provably,
that we can execute any arbitrary
code at compile time.
If we have an ARM emulator at compile time
that's Turing complete, right?
So we can do anything at compile time.
If anyone was in Hannah's talk on,
I believe, Monday, maybe it was Tuesday,
on her constexpr reg x,
then you shouldn't be really surprised by this.
Okay, what values return from main?
Hello?
- [Man] Undefined!
- Undefined, okay thank you.
I was just gonna wait until someone gave the correct answer.
This is undefined.
It is undefined to shift the same
or more than the size of the thing
you are shifting.
And this is a 32 bit int.
Well int, as far as I know is 32 bits
on every platform right now.
So you cannot shift by greater than
or equal to the number of bits
without invoking undefined behavior.
Now what happens?
- [Man] Compiler error.
- Compiler error.
You are not allowed to invoke undefined behavior
in a constexpr context.
So by using constexpr we can catch
an extra class of undefined behavior
that our compiler warnings cannot necessarily find.
Different compilers have different levels
of conformance with UB.
And portability and testing against compilers,
this goes back to my thinking portable.
More compilers better.
Then we can catch more classes or errors at compile time.
So I have an extra level of guarantee
that I have actually correctly implemented
my ARM emulator.
Because I know that I'm not invoking
undefined behavior, basically.
So what do we think now?
We have our no discard constexpr
const noexcept with trailing return type.
Any comments?
I'm looking for a specific comment.
So if someone doesn't yell it quickly
I'll just go to the next slide.
- [Man] Defaults are bad.
- The current defaults are wrong, possibly.
What if we were to reverse our defaults?
What if our functions looked like this instead?
And it was assumed to be a constexpr const
noexcept no discard function.
That'd be pretty cool.
- [Man] It'd be readable.
- It'd be readable.
We can't do that, right?
I mean it would break too much code, today, to do that.
But then we would reverse things around.
We would have to say that it does throw an exception.
It does mutate member data.
And oh, by the way it's okay to discard this return value.
It's interesting.
Mental exercise perhaps.
But this should maybe remind you
of something that's currently in the language today.
- [Man] Lambdas.
- Lambdas.
So lambdas are not noexcept or nodiscard by default
but they are const and constexpr by default.
It gets us halfway there.
And they use auto return type by default.
So if we're using a lambda
we can just put the trailing return
type if we need it.
We don't have to have that redundant auto at the front.
I'm not saying every function should
become a lambda, just for the record.
I just wanted to point this out.
That with the newer features
the standards committee is kind of
switching our level
our way of thinking,
more towards what the defaults
perhaps could have been.
All right so some of those lessons
that I learned, working on this.
It definitely takes discipline
to remember the noexcept nodiscard world.
Who said muscle memory?
You said muscle memory, Victor, yes.
But once we get in the habit of typing it
it's not that hard.
If you assume constexpr,
it's easy to stay in the constexpr world.
But you must have constexpr text
to make sure that your code is actually working
in the constexpr context, yes?
- [Man] Can you comment on true constexpr?
Once we have that?
- Oh the constexpr for Clang?
No, in general will not comment on things
that have not been approved by the committee yet.
(he laughs)
Yeah, so for the sake of the video
there is a plausible feature coming up
that would force something to be
in constexpr or compile time.
That's not relevant for this.
If my emulator only worked at compile time
then it's literally just a mental exercise
at that point.
Now we can't actually do anything fun with it.
Okay, so our constexpr catches undefined behavior.
In my opinion, working on this project,
Clang format became a necessity.
So about Clang format.
It takes discipline and you need tools
to keep you in line,
to keep all of this stuff looking good.
Did anyone see Tony Vinyard's Post Modern C++ talk?
Tony basically argues that code formatters are bad
because they take away expressiveness from our code.
We can't use formatting to express some meaning.
And I watched that talk live
and I agreed with every word he said.
And then I started using Clang format
and now I completely disagree with him
because I love it.
I am able now to just type,
let it flow,
Clang format.
Bam, everything looks nice and pretty.
And I don't have to go back and think,
"I have to reformat all this to make it look good."
I also argue that using Clang format
makes accepting patches from other people easier
because you don't have to say,
"Well this is great except you use tabs everywhere."
You can say, "Please run Clang format
"and resubmit your patch."
I also found that it was very easy
to become undisciplined
when I was working on something I didn't understand yet.
When I'm in, like, research mode.
Or if I'm using a new library for the first time,
then it seems like my brain,
whatever part of my brain that does
best practices naturally,
started to shut down because I'm doing
something that I don't understand right now.
And then I had to go back and apply my best practices.
That surprised me.
Maybe it's something you can look for
when you're working on your own code.
If you're doing something that is unknown to you
maybe the same kinds of things happen to you.
So on the note of constexpr.
And kind of on the note of what Richard said here,
I'm like, hey everything's constexpr.
And everything's in a header file basically.
And then realized, wait a minute,
there are some things that don't need
to be in header files, right?
Like I have to remind myself of that at this point.
But that's okay.
We can put things in our CBP files,
things that rely on external libraries,
code that needs dynamic memory,
things that do things that can't be done at compile time.
I keep mentioning constexpr.
That is actually not the point of this talk,
just for the record.
But I found it interesting,
the more I got into this that constexpr
appears to be the subset of C++
that people say they want.
No or minimal undefined behavior.
Exception handling can't happen.
No dynamic allocations.
We tend to use trivial types.
And with trivial types,
move semantics are almost completely irrelevant.
Because we don't have to care
about efficiently moving something
that is trivial to move.
So I also learned that build system is critical.
I already knew that.
I was surprised by something.
I'm prototyping this project.
I have no warnings.
I set up my build system and now I have
tons of warnings that I have to go and correct.
This is basically what my prototyping
command line looks like.
If I just got a single file,
I'm typing out whatever warnings
all extra shadow pedantic,
those are my defaults.
Does this look good to you?
- [Man] W error.
- The W error, well yeah okay.
I could have put W error in there.
Although I find that kind of just annoying
in prototyping mode, personally.
But that's fine.
I won't argue with you on that.
Would you add any other warnings to this?
- [Man] Lots. - What's that?
- [Man] Lots of warning.
- Lot's of warnings, okay.
This is my protyping command line.
I mentioned that I have cppbestpractices.com?
This is my recommended set of warnings
on that website.
When I set up my build system,
I had many more warnings that I wasn't thinking about.
I'm not gonna walk through all of these,
don't worry about that.
There's still one missing though.
Anyone?
Just for the record this is not
the entire set of warnings that GCC supports.
It's just a really good set of warnings
that GCC supports.
Okay, who went to Herb's talk yesterday?
- [Man] Lifetime?
- Yeah, missing W lifetime.
So, expect that I'll be updating my docs on that.
Is that what you're gonna say?
So even on a trivially sized prototype project,
warnings and type conversions
can get out of hand if you don't start
with them enabled, that's key.
Topic of warnings, I did a Twitter query a while back
and I asked people what feature
from C++ you would remove if you could?
And I got a lot of different answers
but one bubbled to the top.
- [Guy] Exceptions.
- Exceptions is what Guy says.
- [Richard] Macro.
- Macros is what Richard says.
What's that?
- [Man] Implicit array conversion.
- Implicit array conversion that's my answer.
But it's related.
The answer to bubble to the top
was implicit conversions, in general.
Implicit conversions.
And I do agree with this.
Now I will say from a teaching perspective
anyone want to guess what warning
I have the hardest time convincing
people to enable on their compiler?
W conversions.
It causes too many warnings.
Well okay, that's because we're using
too many implicit conversions really.
But it does make somethings painful.
Like this.
This is the dif.
We saw my pc plus equals whatever.
If I need to go,
I'm executing a branch instructions,
so I need to either jump forward or backward.
I've got some offset.
This is a signed int 32 t.
And I'm doing the plus four because like I said,
the pc is always four ahead of the instruction
that's currently executing.
The top line is the line of code
that I want to write.
My u int 32 t plus equals
int 32 t plus four, basically.
No, compiler was having nothing to do with this.
I was getting, the bottom line
is what I ended up implementing.
Knowing, well okay I can rely two's
compliment math here
and I'm 99% sure I'm not doing anything
that is undefined behavior at this point
because I'm converting that to an unsigned int.
And those bits, added back into the bits
that I care about will do a subtraction
if that's what needed to happen.
And it works at compile time so I'm okay with it.
But yeah, this is like,
I left this warning for two weeks
before I finally decided,
"Fine, I'll write all the static casts
"that I have to there to make it compile without a warning."
all right, what warning might we get here?
- [Man] Fall through.
- Possible fall through, right?
But why?
We have an enum class.
It has two values
and we are checking for both of those values, right?
We will get a warning.
Not all paths return a value.
So I think GCC warns and Clang doesn't
or maybe it's the other way around.
What do we do?
I have a lot to decide about what to do with this.
So let's look at this.
I've mocked this up.
I've got my two options.
I'm handling both of the options.
And we can look at compiler explorer here.
And we can see on the right hand side
that when the compiler compiles this
it basically tests to see if the input is zero,
the first value.
And if it is, let me make sure I'm reading this right.
It jumps down to label one.
It bores out so it zeroes out EDI,
which was already zero.
Anyhow, then it calls the t1 handler.
otherwise it calls the t2 handler, right?
So this is very straightforward code.
It says these are the exact two possibilities.
What happens if I add a third option?
So now I've got option one, option two, option three.
Compiler's done the exact same thing.
So if I were to pass in option three
to my process function here,
it's going to call option two.
Because otherwise it would be undefined behavior.
Because it would fall through the function
and not return a value.
So the only option that it has
is to just, it's fine.
It'll just go with the other handler.
So I can demonstrate this.
I am going to explicitly create an object
of my types enum with the value three.
Three is currently not a value that's an enumeration.
The enumeration's gonna have the values
zero and one in it for option one and option two.
So what does the compiler do?
We can look in main here.
It calls the handler for t2.
Let's pass the value three to the handler for t2.
Because otherwise it would be undefined behavior.
Is this okay?
Nah, yeah I don't know.
I'm not gonna argue whether or not
this is a correct optimization to make
but I like how clean the resulting code is, yes.
- [Man] Isn't already undefined behavior?
Because three is not in the enum type?
- Is it not already undefined behavior
because three is not in the enumeration type?
That is an excellent question that we
will discuss right now.
(audience laughs)
See the comment here.
Is this illegal in any way?
So I have a vote saying that this is illegal.
Who says it is illegal?
That's a pretty good number.
Who says it is not illegal?
It's 50 50 so we have to go the standard.
Okay, for an enumeration whose underlying type is fixed
the values of the enumeration are the values
of the underlying type.
What is the underlying type of my enum class?
It's an int.
The default underlying type of an enum class is an int.
For scoped enumeration if you're
gonna look it up in the standard.
So the underlying type is int.
So our value,
our possible values
are the entire set of integers.
So what happens if I say,
well I need to guard against this.
Because I need to know if someone
actually passed me an invalid op code.
Our code goes from trivially simple and easy to read
to something that has to actually check,
did you pass in zero, or did you pass in one,
or did you pass in something else
and now I need to call the acert?
And I have commented that this is
the mysterious reappearing warning.
Does anyone know why I have that here?
- [Man] Acert compiler.
- Acerts are compiled at release mode.
If I build this in release mode I get a warning.
If I build it in debug mode I don't get a warning.
It's very exciting that way.
I could put an abort.
(audience laughs)
I will say actually, at the moment
this is how Fyber emulator's written
because I just want it to crash
if I hit an invalid op code
that I'm not handling yet.
But I will get back to that.
I could throw, this is constexpr land, right?
We can't throw.
What about this?
What if I call some unhandled instruction handler
and then just return a default value?
I honestly don't know.
I'm trying to eliminate
all the possible warnings from my code.
And this is where I'm hitting sticking points.
Yes, Richard.
I'm sorry, the question was,
"Can it deduce what the type is there?"
That's a good question
because I'm not explicitly specifying the type
and on line 14 would it deduce
the thing that was determined by the previous?
I thought it compiled.
But this might have ended up as slide ware, basically.
I'd have to double check, yes?
Why am I not adding a default option for the switch?
Because I didn't want to add a new op code
to my set of enums
and not be warned that I wasn't handling
all of the switch cases.
So if I put a default option here
then I will never ever get a warning again,
saying that I'm not handling all the cases,
even if I go back and change and add new cases.
I don't think the default is the right answer here.
I think that makes the code too hard to maintain
for this example.
- [Man] Stood variant.
Stood variant.
See, you all are gonna derail me time wise.
Stood variant, I asked myself many questions
about stood variant.
Stood variant can be used in a constexpr context.
It has limitations though.
It cannot be reassigned in a constexpr context.
And it actually makes sense,
if you know anything about how
stood variant has to be implemented.
The other thing is that it depends
heavily on your compiler and your standard library
as to whether or not stood variant
compiles to something highly efficient,
like a switch statement or whatever.
Or if it doesn't.
I strongly considered, I might go back to variant.
But for other reasons variant is hard
for me to use in this code.
Okay, subtle rant on my warnings
back to my build system.
And I have to move a little bit faster.
I have 10 years of bad experience with CMake.
Does anyone else have 10 years
of bad experience with CMake?
CMake, today, is much better than it was 10 years ago.
You need to, at some point start a project from scratch
and try to find and follow the best practices for CMake
and you'll go, "Oh."
And it's like a light bulb moment.
CMake format exists.
It is like Clang format.
You can install it with Pip.
And it is awesome for keeping your CMake code more readable.
Package management.
I wanted to put to use package management
for the first time ever.
So I knew that I relied on SFML,
rang, and Catch 2.
And I was also considering using spdlog and format lib.
Sorry, I saw someone taking a picture.
I evaluated Buckaroo, build2, Conan.
Conan has Conan Center which is all of their vetted packages
and Conan regular which is all the other packages.
C++ Archive Network, Hunter, qpm, and vcpkg.
This is my resulting chart of the different versions
of the projects that were available.
Across the top is the current version
of each of these packages.
If you look quickly you will see
there is exactly one line
that actually has all of the current versions
of all of the packages that I wanted to use.
From my perspective, Conan was the option.
I considered going with vcpkg,
because rang, itself, is a header only library.
However, see these asterisks right here
next to SFML for 2.5.0 vcpkg?
That means there are several open issues
with it not actually working on Windows.
I'm thinking portable,
so I needed something that would be able
to use SFML on Windows with my package manager.
So I went with Conan.
While I'm working on this
I had this conversation with my cousin
who was recently on the Mozilla Rust team.
I say, "Hey, I wrote an ELF parser for my project."
Five minutes later my cousin says,
"Look what I just did with the ELF crate in Rust."
Okay, I said, "I did not even consider
"looking for an ELF parser for C++."
Like it didn't cross my mind.
I've been programming in C++ for like 20 years.
I'm not used to this mentality
of looking first, at our package managers.
So I think as we are moving forward as a community,
we have to start getting in this mindset of,
"Stop reinventing the wheel."
Start using other packages when we can.
I've only personally used package managers
in Ruby and Python,
where I didn't really care what the license was.
It was for small internal packages.
Our package managers right now, make it very easy,
plausibly, too easy to add new dependencies
without considering if we have a compatible license.
So I think we need our package managers
to start, perhaps, doing some sort
of license check for us.
I think that's quite doable.
And if you're in Patricia's talk,
making it fixable,
which was earlier in the week also.
She pointed out that our package manager
should warn or error if we install
a package with a known vulnerability.
This would be a great way for us
to stay on top of vulnerabilities
and our dependencies, if it were done.
So, as I mentioned in my previous talk by this name,
basically every program I've ever written
has had to run on multiple platforms.
If you really care, you can go back to the dot talk
but it's something like five CPU architectures
across four different operating systems.
I prefer working on Linux.
But I want to reach a wider audience
with Windows maybe Mac OS.
These are huge.
Trusting and using thread, filesystem, regex.
Now granted, filesystem didn't come
until C++ 17.
The compiler support has been a little slow.
But all the compiler's support it now.
Thread, people complain about regex.
I'm gonna ask this question again.
Who was in Hannah's talk on the compile
time regular expressions?
Yeah, so our standard library regex.
Is it outstanding?
No, probably not.
But it does exist.
It is a good utility, use it.
If you decide that you actually
need something with better run time performance later
that's an optimization.
If you're going to use your package manager
be sure to research that it actually
supports the platforms that you need it to support.
Okay, so who saw my Commodore 64 talk from 2016?
Cool, I had a ton of fun working on that project.
Just for the record, it was one of the most
fun things I've ever done.
But each line of C++ in that project
was carefully crafted to emit
the exact CPU instructions that I needed it to emit.
So that it was possible for me to translate
it to 6502 assembly.
It was extremely fragile
and a little secret is that couldn't
actually handle function calls
because it had no stack handling.
And I had no jump,
well I had jump instructions.
I had no stack handling of any kind.
Super fun to work on, taught me a lot.
I wanted to bring this fun to other people.
This is why I started working on this ARM emulator.
But without these limitations
and with the goal of making it a learning tool.
This is my basic concept.
Commodore BASIC meets C++ with Compiler Explorer.
I will demonstrate.
I think I will demonstrate right now.
So I am calling this my C++ box.
I have my static screen right here
because I haven't written any code yet,
it can't compile.
Now I've got just a couple of learning goals.
Your goal is to,
that is unfortunate.
Why does it say %s right there?
That's what I get for making a last minute
change to my code.
So my string formatting right there isn't working.
But I've got this goal.
I want to make a simple program
with a main function
that compiles and produces a binary
and returns zero.
Can you see this?
I can make it bigger if I need to.
Should I make it bigger or not?
Okay, that's good.
Okay, so I can
make my program that returns zero.
It compiled it.
It executed it.
See we no longer have the staticky screen here
because it actually ran my program
and my current goal number switched from zero to one.
Now this is a little sad since there's
only two goals here.
It's kind of hard to see this.
But my second goal is now I need to write
a main function that returns five.
Now for this one we're going to go ahead
and put this in pause mode.
And we're going to
look at the state of the system
since this is an ARM emulator
and look at our source code output here.
So now I have written this so that it says return five.
And we can see that we are currently paused
on the first line of main.
And we can look at the memory output here
and see that we are getting ready to move
five into our zero.
This is the return value
for a function returning an integer on an ARM.
And we can step through this.
We have our goal up here.
We can step through it.
The system is going to highlight
what register's changed,
what instruction we're currently on.
We're getting ready to move the link register
back into the program counter
so that we're returning from main
and we've returned five from it.
And we can see down here on the bottom,
our source code on line three,
has moved down to line three that is.
So I step down one.
I return five from main.
I got a check box.
This is the ultimate goal,
to make this fun to program in C++.
So that's my trivial learning example.
And so far that little screen on the side
doesn't seem very meaningful.
But we can do some simple graphics output
kind of things with it.
And it's all work in progress.
But you can just leave it run live
and pause and step through it at any moment
and see really what the compiler's done with
your code and what you're doing.
So that's the concept.
I have eight minutes.
So, my project,
I said at the beginning, "You want a project
"that sounds simple."
Ultimately my simple project had an ARM CPU emulator.
This is expected.
A GUI front end, expected.
A basic ELF parser, not expected.
This needs to be cross platform, right?
I'm compiling with Clang,
to ARM on Windows on my x86 box, right?
So I can compile object files
but I can't compile executables,
so I ended up having to write a simple linker
to be able to look up symbols.
And I had to partially stub out the C standard
library to get this to work
but this is the point of a project that sounds simple.
Now I learned 10 times as much as I expected to.
My goal, ultimately, with this project
is to kind of gameify learning best practices
so you get points for using consts,
using constexpr, that kind of thing.
You get negative points for extra CPU instructions,
extra executed, warnings generated, whatever.
Limitations right now,
the standard library support is limited,
no FPU support yet, that's planned.
No exceptions yet, no RTTI.
We'll see about those.
No static initialization.
That's definitely fixable.
So who thinks that I should implement this
so that you can use exceptions and RTTI in this,
learning how to program C++ tool?
Like three people.
Yeah, all right.
What's that?
Boss mode.
Actually Richard, on a serious note
I was considering that when you're on level zero, learning,
that feature's are disabled.
You can't type the template keyword.
You'd get an error in the system.
For example, force people to take it one step at a time.
And then once you've leveled up
now you're allowed to use templates.
So, Guy.
This was in Guy's talk earlier this week.
This was his experience with getting
his io2d compiling with a package manager.
So I wanted to mention this.
It's missing just the testing lines.
This is my appveyor.yml file.
I've got install Conan,
set up the package database, that I can use.
And then tell Conan to install
whatever packages are missing.
And then build a CMake.
That's it.
This is on Windows.
So our package managers have really come a long ways,
terribly impressed by this.
Who said this, this week?
Kate did.
"Is it faster to write a simpler code?
"No, no, no, no, no," I believe was her answer to herself.
There's still a lot of simplifications to do.
I tried really hard on this code base
to make it as simple, and as readable,
and as clean as possible.
But it's currently at 2500 lines of code.
I feel like that's pretty good
for an ARM emulator, ELF parser,
a simple linker, and a GUI, personally.
If you wanna check it out it's on GitHub.
It's Cpp Box is the name.
You can check it out and compile it yourself.
At the moment, paths to the compilers are hard coded.
That needs to get fixed.
So I want this project, itself,
to be an example of best practices.
So people can go back and look at it
and be like, "Okay, this guy who's teaching these things,
"actually is running a project
"that's following all these things."
Continuous integration by the way,
is absolutely key and is critical
and I'm sure you all know that.
But I didn't have my continuous integration
environment set up for the first couple months
in this project.
And then I really wished that I had done it
from the beginning.
I wanna point this out.
The pain of setting up your CI helps
you refine your build story.
Once I set up my CI,
was when I really realized that yes,
my build script can in fact be just this simple.
Okay, and that wraps us up.
This is me.
Does anyone have any questions?
We have just a few minutes left.
(audience applauds)
Run, run.
- [Man] So I wanna just highlight
a slight tension between two things you said.
It was a great talk and I really enjoyed it
and learned a lot.
It was great stuff.
But at the beginning you said
that the way we get lured into this stuff
is that it sounds easy
and it was interesting.
Is that how I remember correctly?
- It must sound interesting to you
and sound easy to you.
- [Man] Sound easy to you.
And somewhat later you said,
"We need to as a community learn to go find
"if somebody's already invented the wheel."
- Yes.
- [Man] And I totally agree with you on all of that.
But there is a tension there,
because the problem is that we need a wheel
and it sounds easy, and it's interesting.
So we start writing it.
And I'm wondering
what's the solution to that?
I'm wondering if maybe we need to train ourselves
that finding the wheel
that somebody's already written,
sounds interesting,
but we happen to know that it's almost never
all that easy.
So how can we make that sound easy?
So we actually think of doing that
rather than writing it ourselves?
- That's a great question.
But I would still,
so there's for example,
a ton of awesome graphing calculator tools out there.
I feel like a graphing calculator
is one of these things that I sometimes recommend
to people who are asking me this.
Because you're like,
"Oh, I'll just make a simple calculator."
But it very quickly can be something
actually, quite complicated.
There's the, I'm writing a tool
because I want to learn something.
And then the mentality of,
But I am also trying to get a project
done for work or whatever.
So in this case I wanted to learn
how to write an ARM emulator.
Yes, there are other ARM emulators out there.
I wanted to create this thing.
I also, at some point in my life
would like to make my own GUI toolkit from scratch.
I didn't do that.
That wasn't the point of this exercise.
I used SFML and MGUI on top of it.
Plus the awesome work from Alias
for tying MGUI and SFML together.
I stood on their shoulders for the bits
that I wasn't concerned about learning
on this project.
- [Man] I'm totally all for that.
But I'm just trying to highlight,
you said, "As a community we need
"to start to think in terms of using
"already invented wheels."
And I think one of the problems is
as soon as you get the slightest bit abstract,
as the kind of wheel you want,
it's very hard to know what it's called.
And therefore it's very hard to find out
if it already exists.
- That is true.
- [Man] I don't know if there's a solution to that.
- I don't know.
And in my mind it kind of comes back
to like the Java API,
which has everything in it, right?
And sometimes it's just a matter of reading the docs
until, "Oh look, that thing actually does exist."
Say, it's Google.
It's browsing package databases, I guess.
I don't have a good, clear answer, yes.
- [Younger Man] So I wanted to got back
and make a comment on your switch
on the enum paradigm.
- Do I need to go back there or no?
- [Younger Man] Not necessarily.
You can use your imagination.
I wanted to tell you what we currently
have in our code base
and then get your comment on if that is good
or if I'm missing something.
So I have a, and please don't throw tomatoes,
macro (he laughs) which I believe
is called default assert and fall through.
Which I encourage everyone to put
in every switch statement.
- In the switch statement or outside the switch statement?
- [Younger Man] In the switch statement.
And if possible, have a logical fall through
that will not break your program.
And basically encapsulate the assertion,
logic, and whatever run time diagnostic,
"If this happens in production
"do something to notify somebody."
If you actually hit a switch statement
that you don't have code for.
And it's sort of the fail safe approach
in the sense that
if somebody expands the enum
and doesn't catch a case
or the enum is being switched,
it will give you a run time assert
preferably in unit testing or whatnot.
And it sort of addresses all of those cases
in a standard paradigm.
And I'm wondering is that a good approach?
Am I missing something that's terrible with that?
- I would have to spend time actually
looking at what you're doing.
I feel like it doesn't,
it probably would not play nicely with constexpr
because it's almost certainly
gonna do something that the compiler would complain about.
And I don't know.
My gut is that it sounds like a big hammer approach.
But I don't know if it is or not.
Honestly, I would have to look at it more closely
to have an educated opinion, sorry.
Yes?
- [Man] So going back to slide nine two,
nine point two I think.
It's on a switch stuff.
- Oh no, everyone's gonna pick on the switch stuff.
- [Man] Or was it nine two.
Where are the switches on here?
- I don't know.
- [Man] Keep going, there we go.
Why are you not using constexpr here?
- Oh, slide ware, sorry.
Yes, that does have constexpr in the real.
That should be constexpr noexcept and other things.
Oh and nodiscard yes.
- [Man] And would it, if it had been constexpr
would it have caught any undefined
behavior in that case?
- It should have.
Constexpr will only catch undefined behavior.
Like just slapping constexpr in front of the function
won't catch undefined behavior, right?
You have to actually use that function
in a constexpr context,
in a context that would invoke undefined behavior,
for it to find the undefined behavior.
To be very specific.
- [Man] And if you go forward two slides or so,
from there, there's something.
- We're technically out of time,
just for the record.
- [Man] Actually was it back in the cert somewhere.
And I don't think I've ever seen
this paradigm of a cert
with the Bang and then the comment.
Go back one.
Oh man, I'll never find it.
That one.
- Oh, you've never seen that?
Okay so a const character literal
is going to implicitly evaluate to true.
So you invert it so it's false, yes?
- [Younger Man] I was just gonna say
you might be able to use built in unreachable.
- I've been considering that.
- [Younger Man] Or assume false on MSPC
might do the same thing.
I'm not sure.
- Actually what I was going,
what I'm considering doing is a function
that uses the standard defined no return attribute instead.
So then it's cross platform capable as well.
All right, thanks everyone.
(audience applauds)