CppCast - Packs and Pipelines
Episode Date: March 5, 2020Rob and Jason are joined by Barry Revzin. They first discuss a blog post about how to declare a class's data members and methods. They then discuss some of his contributions to C++20 including some ne...cessary fixes to spaceship operator, and some proposals he is working on for C++23 including pack declaration and a new pipe operator. News Announcing TCMalloc How I Declare my class and why Quill Asynchronous Low Latency Logging Library Are you ready for C++Now? Links P1061R1 Structured Bindings can introduce a Pack P1185R2 <=> != == P1858R1 Generalized pack declaration and usage P2011R0 A pipeline-rewrite operator Sponsors Backtrace Software Crash Management for Embedded Devices and IOT Projects Announcing Visual Studio Extension - Integrated Crash Reporting in 5 Minutes
Transcript
Discussion (0)
Episode 237 of CppCast with guest Barry Revson, recorded March 5th, 2020. Check out their new Visual Studio extension for C++ and claim your free trial at backtrace.io.
In this episode, we discuss how to declare a class.
Then we talk to Barry Revzin from Jump Trading.
Barry talks to us about his 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 all right, Rob. How are you doing?
Doing fine.
Don't really have too much news to go over myself.
You got anything to share?
Not at the moment, no.
Okay.
We will jump right into this piece of feedback then.
We got an email from Nis writing,
Dear Rob and Jason,
thanks a lot for providing a great podcast
and for keeping the high quality.
I just stumbled upon this announcement of TC Malik,
and this is actually on the AppSale blog,
which sounds quite interesting.
While reading the article,
I learned that TC Malik uses Bazel
as the official build system.
I wasn't aware of Bazel as an alternative to CMake
and can't remember if you've ever mentioned it on your show could you reach out to someone who can tell us
more about basil and tc malik um i feel like we've probably mentioned basil i'm not really sure how
popular it is as a build system yeah surely as a as a um as a news item, I would have to imagine we'd mention it at some point.
Yeah.
But we've definitely not discussed TC Malick before, which was just announced recently.
I'm very confused by this, actually.
What's confusing you?
TC Malick has been around for a long time.
Has it been?
Super long time.
I used TC Malick professionally in about 2008 and it had already
been around and was stable by then. So I'm so confused. Maybe it was a different TC Malik?
I think it's the fact that it's being integrated into Abseil. Well, I think they specifically say
that you don't have to use it with Abseil, right?
I have no idea.
I'm very confused.
I feel like I've just...
It says it's a distinct repository allowing Abseil to be used independently of TCMalloc.
Oh, they've...
Disclaimer, this is not an officially supported Google product.
Okay.
It's...
You just...
I'm sorry, but you just broke my brain by putting this on here.
It's okay.
Gperf tools, original Google performance tools.
Yeah, I see something from 2011 easily searching.
I have no idea what this is and why it's moved to this new repository
and what's up with that.
Okay, so it might not be completely new then.
Maybe it's been updated recently i don't know
i mean it does say it's got optimizations leveraging modern c++ features including
c++ 14 and 17 features so you know it must have at least been updated since 2011 agreed yeah okay
well we definitely have not uh you know talked in too much detail on the show about either of these.
We feel like I have a bit of a backlog of projects to look up and try to find guests for.
Agreed, yeah. Yeah, and if you ever have a suggestion like this, or if you're listening and you know something about TC Malick,
or I think the last suggestion we had was Fuchsia OS,
if you have a suggestion for someone who you think might be a good guest uh please let me know sometimes it's a little hard to find uh guests to talk
about these topics because uh you know there's not a whole lot of public information about them
right yeah okay well we'd love to hear your thoughts about the show you can always reach
out to us on facebook twitter email us at feedback at cpcast.com and don't forget to
leave us review on itunes or subscribe on YouTube. Joining us today is Barry Revzin. Barry is a senior C++
developer at Jump Trading in Chicago, a research and technology-driven trading firm. After programming
for many years, he got really into the nuances and intricacies of C++ by being unreasonably
active on Stack Overflow, where he is the top contributor in C++14, 17, and 20.
A lot of his C++ knowledge comes from just answering questions that he doesn't know the answer to, especially when he answers them incorrectly at first.
His C++ involvement escalated when he started attending standards committee meetings, having
written dozens of papers for C++20, and already a few hoping for C++23.
Outside of the C++ world, Barry is an obsessive swimming fan.
He writes fun data articles for SwimSWam and also does analytics for the DC Trident,
a professional swim team with multiple Olympic gold medalists, including Katie Ledecky.
Barry, welcome to the show.
Hey, great to be here.
Wow, that is a wide-ranging thing.
I'm just like, want to ask about the swimming thing.
What is SwimSwam?
Yeah, what is SwimSwam?
Yeah, Swim swim swim is just a
swimming site it covers all things swimming news related college high school world olympics
it's a it's a great source of what you want is swimming news really i'm guessing you got into
this by like being in competitive swimming at one point yeah uh so I used to swim competitively and I still do. I competed in Masters.
I was never very good
on any level
but I like the sport
and I've been an
obsessive fan of it
on the world level and I just
somehow got into
writing for
the magazine, writing for the site.
It's been a ton of fun.
Okay, well Barry, we've got a been a ton of fun. It's cool.
Okay, well, Barry, 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 other stuff you've been interested in.
This first one's a doozy, just for the record.
Yeah, this first one is a blog post from Howard Hinnant, and it's how I declare my class and why. And I guess
just to quickly summarize what he's trying to say here
is a lot of people, myself included, will usually put
public API methods at the top of a class definition.
And he's saying that's wrong, and you should be putting your private
data members on the top.
And I'm not sure if I agree with this.
What do you think, Jason?
Oh, I want to ask Barry what he thinks of this article first before I go on some rant.
Yeah, so I actually pretty much agree with the article, especially the gist of it.
I find that the number of data members a class has tends to be pretty small, right? Like most classes, probably three or less,
really most classes, like 10 or less. So the amount of space that those data members take
up is going to be pretty small. And, you know, I find that when I'm looking at class definitions,
you know, it's kind of like, either I don't care about the data members, in which case,
if they're at the top, it doesn't cost me anything to scroll through them. Or I do care about the data members, in which case we're at the bottom, it's just like an arbitrary amount of stuff that you have to look
through to find them on. And so that I do find that like putting them at the front makes it a
lot easier to really understand what's going on, if you care. And if you don't care, then it really
doesn't matter where they are anyway. So I'm the sticking point for me on this is when do I care about private data members as a user of a class?
Like in what scenarios?
Well, sometimes I want to know like what's actually going on in the class.
How is it implemented?
What are the details like that around it?
What's the storage look like?
What are the copy semantics? That kind of thing.
I mean, if all I'm looking for is what the public API looks like, then yeah, it's totally irrelevant,
so you don't care. But then in that case, it doesn't matter where the members are.
And in that case, maybe you're not even looking at the header file.
You're looking at the Doxygen output or something.
Sure. That's true.
Perhaps.
This is just for the record, it polarizing on twitter if any of you
followed that um there's lots of people complaining on both sides of the aisle here i think i don't
know i've never seen a polarizing discussion on twitter before oh right right yeah sorry my bad
it must have just been the way i interpreted it um uh okay yeah all right i accept that i don't i don't feel like i have really strong
opinion except for like what i already said like why do i care about the private implementation of
it although your argument that if you do care then it's there up front for you to see in one place
i mean i can't say for certain what i actually do in my classes i don't i'm pretty sure i do
it semi-randomly all right but if if i were actually
having the kind of focus that i wish i had then i would probably consistently put them first
that's a good argument right there like just having a plan is probably better than what 95
percent of us do it's probably true okay uh next thing we have is a new library called Quill.
And this is an asynchronous, low-latency logging library.
And it's got some pretty impressive metrics to compare itself against other popular C++ logging libraries.
But the thing I like the most about it is it's using the format library for how to log different strings and output to it.
Format all the things.
Format all the things.
Holy cow, it's faster than speed log.
Yeah.
It's a lot faster than speed log.
Yeah, but I think the way that that works
is that it does its formatting.
So speed log formats up front, right?
Yes.
And then it pushes that formatted
thing wherever, versus I think, in reading this, like quill stashes, all the things that needs to
format and does them later. Yeah, yeah, asynchronous, it sounds like it's running in a thread or
yeah, something, which then did raise questions for me, which I would love to have both of your
opinions on, personally, because whenever I, you know, make some sort of tweet or
argument about stop using end line, I get the inevitable backlash from people that say, oh,
but I need that flush because if I'm using IO streams, I want that to be written in case my
program crashes. Okay, fine. But this being an asynchronous library, asynchronous logging
library, what happens if your program
crashes do you not get that last bit of logging data that might have been critical do we care
does anyone have an opinion i mean i haven't looked into the library to determine if you would
but i would imagine you definitely don't want to lose that logging output when your program crashes
because it might tell you why you're crashing but if it's asynchronous there has to be some window that you're going to lose data right yeah
i mean that's inherent in the problem right yeah right so then would would you use this logging
library yeah i mean is that specific to this one or or any other kind of asynchronous logging
library oh yeah just in general would you use an asynchronous logging library i have actually
written asynchronous logging code before and for me it worked so just for the record i'm not trying
to bash on this library i'm just you know just trying to feel out the opinions yeah i mean i
would say so because you know as you can kind of see here uh it's a pretty significant cost of doing
your formatting in line right and if and if that, that's probably not a cost you want to pay.
Yeah.
Yeah, so if it's something
that's generating just a ton of data,
like, I don't know, a game engine or something,
that you want to log all these things.
Yeah.
Okay.
And then the last thing we have
is a post from John Kalb
on his website, slash slash info,
and it's, are you ready for C++ now,
which is coming up pretty soon.
And in this post,
he's basically putting out his opinion that even though some people think this
conference is very much aimed at experts,
you should not feel intimidated and afraid to attend C++ now.
If you are interested in seeing what's happening in C++.
Are you going to be going, Barry?
I've never actually been to C++ Now.
I've had a lot of people try to convince me that I should go, and I'm still not going
this year, but maybe someday I will actually go.
It is a great conference, and definitely, just to agree with John, you shouldn't feel
intimidated to go.
Everyone's friendly. I feel like I have, no matter how much I've learned, I've always been in the
like 50% of what's going on. I fully understand. And 50%, I don't. I don't know how that keeps
happening year after year. But well, John has a good point in here that like if you attend a conference and
you're not surprised or blown away by some of the content you see then what's kind of the point of
you even attending right i will not be making it this year though that is official at this point i
have training that will be overlapping with it okay okay so barry do you want to tell us a little
bit more about um the work you're doing at Jump Trading?
I mean, is there more you can tell us about it?
I mean, there isn't really too much I can talk about besides just to say, like, I mean, I find the work really interesting.
It's a very complex problem space.
The problems are really challenging.
So the work's a lot of fun, even if I can't get into too much specifics.
So you have to do lots of really fast things.
Yeah.
Okay.
Okay, that's fair.
Do you want to tell us a little bit more then about how you got involved in the C++ Committee?
Yeah, I mean, I just kind of fell into it.
I just decided to show up to the Olu meeting in 2016.
I don't really know anything about it too much.
And then I just kind of started going regularly after that.
It's a really kind of interesting and challenging place to be.
You kind of learn that once you start going to these things and getting involved in what the discussions around proposals are like,
you kind of find it's fundamentally like a people problem, right?
Like any given problem, there isn't necessarily the one obvious solution.
And you have a lot of issues that you have to deal with, right?
Like it's an old language.
There's a lot of code that exists in it.
And you can't just go breaking things for convenience.
And so it's a lot of things that are just really hard
to try to introduce as a result of that.
But it's fun. I mean, I really enjoy it personally. So that's that. But, you know, it's fun.
I mean, I really enjoy it personally.
Sorry.
Go ahead, Jason.
No, go ahead, Rob.
So you said you first went in the Ulu meeting,
which I believe was when C++17 was getting finalized.
Did you have any papers that you were able to get accepted
during the C++20 timeframe?
So I don't have anything in 17.
I do have a lot of things in 20.
Okay. What were some of the papers you got into 20?
So I guess the ones I'm most associated with lately
are all the spaceship-related papers.
So a lot of changes to the way the spaceship operator works,
especially with regards to quality. We kind of split those up after
Spaceship first was adopted, and then plumbing it through
the whole library and doing all the work there. So for our listeners who don't know,
could you give them an overview as to what Spaceship is?
What is the point? Yeah, so what the Spaceship Operator does is it's a
three-way comparison. So on C c++ historical we've kind of had equality and and less than as the two primary
operators and both of them just say you know true or false right you get like that one bit of
information um and and what the what the three-way comparison operator does is it basically says
it gives you like the full uh range of the solution right like you compare two things
and you get if if it's less than greater than or equal to as as one operation um and it also has a
way of expressing a partial ordering uh so for a lot of operations you know you might not have
a trichotomy there so you you can get the result that uh these two objects are not ordered with
respect to each other um and so uh the other relational operators uh can
be trivially rewritten in terms of a through a comparison right so so a is less than b is the
same thing as saying that the ordering of a and b is less than right um and and that rewrite isn't
always doable in the other direction right one of the one of the problems is uh i mean it's easy to
say that a greater than b is the same as b is less than a uh but you have this problem with less than or equal to in that uh there are really two ways
that you could write less than or equal to you can say a is less than or equal to b means not
b is less than a uh which is a valid transformation if you have a total ordering uh but if you don't
have a total ordering then that's not a valid transformation, and you might get the wrong answer.
So the only way to really get the correct answer is to write it as A is less than B or A equal equal B.
But that's twice as expensive for a lot of types for which you do have a total order.
So you kind of have to make a choice.
And the standard library kind of makes two different choices depending on the types.
For most types, it does write less than or equal to in terms of not less than uh so like that's how pair works and tuple
and vector and all those types um but for optional and variant uh we went with the correctness route
and instead uh less than or equal to just forwards down to the types less than or equal to
and i think i think this is like tony van eerd's paper uh where
he pointed out that if you compare floats you get one answer but if you compare optional floats that
contain the same values you would get a different answer um because because floats are not totally
ordered uh and and so so the goal of spaceship is a to make it to that you know all the all the
operators all the relational operators can be correct, and you only have to write Spaceship to get that to work for you,
which is pretty nice.
Because Spaceship itself is also much easier to implement than LessThan,
just because you only have to do the one thing,
and it's easier to reason about.
It's interesting.
I feel like most people talk about it in terms of convenience
and that it gives us this way of only implementing one thing,
and I don't think
i've ever heard anyone talk about it in terms of correctness so i appreciate that yeah i mean
convenience is obviously like a very big thing too and the fact that you can just default spaceship
and you don't even have to write any code if all you want is just a simple member wise comparison
uh i mean that's that's super nice so if we whatever, what does that look like? That's operator less than equal greater than something equals default?
Yeah.
So, you know, auto operator less than equals greater than open paren type const ref close
paren const equals default.
Okay.
And then it's important to have both const as well.
So how does that work from like an implementation standpoint if the underlying types in my struct
aren't greater than comparable for whatever reason?
And then is the explicitly defaulted one
take that into account?
Yeah, so what defaulted Spaceship does
is it'll effectively, you know,
in the same way that a defaulted copy constructor
will try to copy construct all of its elements,
defaulted Spaceship will use Spaceship on all of its elements if you use auto as the return type.
And so if those types don't have Spaceship, then that operator becomes deleted, defined as deleted.
So what were some of the changes that had to be made to spaceship operator from the kind of
initial proposal from herb so so the big kind of fundamental problem is that so herbs herbs paper
was that this is like your your one-stop shop for all things comparisons you write you write one
operator and you get you get everything um so that's obviously like a huge convenience win
and so even like equals so if i write like a equal equal b that will invoke spaceship and check that the result is is equals uh but there's a lot of types for which
uh you want to use a different algorithm for equality than for ordering um so if you think
about strings right like i know right off the back that two strings of differing size are not equal
right um i can easily short circuit on the size there uh but i don't know right off
the back what the ordering is right because like a shorter string could be greater or less than a
longer string so if i if you rely on spaceship to get your quality you're severely pessimizing
things like string equality right which is which you might think is fine you will just write like
a custom equals for your string um and and not equals and and that works
but but this has compounding effects when you start wrapping these types because if now i have
a struct that has a string member and i just default spaceship i just got a slow equals
and and so it's it's basically uh not really feasible to be able to write like fast code
because like you just have to be constantly
aware of anytime you write this that like it's it's not even does my type have to have a fast
equals it's does any type that my type depends on anywhere down in its hierarchy have a different
equals uh so so what we did was we we split the two completely so now all the relational operators
are like the secondary operators of spaceship and not equals is just a secondary operator of equals um and equal equal and spaceship are the
two primaries and and otherwise those those are just like independent things um and and part of
the reason we did this is is you know we looked at other languages and like this is the model that
a lot of other languages use as well is that you have equality and you have ordering and they're
like just separate things okay that sounds like it makes sense yeah what other languages were
you looking at that had similar constructs uh d rust swift kotlin uh haskell i believe good old
haskell yeah so is there anything else then inhip that had to be addressed besides the equality comparison?
Yeah, so one of the problems there is, you know, when I wrote that paper, which is P1185, if anyone's curious,
you know, what I did was, well, you know, Spaceship has this nice feature where it's symmetric and reversible.
So, you know, if you have like two different types, T and U, and you write like one Sp that can with a t on the left hand side and the u on the right hand side you know i can
write t is less than u or i can write u is less than t uh and all of those work and find your one
operator uh and you don't have to write like all these permutations of things which is super nice
um so so i approach it as you know it's really important for if we're going to
have these two primary comparison operators that they have the same rules so we're like sure let's
just have the same rules for equal equal um and let's make equal equal reversible and symmetric
so that if you want to compare a t and a u you can just write one operator equals that takes a t and
u uh you don't have to write that that in both directions and then the not equal in both directions as well, right?
So you can write one equality instead of four operators.
The problem is that that ends up actually
just breaking a lot of code.
And that's actually, you know,
unfortunately that's still kind of
the status quo right now.
So when I mentioned that,
it's important to write the two
consts uh when you when you write your comparisons so there's there's a lot of code that exists today
that uh has a non-const operator equal that takes a const object as a as its argument uh if you're
saying uh operator equal equals just to be clear yeah operator equal equal all right just to make
sure okay as a member as a member function that is not const yes i have recently run into code that was incorrectly written like that so
in z plus or 17 i mean you could say that that's like not the correct way to write that but if all
of your objects just happen to be non-const that that's fine like that'll never it'll work it'll
do the right thing uh yeah you'll never know this. The problem is with the 20 rules,
because we have these additional
reversed candidates, that operator
becomes ambiguous with itself.
Because we consider the reversed version
of that as a candidate, and we have
one
parameter has a
one of them has a
cons qualification in one parameter, and the other one
has a cons qualification in the other parameter, so neither is better
than the other, and we're stuck.
Right.
Yeah, so that sucks. So right now,
Clang just implements that as a warning
and ignores that rule,
and just says that you should fix this by adding this const,
but I know what you mean, so I'll let it work anyway.
I think I'm okay with it,
just breaking the code personally,
but, you know,
I know that's not the decisions
that they can make.
Yeah, so this is why I said
that it's hard to add new things sometimes.
Right.
I want to interrupt the discussion
for just a moment
to bring you a word from our sponsors.
Backtrace is the only cross-platform
crash and exception reporting solution
that automates all the manual work
needed to capture, symbolicate, dedupe, classify, prioritize, and investigate crashes in one interface.
Backtrace customers reduce engineering team time spent on figuring out what crashed, why, and whether it even matters by half or more.
At the time of error, Backtrace jumps into action, capturing detailed dumps of app environmental state. Thank you. companies like fastly amazon and comcast use backtrace to improve software stability it's free to try minutes to set up with no commitment necessary
check them out at backtrace.io cppcast
well you also said that you are working on a couple of papers for c++ 23 and i saw that there
were uh some from the prog mailing i think with your name on it. Do you want to tell us about what you're working on now?
Yeah, so I had two kind of big language features that I started pushing in Prague.
One about just letting you use packs everywhere and making them more convenient.
And one that I co-wrote with Vector Bull.
What's his real name? colby colby pike uh i was gonna say i have no idea actually uh i just yeah i just always call
him vector ball and uh about a pipeline rewrite operator um those are those are those like very
different kinds of things with very different magnitudes so i don't know which one uh why
just tell us more about the packs to start with? Sure.
So C++11 gave us variadic templates, right?
And there are a few very specific places where you can declare packs.
Basically, like function parameters and template parameters, effectively.
And we also, at the same time, got std tuple.
And std tuple and a function parameter pack are very kind of similar things, right?
They're both, you know, heterogeneous collections of objects.
And there are some things that are a lot easier to do with a tuple.
There are some things that are a lot easier to do with a pack.
But converting a pack to a tuple is extremely easy, right? Like as soon as you understand how variadic templates work, it's very easy to implement make tuple, right?
Maybe not std make tuple because it does
this extra reference wrapper unwrapping
stuff. But if you ignore that part,
that's a very easy function to
implement. Versus going from a tuple
to a pack, if what you need
to do is perform operations
on its constituents, that's really hard.
And not only is it hard to implement,
but it's also really hard to use.
And we finally got std apply in c++ 17 um which takes a function object as the first parameter but you know
typically you're you're gonna if you have like a function template or like an overload set you
can't use that anyway so you have to resort to using a lambda uh you know that takes uh a parameter
pack itself uh and you know either way you have to introduce
this extra scope um so it's it's tedious and uh all this stuff is kind of like hard to use
um and on top of that tuple itself is is pretty difficult to implement and it's very expensive
to compile right so if you look at uh you know any kind of metaprogramming benchmark so you know
if anyone whenever anyone writes like a new library and they're demonstrating how fast it is to compile,
their baseline comparison is always,
this is what it looks like with std tuple.
Well, that doesn't work at all,
and it just falls over at 200 elements,
and here's what it works with my way.
And I think that's a really bad state of affairs
on a number of levels.
And so what this paper does is it tries to just, A, let you use packs in more places
and make them much more convenient to use everywhere.
So on the first level, just let me declare a member pack,
which makes implementing tuple just be like,
oh, okay, I just have a member of type T dot dot dot elements, and then
I'm basically done, right? So that's
become something that's very easy to implement, and that's something
that would be very cheap to compile
as well. Like, I don't have this
inheritance tree that I have
to deal with.
And I would argue that that's pretty easy
to understand, too. Like, once you understand how
variadic templates work in general, like,
there's no surprises there. A cycling step is indexing into packs this this is something that
people always kind of like ask about how to do like i have a pack how do i get the second element
and the answer the answer is what like stick it in a tuple and use tuple element or like
or but no but that's expensive to compile so stick this into this cheaper implementation of tuple
that has a cheaper implementation of tuple element.
These are not really good answers to that.
So the paper also just adds an indexing operation into a pack that is just a direct thing.
And the most aggressive part of the paper is directly unpacking. So instead of doing std apply,
just a mechanism that you could take a tuple
and splat it out into a pack directly.
And so you just get as a pack all of its constituents.
And so std apply first just becomes very trivial to implement,
but also completely obsolete since why would you ever use it?
And the whole thing is it makes tuples and really tuple like types just like structs with public members as well um much
much easier to use does it use the uh is it specific to tuple or does it use the um uh tuple
size tuple element whatever hooks that are used for also um structured bindings structured bindings
yeah yeah so it's built on the whole thing is built on top of structured bindings. Yeah, so it's built on
the whole thing is built on top of structured bindings.
So like unpacking an object
goes through the structured bindings hooks to figure
out how to unpack it.
And there's also, there's one part of the paper
that in Prague people asked to split out
which was
I suggested like a minor simplification
of the like tuple protocol
since you know if we you can imagine if we could declare I suggested a minor simplification of the tuple protocol.
You can imagine if we could declare an alias pack, if that's a thing.
Not just an alias type, but a pack of types.
Then you can declare an alias pack named tuple elements.
That is, your pack of elements.
And if you have that as a member, then you don't need tuple size and tuple elements.
This would be your one-stop shop for all of that right um so so you can reduce unpacking the tuple from
two n plus one template instantiations to just uh and okay which is which is also you know a lot
cheaper so how does this interact with or replace the like four dot dot dot kind of um proposals that we've seen
proposed a while ago i think have been pushed to 2023 also yeah so the expansion statements got
pushed um they didn't quite finish the the wording in time and there are a few issues that weren't
quite done but that's certainly going to be in the 23 time frame okay um and i think i think these
things just work very well together right so expansion statements is all about like iterating over a thing and and this is uh you know give just
gives you access to all the things at once um i think they're just they're they're fairly
complementary i'm thinking like uh the history of this we got variadic templates in c++ 11 and then in 17 we got variadic using and fold expressions
yep and then in 20 uh variadic expansions and lambda captures uh yep that was my paper as well
oh that was your oh very good i thought someone else i knew claimed responsibility for that one. Yes, okay.
So is this the end?
Like, have you filled all of the gaps now, or what?
Yeah, I mean, I think this would kind of be, like,
the end all of things you could do with packs.
I mean, this paper is roughly,
you can declare packs anywhere that you could declare anything,
and you can unpack them directly anywhere where you can unpack anything.
I don't know that there's any other place to go, at least not that I can think of.
Well, okay, so there is one thing, I guess.
So functions returning a pack, this was something that I had in the first draft of the paper that I removed in the second draft.
Okay. of the paper um that i removed in the second draft okay uh and i that's actually kind of
interesting to go through um but probably kind of hard to to discuss but there's a there's a
section in the in the paper that goes through i removed it and and fundamentally it's like there's
two different models you can take um for what it means to have a function returning a pack
uh you can say that this is like syntax sugar for returning like a language tuple
right so it's so it's one function
that returns all of these things but there's no like overarching object it's just like a pack
um there's another model that is uh this is kind of like a pack of functions in which each one is
invoked one by one and there's uh there's there's a lot of interesting benefits of that second model, which is like, well, if I just want the second element of this pack,
do I have to, like, you know, in their initial model approach,
like, that's not a very efficient operation
because you have to get everything and then drop everything else.
Right.
But on the other hand, the pack of functions model is I think extremely confusing
because you look in Soros
and you have one function body
but then when you invoke this thing as a pack
it gets invoked n times which I think
has the potential to be
pretty surprising
so I decided that
just not to pursue
that direction at all
I don't think there's a good
answer so i'm not gonna try to pick one of them so how was this uh paper received at prague uh so
it was it went to the uh evolution incubator um and it was it was pretty well pretty well
achieved there uh so james two tom pointed out one thing um which was that there's uh ambiguity uh in the in the syntax um
uh so of course there is of course right of course there's always an ambiguity um so actually this
one this one's kind of fine so if you have like t's dot dot dot open bracket one close bracket
that was the syntax i have for indexing into a pack uh so that syntax is technically valid uh c++ today uh because the bracket one could be an
array declarator so that could be uh you know a pack of arrays where basically just ignore the
the index entirely and it's you know this decays down into like a pack of pointers. Okay. Only Clang actually
even allows you to write that.
GCC and Microsoft don't.
Saying that
we're going to say that this syntax
means this new pack indexing
thing rather than a pack of arrays
thing
seems fine.
The only code I could find that uses it were
compiler unit tests in Clang
because it's the only one that supports it.
Yeah. Sounds like
you're fine then. Yeah, I think
we're okay on that part. So next up
is Evolution there. That's interesting.
The only code that uses it is
compiler unit tests. Huh.
I mean, it's like a weird thing to want to write
because you need to declare like a pack of array,
which is weird because that's a pointer
and you can already declare pointers.
And you need to not name the parameter
because if you stuck a name in,
the name would go between the ellipsis and the bracket
and then it wouldn't be ambiguous anymore.
So it's a very specific kind of thing
that you really have no reason to write
to begin with right so that one that one's not a huge concern for me okay no other i'm sorry what
what was the uh other paper that you said you were proposing at prog the other big one uh see that
one's um so uh the pipeline rewrite operator uh so this is kind of interesting because it's, as far as the actual proposal goes,
it's quite small.
And I've actually implemented it in Clang.
And I've never implemented anything in Clang before.
And this didn't even take me that long.
Like it's a very small kind of thing.
And so what it is, is, you know,
if you're familiar with ranges,
so what ranges introduces is this pipe syntax.
So you have x's
pipe filter f.
And that
gives you a filter view
that only gives you the elements
that that product
applies to. And that's a nice way
of writing it because we write the range on the
left and we can have this
chain of operations that we apply the range on the left and then we can have this chain of operations that we
apply to it on the right.
And the way that works is
that filter is
an object that has to have
two overloads. It has one
that takes a range and the predicate that gives you a filter
view. It has another that
has a predicate that returns something that's
left-pipable with a range.
And this is
a little bit tedious to implement,
it's a little bit tedious to compile, but it's not
a huge amount.
It's a handful
of lines per algorithm that you need
to do this for,
which the standard library has hundreds of algorithms,
so a few lines per algorithm certainly adds up
over time.
But the thing is, this is stuff you have to write. It's's a lot of boilerplate it's a lot of overhead um and so one
of the things that i would like to do is i'd like all the standard library algorithms to be pipable
like that uh and right now it's just the range view adapters so you know you know i have a choice
of like well you know am i going to show up to the to the committee with a paper saying that like i
want to pipe support for these you know 100 plus algorithms um that's a that's a lot of work uh but even even if you set a set aside the all the all the boiler
plate aspects of this um some of these are just like fundamentally ambiguous and you you can't
even support that way uh because in order for the pipes and text to work you have to be able to
distinguish between the cases where you have a full call like filter range comma predicate and a partial call
like filter predicate okay um and if you you can imagine like in ranges specifically uh we always
have a range the first argument you know so what if what if uh your your algorithm can take a range
as the second argument as well how do you differentiate between those cases uh so like a couple examples are like zip
right so so zip a is a totally valid thing i'm just taking a single range and zipping it i mean
it's not super useful but it's certainly like a valid thing um so like how do i make zip a comma b
and a pipe zip b both work okay and and you can't uh so like So like RangeV3 makes the choice
of only allowing...
You can't pipe into Zip in RangeV3.
Just like you can't pipe into Concat.
And this is a choice
that you have to make. You have to go through every algorithm
and try to figure out if it has
ambiguity and if it does, how to address the ambiguity.
Join has another
kind of interesting one where it's like
if you want to have a
delimiter and your delimiter is something that could hypothetically be joinable uh you have to
figure out what what to do with that right and so range v3 just names those things differently
uh and accumulate as far as all of them so has a similar problem right like right now we don't
default all the other arguments but like hypothetically we could um and if we did then accumulate range uh could itself be uh ambiguous is that am i accumulating this range or is this
like the initial value of something that i'm going to accumulate later so so fundamentally we have
this you know these issues where i like oh you know this is really nice syntax it's really easy
to read but it's a lot of boilerplate to support and then we can't even support it for every
algorithm because we have
ambiguities. So what the
provolsel is, is just like a new operator
in a language that's not overloadable.
So you can't, no shenanigans.
And it's
spelled pipe greater than.
Pipe greater than, okay.
It looks like an arrow.
Yeah, it looks like an arrow.
And the right-hand side of that looks like a function call.
So you have, like, x pipe greater than f open paren close paren.
And what that means is that means exactly we take the thing on the left
and we stick that into the first argument of the thing on the right.
And then what is the result?
The result is the result of that call expression.
So in that example, so x pipeline greater than f open paren close paren
evaluates exactly as f of x.
Okay.
There's no intermediate f open paren close paren expression at all.
Okay.
So if f takes more than one parameter, it's not going to return you a careed function or something like that.
Right, exactly.
So if you did like x pipeline f of y, that's f of x comma y.
And so that's it it that's basically the whole
that's the whole paper and so what this lets you do is uh so all the range view adapters just like
work directly you just use um pipeline instead of pipe um because they already support the total
call syntax but then for free you get all the other range adapters that don't have pipeline
support like zip and concat because there's no ambiguity issue, and all the standard library algorithms for free
with no code changes necessary,
and everything that has nothing to do with ranges everywhere else
that you might have wanted to do this.
So there's an example in the paper.
Eric and David Holman gave this talk
about the new sender-receiver model at CppCon,
and they had this example where they just had an algorithm and they had a few then calls
and then they block-weighted for it. And so the paper has an example where
I rewrite that using pipeline. And it's just like this nice linear
flow of operations. Staggeringly easy to read. Again, no work.
It's not like I need to be like, hey, Eric, I know you have all this free time with not
dealing with other sender-receiver issues.
Can you also add this extra pipe hook?
And no, no one has to do anything.
So what if you pass it a member function?
I mean, what if the member function is on the thing on the right-hand side?
Is that the this pointer then?
Or does it not compile?
So one of the things that we still have to discuss is the way that we picked the grammar
is that the thing on the right-hand side
is primary expression.
So if you want to invoke a member function,
if you want to pass it to vector.pushback or something,
you have to parenthesize that right now.
It has to be like open paren v.pushback close paren,
open paren close paren.
So that's not where we want to go like we don't we definitely want you to to call a member function there um
that that'll work fine okay so did the committee have a chance to review this paper at prog
yeah so that also went to the incubator um and the the grammar issue is one of the things brought up
um another thing that brought up uh that i really hadn't thought about was precedence.
So where in the precedence scheme does pipeline fit with other operators?
So the way that Colby and I picked it was that since it kind of matches dot and arrow pretty closely,
that we just stuck it in the same world as those.
They behave exactly the same way.
So if you have like a call chain, you can just interleave dot and pipeline and arrow, that we just stuck it in the same the same world as those they behave exactly the same way um so
if you have like a call chain you can just interleave dot and pipeline and arrow and that
still reads left to right um but there were there were a few questions um from from several people
about like that we should consider making the precedence lower um so that uh apparently it's
more common in in other places uh to to to want to avoid having to parenthesize things all the time.
And so that's something that I've not taken the time to explore yet, is what the precedence is.
That is an interesting one.
So I was thinking about the, like, I don't know, I feel like there's some sort of implication here that you explicitly say it will not be overloadable.
It's the only other operator that's not overloadable, dot.
Scope isn't overloadable?
Oh, I don't think of it as an operator.
Okay, yeah, yeah.
I believe there's one more.
It's like arrow star.
No, arrow star is overloadable.
Yeah, that's overloadable.
There's a list of non-overloadable operators.
Yeah, but it's short.
Yes, it's very short.
Interesting.
Do you want to tell us a little bit more about uh your obsession with the stack overflow how much time
you spend uh answering c++ questions there uh too too much uh i think that's the that's the only
correct answer uh yeah no i just i mean i'm i'm on there you know daily seeing what the new uh
lately i pretty much only look at the C++17 and C++20 questions.
And, you know, see, you know, I try to answer all the interesting ones.
You know, with my involvement in Spaceship,
I'm always annoyed when there's a comparison question, someone beats me to it.
It's like, come on.
But, no, it's great because, you know,
one, it's a good way to, like, give information back to Cause, uh, you know, I follow this stuff a lot more closely
than most people and, and people do ask, you know, very interesting questions. Uh, and,
and some of them are like, Oh, you know, this, this thing is actually really hard to do,
and maybe it should be easier to do. And, or, you know, a lot, I find a lot of people ask the
same kinds of questions. So this is kind of how I gauge things that are generally confusing versus, you know, oh, this is like, you know,
this is some something one person asked one time and not this is a question I see four times a week,
right? It's a different kind of thing. So I don't know, I'm afraid this question will sound silly,
but I'm actually very curious where that fits on the like resume like if if you're like number one c++ stack
overflow answer is that like resume cred or do you know i mean i would i would probably put it
on on my resume i have seen people post stack overflow profiles on resumes yeah i think i mean
a few years ago well now at this point a long time ago i had mine on there but i haven't been on stack
overflow and to answer a question in probably eight or nine years or something so i just don't think about it uh
does that sound right how old is stack overflow now it's all i think 2008 maybe yeah okay so i'm
not crazy yeah so uh yeah so i mean but there are certainly i would say probably a large group of
people who know you because of stack overflow yeah probably i'm certainly on there enough that uh i'm ubiquitous i mean it is a it is a good way to demonstrate competence right like
if you if you write a lot of answers and you write like high quality answers that is like a really
easy way for you know someone to look and see that oh yeah you know this is a really good answer you
really understand the topic right you know like i don't you know i can save you know that all that you know interview
process because well this is good demonstration of that why are you asking me these questions
just look at my stack over it it's shorter i mean one of the one of the things that answering a lot
of questions does is uh it it helps me remember where to go to look for answers really quickly
um so so in particular like i know where a lot of things are in the standard
just because I've had to look them up so many times before
that I just know that, oh, you know, I see this question about, like,
you know, GCC is accepting my code, but Clang is giving me the error.
Who's right?
And I'm just being like, oh, yeah, no, that's temp.res8.
They're both fine, right?
It's just like, you know, I know where these things are enough.
And that's really helpful, you know,
especially when, you know, it comes time to writing,
like, core wording for my papers.
It's like, okay, I know I've seen enough of this.
And that's why.
So we've already established that you can't really talk a whole lot
about what you do in your day job.
But if you can, just out of curiosity,
your papers, are they mostly driven by what you see on stack overflow what you experience at work or your
personal interest uh kind of kind of a mix of like what i see on stack overflow and and what i what i
do in my day job um if there's like if there's like an overarching theme to to all the things
that i've been proposing it's like i can do all these things and i have done all these things but
these things are way too difficult and i don't actually these things and i have done all these things but these things are
way too difficult and i don't actually like why am i spending all this time doing this kind of
thing i want this to be easier for for everyone right all right so like so like i've implemented
optional and i've implemented a trivial propagating optional and and for anyone's
done it like that's a that's a lot of work it's a lot of work. It's a lot of code. And it's just silly.
So one of the things I pushed for 20 was to make it easier to propagate triviality for types like optional.
Because I've done this, and it's unreasonably difficult.
And that's not beneficial.
Was that approved for 20?
Yep.
Yep, that's in 20.
What does that look like? Is that some feature that I missed? Core language feature? Library feature?
I consider it as fixing a core language defect, kind of.
So if you have a constrained defaulted copy constructor...
Copy constructor, okay.
And you have a constrained non-defaulted copy constructor, right?
So optional, for instance, would have a constrained copy constructor that's non-defaulted and an even more constrained copy constructor that is defaulted.
And the rule is that still counts as being trivially copyable.
So the rule used to be that we look at every copy constructor, which would consider both of those.
And every copy constructor has to be trivial or deleted.
And the new rule is that we only consider the most constrained one.
Okay.
I think I saw that wording, but I could not.
I didn't.
Okay.
Okay.
And is this also a utility that applies for trivial destructibility as well,
which comes up as optional?
Yep.
All the special
members right so so it's the small like core wording language rule change um that fundamentally
means like what you would expect it to mean right like you know i just it's it's only the one that
would actually be invoked that matters for triviality interesting and and so what this
means is like i can implement optional-compliant optional without any base classes at all, which is nice.
It saves an enormous amount of lines of code
and is staggeringly more readable as a result.
So this is if I were to have a concepts constraint on my destructor
versus one that's not constrained, it will do the right thing.
Yep.
I have got to play with that
yeah so for optional specifically you'd have a normal destructor that is like you know if engaged
explicitly invoke the destructor of the member and then you'd have a another destructor that
is requires trivially destructible t equals default right and that's it that actually
answers several questions that i had recently
about i i guess wording that you added because i'm like how do you write more than one copy
constructor now i understand okay yeah and now we can have more than one destructor i i sorry i'm
excited about that thank you for your contribution to the committee you're welcome i'm also just for
the record excited about being
able to put variatic expansions in more places because i totally see the utility for that as
well okay well it's been great having you on the show today barry now it's been great being here
yeah thanks for coming on i was just making a note for a future c++ weekly episode
it's not going to be on anyone's top 20 list of C++20 features,
but it's definitely handy.
I know. It is very interesting, actually.
I think I can easily see people who have not appreciated what you did,
like me just now having their minds go like this.
So, yeah, thanks.
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 was provided by podcastthemes.com.