CppCon 2019: David Stone - Removing Metaprogramming From C++, Part 1 of N: constexpr Function Params

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

absolutely incredible proposal! hope you will make it into C++23

👍︎︎ 17 👤︎︎ u/innochenti 📅︎︎ Oct 11 2019 🗫︎ replies

One thing seems silly to me.

If you know you are in a constexpr context with if(std::is_constant_evalutated()), or you are in a consteval function, then you know all of the arguments that got you there are compile time constants. Surely the compiler should permit you to use these arguments as if they are constexpr.

Essentially, in all consteval functions, all parameters should also be assumed to be consteval, without having to be declared as such. Inside the scope of the if block of if(std::is_constant_evalutated()), all parameters should be assumed to be consteval, without them having to be declared as such.

You already are using this logic with the proposed is_constant_expression(variable). Technically some variable that may be runtime cannot be assumed to be compile time unless checked by an if block, just as with what I propose with normal parameters to constexpr functions inside an is_constant_evaluated block.

Also, I don't think we necessarily need to break non-parametric constexpr variables by making them maybe constexpr. We can still have constexpr parameters being maybe constexpr (since it is part of a function signature), and consteval parameters being definitely constexpr. There is always the possibility in future to relax constexpr variables to being maybe constexpr outside a function parameter context, but it's definitely a breaking change to do it now purely for ideological reasons.

Maybe-constexpr variables are a hassle anyway, because you always have to prove later that they are indeed constexpr if you want to use them in a compile time context. It only really serves a purpose as an alternative to parametric overload in my mind, where overloads are defined by if block, as with if constexpr and if is_constant_evaluated.

Perhaps if we get epochs, we can do the nice consistent thing and make constexpr variables maybe-constexpr.

👍︎︎ 8 👤︎︎ u/ENTProfiterole 📅︎︎ Oct 11 2019 🗫︎ replies

I have a question about the proposed new meaning of constexpr for variables at 53:41:

done at compile time if necessary, usable at compile time if possible

To me this sounds like the compiler would have to look at how the variable is being used to decide if it should do the initialization at compile time, but is this really the case? What makes more sense is if it tried to evaluate it at compile time and fell back to runtime if it failed like is already the case for const integers and global variables. In that case, wouldn't it be more correct to say: "done at compile time if possible, ..."? I know it breaks the symmetry but to me this makes more sense if this is how it works.

👍︎︎ 3 👤︎︎ u/HappyFruitTree 📅︎︎ Oct 11 2019 🗫︎ replies

I think his discordia analogy for shadow worlds is wrong. In discordia, things sometimes work the opposite of how they are supposed to. In the shadow worlds, you sometimes lack things that the main world would have. Some examples:

  1. Some cars have to drive at a slower speed.
  2. Some vehicles are not allowed on all roads.
  3. Some cars get to drive in a special lane if they have at least one passenger.

I like having different notation for values that should be known at compile time rather than call time as they will get treated differently by the compiler and have different effects and get modified in different ways. Stripping this out makes it harder to figure out what is going on when.

I think the main reason TMP is difficult is that if you are having bugs in TMP, this likely prevents a successful compile and that is (outside of TMP) usually the easiest hurdle in getting to a successful program. As a consequence it feels draining in a standup when discussing that you are not even at a compile yet. TBH, the thing it needs the most is the ability to printout simpler diagnostic messages.

👍︎︎ 3 👤︎︎ u/ronniethelizard 📅︎︎ Oct 12 2019 🗫︎ replies

I think he makes a mistake in one of the questions in the talk: https://www.youtube.com/watch?v=bIc5ZxFL198&t=3445s

Wouldn't that be a bracket operator that's constexpr with a parameter that's `consteval`? For a tuple, the index parameter would have to be a compile time constant in order for the return type of the function to be known at compile time. And the operator itself would be constexpr because it could only be compile time constant if the implicit this was also compile time constant. Perhaps that means we need another decorator keyword at the end of member functions in addition to const, final, etc... in order to talk about the constexpr-ness of this.

I kind of agree that maybe constexpr should just be assumed everywhere. It does seem like kind of a mistake. I don't think though that we could've figured out it was a mistake without trying it first though.

👍︎︎ 2 👤︎︎ u/Omnifarious0 📅︎︎ Oct 12 2019 🗫︎ replies

There should never be a need to mark anything as constexpr. That should be determinable at compile time by the compiler and simply evaluated then.

A compiler can detect purity and const ness and if so evaluate all such functions at compile time if possible.

👍︎︎ 4 👤︎︎ u/smuccione 📅︎︎ Oct 11 2019 🗫︎ replies

I'm still a bit confused and couldn't find info anywhere else, but is this for C++20? Or not?

👍︎︎ 2 👤︎︎ u/ChryslusExplodius 📅︎︎ Oct 11 2019 🗫︎ replies
Captions
all right everybody I'm here to you today about a feature that I am proposing to C++ context for function parameters but before I get into that first on and talked a little bit about who I am my name is David stone I am a software engineer at uber where I work on autonomous vehicles and I'm also a member of the C++ standardization committee where I chaired the module study group and I'm the vice chair of the evolution working group which is the group that new language proposals have to go through so today I'll go over briefly how Const exper works today then what am i proposing where is this in the process of standardization and other related work but if you have any questions feel free to raise your hand throughout you don't have to save them for the end or anything I'm happy to kind of take them as we go I don't want you sitting there in the audience being confused by anything I'm saying so first how const expert works now there are two types of context per and c++ there's context per on variables which looks like this you declare a variable with Const expert and then you can use it in any context that requires a compile time constant for instance you can use it as an array bound you can use it as a template parameter you can use it in a static assert it's fairly fairly straightforward be more complicated Const x-bar in C++ as context per unfunctional that is declared context per it accepts a double returns a double and it returns the inverse and if you try to divide by 0 throws an exception so if you take this function and you use it in a context that requires a constant expression you'll get a compile time error because you're not allowed to throw a compile time if you use it in a context like that second line there this runtime computation and you give it some runtime value that works just fine and this function will just be a regular C++ function at that point and if you pass the compile time constant but you don't use it in the context that requires a constant expression then you'll get a runtime exception through because this will not actually be evaluated at compile time so the difference is that context for variables require compile time construction and destruction whereas cleansed exper on a function merely permits compile time evaluation essentially if everything that it does can be done at compile time following the rules of a context per function and you use it in the context that requires a constant expression it will be otherwise it won't so i mentioned like some things that like a context for a custom evaluation cannot do and we've really we've really shortened this list a lot as c++ has progressed in c++ so three there was a small number of things that you could do and now we have a small number of things that you can't do and you can kind of group them into a few categories so one thing that you can't do at compile time is read values that are not constant expressions call functions that are not contexts per or are not defined like that make sense you can't take a runtime variable and use it at compile time it actually needs to be compiled time all the way through if you're gonna use compound time that's that's a reasonable restriction you also can't use volatile which is the same sort of idea like volatiles meaning is that reads and writes of this variable are an observable side-effect it's used to interface with hardware on your computer which doesn't really exist at compile time that's another reasonable restriction you can't exceed implementation limits basically just the compiler isn't gonna go into an infinite loop at compile time it has a certain step limit it has a certain recursion depth just sort of things to make sure that your compiler itself doesn't crash or run out of memory or do anything like that you also can't invoke undefined or indeterminate behavior for instance division by zero and is your overflow things like that the compiler is actually required to diagnose this undefined behavior for you and similarly you can't throw an exception so--but basically like the goal here is that we want to catch any sort of error that might happen at compile time rather than just compiling your program compiler-- says there's an error here like it tells you that you also can't inspect the common initial subsequence of a union or reinterpret caster cast from void star basically you can't do things that would get you the object representation at compile time and then you also can't do some things with addresses we might go into that some more later and we'll see kind of if people have any questions related to that but it's not it's not too important like it's more of like arcane corners of the language that we probably don't care about so here's an example kind of make it a little more concrete so we have some function declared context for that acceptable and if the bool is true we return 6 otherwise we return 1 over 0 now if we call that with true then that's fine that's gonna give us the value of 6 it's a constant expression but if we call it with false that ill-formed and the compiler is actually required to give you a diagnostic because you divide it by 0 but if we don't assign the result to a context where variable like I said before it will be evaluated at runtime so you'll either get 6 or undefined behavior at runtime for division by 0 so that's kind of the background of Const exper though that's that's really what you need to know about how the language works right now to understand the rest of this proposal so the proposal you can see the latest draft of it on github there's a link there I'll be attaching it to the like the CPP con schedule so you don't have to like write this down if you want to go look at it but the basic feature is this I'm proposing allowing you to declare a function parameter as Const expert and you can use that value any way that you can use it comes to expert variable or a template parameter now so inside the body of the function you can say static assert on the value and everything is fine that's the core feature that I'm proposing then an additional thing is I'm proposing adding a new function is constant expression you give it an expression and it tells you is that a constant expression and it turns out the it's not actually possible to write this in C++ today generally you can you can write an is constant expression that works in some cases but not others and like the reasons for it are really strange like sometimes you can determine if something is custom expression if you use brace initialization because it has to check whether it's a narrowing conversion but you can't actually do that in every context so for instance here if we say static assert is constant expression the string literal awesome that'll pass because the string literal is a constant expression the the next thing that I'm proposing is part of this is the ability to overload on context for parameters so if you have two functions F one of them takes the context for int the other one takes an int then you will call the correct overload depending on whether you give it a compile time constant or not it's really helpful and I'll go into some details of exactly why you would want disability the sorts of things that it allows you to do that you can't really Express today the final thing that I'm proposing is maybe context per and I have here it's a it's a strawman syntax all later on go into the syntax I'm actually composing before we can actually understand how we should spell this what what it should look like in your source code we have to understand a little bit more about what are the semantics that I'm proposing what is the mental model that you're supposed to have and I don't want to bias anyone in favor of a particular syntax that I just kind of put up there as one of the options so so right now and I'm using a very obviously not real syntax proposal sometimes in the past we've kind of just put in a placeholder syntax and said oh yeah you know well we'll get to that later and you know figure out what the real thing is and then we end up with stuff like and reflects per so I want to make sure that like naming is really important and it's actually kind of a theme of this presentation that the the way that your code looks the way that you write things actually affects the way that people think about it and so we want to make sure that we give people a way of writing something that matches the model that we want them to have so we'll we'll kind of get into exactly what what syntax should go here a little bit later so I want to talk a bit more about what are the design goals of this proposal why why am I trying to solve the problems in this way what are the things I'm trying to solve at a high level first of all consistency the title of this presentation is eliminating meta programming from C++ and what I mean by that is we we have like meta programming which is a completely different type of programming than regular C++ like how many of you have ever done meta programming okay most people how many of you feel comfortable doing it at work okay slightly fewer how many of your co-workers feel comfortable with you doing it at work very few people and I think that's a problem because like there are some good things to it but just the fact that you have to do it differently from everything else kind of scares people off one of my other goals is to improve performance there are certain things that my proposal will allow you to write that you can't write today that help c++ continue to be the high-performance programming language and the final design goal that I have is improving Diagnostics right now as a library author there are bugs that your users have that you can't catch and you know they're there you could look at the source code and the compiler knows that they're there but just because of the way that the code is structured or the rules of C++ you actually can't tell the user that they're doing something wrong so I I work at Newburgh as I said on autonomous vehicles and I care very much about the rules of the road it's it's something that we have to we have to focus on a lot and you know lots of people think that the place they live has crazy drivers but let me tell you about a little place called discord again in discordia V the rules of driving are a little bit different than you might be used to now it's important I think to have consistent rules not just a set of rules that cover every situation but you want those rules to actually be consistent so that people can understand them in discordia they don't believe that in discordia on the east-west streets the traffic lights are the way you're used to them but on the north-south streets red means go and green means stop and everyone who lives there says no that's fine that's fine like you know which road you're on you can figure it out it's not a problem in discordia on Tuesdays you push the brake to go faster and you push the gas pedal to slow down but that's okay I'm like you get in your car to start you push the wrong pedal the car doesn't go you push the other one it's fine like you get used to it on Friday nights semi trucks drive on the left side of the road but nobody drives on Friday nights anyway so it's not a big deal I think that is a big deal I think that having inconsistent rules that change depending on things that really shouldn't affect what you're doing are a bad thing and that leads me to the fundamental principle behind my proposal and that is no shadow worlds Malta's carrucha wrote an article titled no shadow world's a few years ago and he got the idea from a guy named Gilad bracha the central idea is that a shadow world is a part of a programming language that is segregated from the rest of the language it's something where when you're working in it you wish you had the power of the full programming language but you don't C++ has no fewer than four general-purpose shadow languages or programming languages I should say there's regular runtime C++ but then there's three shadow worlds there's constructs per C++ templates and macros now what I find interesting is that there's a long history of people recognizing the concept of shadow world's and then falling victim to them without realizing it so I mentioned multi skarupa wrote an article about an article written by Gilad bracha now gilad bracha was talking about kind of the the importance of recognizing this and not introducing shadow worlds but then in his article and in his presentation he was talking about a programming language that introduced a a macro system that was a shadow world and multi Scarpa was writing about this and he noticed that and he said you know I have I've you know this idea for programming language of no shadow world's we need to eliminate macros from our programming languages and what that means is that we have to have a powerful template system but in my mind the template system is itself a shadow world like it's it this is a really difficult problem is really easy to accidentally constrain yourself into creating something new in a small corner of the language without realizing it so my goal here is to try to find a thing that will be consistent with the rest of the language that will sort of align with things and fit into existing things rather than being a completely new thing so we have I'm as I mentioned in C++ context birth in C++ 11 it was a really limited shadow world you could hardly do anything a Const expert function basically had to just be a return statement a static assert or a type def that were note if there were no force so that meant that when you were writing code and Const X for C++ 11 there's lots of recursive code with a ternary conditional and like you could get used to it just like you could get used to red means go and green means stop but you know there's always a little weird C++ 17 expanded context or in 14 and and 17 but we had no memory allocation we had no destructors c++ 20 lifts that restriction and now we can do almost everything in c++ 17 or ends equal spell 20 rather and const expert except for that list i mentioned at the beginning and that's a really short list like we're we're kind of recognizing this problem and starting to fix it for context but we're generalizing funds decks for more and more to have it be able to be just normal code that you write and then macros i think we're all aware of the problems with macros there's no loops no mutation almost no recursion turns out there actually is a way to kind of fake it but it's it's really ugly there's no scoping there's weird if statements like I don't think I really have to go into a lot of detail about the problems of macros but for this presentation I'm actually gonna be focused on templates templates are the shadow of functions the compile time parameters are lexically split off from the other parameters so if you want to pass a compiled time parameter and a run time parameter you have to say something like F open angle bracket 0 close angle bracket open paren 1 close paren and that may seem fine at first but then you realize though there are a lot of things that templates can't do you cannot pass template arguments to constructors you can't pass template arguments to overloaded operators you can't overload on whether a value is known at compile time you can't put a compile time argument after a runtime argument regardless of how you want to write your function if it's a template parameter it goes first you can't forward a pack of arguments some of them known at compile time and some of them known at runtime without having two different parameter packs and you have to know that the function you're forwarding to has this requirement so the reason I had this proposal is because of low key MPL fusion hana mehta burg and simple c++ metaprogramming library turbo and many others that I don't know about or I've forgotten Larry wall the creator of Perl 6 said when they built the University of California at Irvine they just put in the buildings they did not put in any sidewalks they just elected grass the next year they came back and put the sidewalks where the trails in the grass were and we see here what happens if we don't do that if we build people a path that is not the path to where they want to go they're gonna make their own path that goes there my proposal is that we should pave this path so I'm going to give you a few examples of the types of things that we have to do today and the types of problems that we face because we do not have this ability to have a context for function parameter so here we have arrays you index them with the bracket operator that's the syntax we're familiar with we know and love if you index it with zero that's fine index it with one it's fine index it with two undefined behavior the compiler can clearly see that what you're doing is wrong and maybe your compiler has implemented a custom warning I don't believe in you have four standard array and then we look at tuple and what is tuple conceptually I believe that tuple is essentially the same type of thing as array except the types can be different but the syntax doesn't reflect that you have to say STD : pull and get and then put the index of angle brackets knows the syntax we made up for C++ 11 because we added tuple because the bracket operator couldn't work because you can't pass a template parameter to an overloaded operator and with tuple you can index the first two values and it's fine and then you index the third value which doesn't exist and you get a compile time error so we have the syntax we want on one hand or the semantics that we want on the other hand with my proposal it could look like this we can have a compile time error for indexing an array out of bounds with a Const exper value and we can have the nice syntax for indexing a tuple how many of you have used true type okay TrueType is it was added in C++ 11 it's part of type traits it is a type that encodes the value true this is what I think true should look like integral constant is even worse we have integral constant which encodes an integer value in a type whose wrote a proposal to just shorten that to constant now that we have class types as non type template parameters and then someone realized hey we can just deduce the type you don't have to specify it so we can have constant and then just some value we can encode arbitrary values and types Hana has int underscore see following the lead of MPL int underscore more ways to encode values and types but trying to get the the syntax a little bit shorter there's a bunch more I don't know now this is what I think the number 24 should look like Louis D on the creator of boost Hana said if we have context for parameters I don't think there's much purpose for integral constants anymore major parts of Hana would be significantly more intuitive to use that way this is what code using Hana looks like for the person writing the function you say I'm going to accept a value of type Hana int underscore parameterised on the actual value but I don't care about that I care about that value in there but the reason you have the syntax is because that function syntax is so powerful you want your users to be able to just call regular functions under my proposal it would look like this conceptually the thing that you're trying to say is I want an int and I want it to be context bar and this allows you to directly express your intent this is what it looks like on the caller side if they don't if they haven't already wrapped up their integer type in one of these things they have to do something like this I think it should look like this C++ 20 adds the spaceship operator the comparison operator a single operation that returns whether two things are less than equal to or greater than and when you use the spaceship operator directly which should be rare but when you do the intended use for it is that you compare it against an integer literal zero so you say left hand side spaceship right hand side if you say less than zero then you're saying left hand side is less than right hand side if you say equals zero you're saying the two are equal if you say greater than zero you're saying we rigged the left hand side is greater than the right hand side this is valid but due to kind of a glitch I guess in the language it's it's defined as an unspecified comparison type that accepts an integer literal zero but it turns out in C++ there's only one type that has that property and that's no better team so you could also compare the result of a spaceship operator in your implementation most likely with Mulliner this was never intended in the design we knew that it was possible but there wasn't really anything we could do to prevent it without adding even more stuff into the compiler and I think part of the problem here is twofold really one is that this feature cannot accept that right hand side parameter as a context for int and just require it to be zero and second we can't define types like no clutter T that can only be constructed from a subset of integer values we can only operate on types even if the value is known at compile time but this is a property that we found so useful that it's actually existed since C the ability to say the integer value 0 is special for this type and I think that built-in pointer types are not the only type that have this property where they care about a particular value as being special so under my proposal we can have this if you try to compare against no putter which is not intended to be supported you get in there saying you can't compare that should say operator less than not operator equal equals [Music] okay so how many people by show of hands think they know the rule for when you need to use type name in template code okay how many people think they know the rules for when you need to use template much fewer okay so sometimes this is the code that you write and sometimes this is the code that you write and the reason for that is that without the template keyword what this looks like to the compiler is CF less than n greater than a parenthesized X and that's syntactically that one so you need to put in a template keyword there they say actually the CF is a member template member function template and n is a template parameter and X is a regular parameter I think that's crazy like I we're in a we have a room full of people who are really passionate about learning about C++ and I asked how many people know how to call a function in a template and most people don't raise their hand because it doesn't look like this like this is this is really the thing that we're trying to express we're trying to express I have a function and it takes two parameters and the first one is not a compile time reg X so this regular expression in I believe all popular standard library implementations is going to be exponential time with a bunch of A's and B's if you have a string 100 characters long then it's gonna take about two to the 100 steps to realize that a bunch of A's and B's don't match this regular expression it's possible to work around this it's possible to to avoid this problem but doing so is valid only for some types of regular expressions if you have a back reference then we don't know of any way to do better than exponential time but there are no back references in this regular expression it is theoretically possible to construct a regular expression that uses linear time in linear space to parse this rather than exponential time but that would require the regular eggox implementation to do two scans of the input string always and have some sort of variant type in it that stores do I have this this this DFA this finite automaton that I built up or do I have the backtracking implementation you'd have to have some sort of way of switching on those and you have to pay the cost all the time so with this proposal same regular expression most regular expressions are compile time constants this is the idea behind honey-do Zhukova x' CTR e-library the compile time regular expression library that if you know the regular expression string at compile time then you can actually do some really amazing optimizations you can compile this code down to like assembly instructions that you could just print out on your screen or like a couple pages like it it can be like really straightforward code that is exactly the code that you would hand write to match the string but you have to know the string a compile time to build up these structures and if we had punched extra parameters we could do that a lot easier regular expressions also have a syntax that sometimes confuses people this is not a valid regular expression some people are used to the glob syntax I think okay I'll put in a star they'll be like in my bash shell it'll just match everything but actually that's not the case the star is the repetition character so you know right now this throws reg out there at runtime but if we had constructs for parameters we can have a static assert this is hey that star token has to occur after a Parekh - that you're trying to repeat you have to say like dot star or something yes Albert yeah so the question was is stood rag act something that be a maybe Const expert yes that would be I think how you would do this so that you can allow people to continue to pass in runtime strings if they are but take advantage of the extra information if it's known a compile time so there's a library that I've written called the bounded integer library basically usage of it looks something like this you define a lower and an upper bound on your integer type and it sizes the integer appropriately so you can have an integer from zero to ten and then enjoy from five to six and you can add them and the type of that is in the engine for five to sixteen and in in certain domains that can be really helpful of making sure that your integers are exactly as large as they need to be you can avoid overflow you can use this to implement essentially a static big integer you can just continue to grow your integer as needed you can have an arbitrarily sized integer but the usage of it isn't always nice so right now if you really want to get kind of the the maximum performance and maximum type check and you have to write something like this you bring in the literals namespace into your code which is kind of the recommended way of using literals and then you construct it you pass literals to the constructor but if you forget to do that then you might get undefined behavior because you're trying to construct a ninja with an value that's out of range so under this proposal could look like this you can just construct the type with just regular interest like you're used to now I'd like to talk about a few things that we can't do now or like probably just can't solve or they're so complicated or so slow to compile or they have such other bad drawbacks that we just don't bother for instance a units library let's say we have a value just type is meters and we want to cube it with this proposal you could just write this code you can say pow two meters three so two meters to the third power that gives you eight cubic meters you can load in a runtime value of type meters say POW that runtime value where we don't know what the content is raises the second power and then we get square meters because we know what the type is because that second argument can be a compile-time constant be possible to write a units library that does this sort of automatic units adjustment with certain mathematical operations let's take Stirling compilers have a built-in Stirling function that essentially has a constant parameter built into it or maybe constant parameter right now you have to have your compiler give you this built in if you want to use it a compile time like we do in chart rates length and if you want to have optimal performance if the compile if your if you're not lucky enough to have a function like this that the compiler cares about then you can't do this you have to say I'm gonna write cons decks first or Lin it's just the top but is slow at runtime if the compiler doesn't realize that it can turn it into the bottom at runtime I think that we should be empowering our users to write the code that they need to write to work in the situations they need and to be optimal at runtime without having to be a compiler writer to do so similar example Intel processors have some AES instructions built in a es keygen assist they help also basically implemented concepts for function parameters but just for this built in the Li function there at the bottom ----builtin ia32 AES key Genesis requires that second parameter is a compile-time constant if you pass it a runtime value you get a compile error but you don't have to use the templates of deaths you can just use the regular function syntax because conceptually they wanted that second parameter to be second it goes after the Y goes after the X so why do we need this proposal it gives us a uniform caller syntax it gives us better run times we can add compile time Diagnostics to libraries you don't have to change your callers if you find a way to take advantage of the fact that a value is known at compile time while still providing the same interface it can give you better compile times if anyone's ever tried to use things like MPL and boost Hana they compile pretty slow because every value has to be wrapped in a tight and instantiating a type is one of the slowest things that you can do at compile time you don't need to reinvent the entire language in a meta programming library Hamma has Hana for each MP l has n PL for each a lot of these libraries have their own essentially like push back and sort and binary search we have Yoda in the form of make index sequence we have all of these algorithms by a slightly different name sometimes because we didn't realize it was the same algorithm implemented at compile time and implemented at run time I think that this is why the syntax is important by changing the syntax we change how we think about the problem and if the syntax doesn't match the way we think then we'll have a hard time really understanding the essence of the problem decomposing it and reusing the solutions we already have we will have to throw everything out and start anew just because we want to use it at a different time in our programs translation or execution so hopefully find at least some of these motivating examples compelling I like to think about proposals in terms of guiding principles the principles of this proposal are aesthetics performance correctness abstraction respecting the users time and not duplicating work you know don't repeat yourself so meta programming I care very much about meta programming I gave you an example of the bounded integer library which is essentially an implementation of dependent types in C++ using some pretty advanced meta programming I I'm regularly using cutting-edge features I am building with playing trunk because they have the the latest version of a lot of the features I need and I'm regularly crashing my compiler by using meta programming like I'm I'm kind of on the edge here I care a lot about this it's really important to me it's really powerful but it's the wrong solution it's the best solution we have it's the dirt path in the grass but sometimes it's harder to walk on a dirt path you definitely can't drive on one really slows you down it can it can kind of get in your way it's it's it's not the way that we think about programs so how does this proposal work just like a template fee so I've been I've been spending a lot of this presentation talking about like why templates are bad but templates are bad just because the way that you use them is different from the way that you use regular functions the the semantics of templates are really powerful that's why we've been able to build all of these powerful things in C++ so what types can be used literal types the Const expert constructor and destructor this these roles have been expanded in C++ 20 allowing context for destructors and also they have to have strong structural equality which means you have to default operator equal equal which is a new concept in C++ 20 and all of your bases and members have to have strong structural equality themselves basically the compiler has to be able to easily and obviously determine what it means for two values to be equivalent this is the same rule as class types as non-type type of parameters have and that's kind of the goal here we want to have one consistent set of rules for things rather than one rule that applies to templates one rule that applies to contexts per one rule that applies in your driving north one rule that applies in your driving west where can you use it if you have a context for parameter you can use it essentially anywhere after that this is the same rule again that templates follow if you have a non type template parameter you can use it later on in the template argument list you can use it in the know except you can use it in the requires clause you can use in the body if it's a static assert and rave and all of these things are allowed so now we get to maybe const expert that syntax that I've been showing is this to avoid biasing you but first I want to talk a little bit about the use cases of it and then we can kind of understand what it should be called use cases are the ability to forward arguments so this is kind of similar to right now we need have forwarding references or universal references and template if you say T ref ref it's can be an R value reference at candy and L value reference and you call standard forward to say whatever it was keep it that way the other the other main use case for maybe comes to expert parameters is to have two implementations that diverge only one area because again remember the principle of this proposal one of them is that you shouldn't have to write the same thing multiple times in slightly different ways so here we have an example of controlled divergence this could be a possible implementation of arrays operator bracket you give it a maybe concepts for index and if that's a constant expression you static assert that it's in bounds and no matter what you index some some secret c array that nobody knows about to make this an aggregate I think this this would be good use for it because conceptually you have one function and if you can use it a compile time you just do this one check but otherwise the body is the same overload resolution for context for parameters has context for basically being a tiebreaker it matters only for otherwise ambiguous calls and the types have to be the same standardization where is yes okay so the question was why do you need to overload resolution with context where that seems to be a duplicate with the maybe context per feature there are some situations where it's difficult to have control flow for instance in a constructor initializer list you might need to like call a function or not depending on whether its context were in the initializer list and there's no way to really branch there so you would then have to wrap it into lambda and return something for the lambda which might not even work because you might not have a copy elision in that context depending on what you have to return yes oh okay yes so the question was if I could have two overloads with function one with contacts per anyone without doesn't that satisfy the same the same sort of problems at death yes all of the problems that can be solved with maybe Const expert can be solved with customer overloading but not always easily so if we go back to this list consider the case of forwarding arguments I want to write a function that accepts some arguments does something and then forwards all of those arguments on to another function if I don't have maybe Const exper if I have a function that takes 5 parameters I have to write 2 to the 5 overloads with every possible combination of contexts were on or not on every parameter and that that's basically the same problem that some of us have suffered through in old versions of compilers that implemented move semantics not forwarding references we're like in place back on old versions of Visual Studio had just ten overloads stamped out and that actually led to serious compile-time problems so in a later version they reduced that number down to five and then they implemented very attics which allowed them to get rid of that pattern I basically I want to make sure that we don't have the same problem I want to make it easy to forward context furnace and it turns out that you you don't want to just deduce that from any template because that gives you a lot of new template instantiations like all the existing code that's calling f5 f6 f7 is gonna create a completely new template instantiation if we suddenly started deducing it which would be a serious regression at compile time so it has to be something that you opt into to kind of avoid that problem yes so the question was if you mark the function context / isn't that kind of the same thing as having every argument maybe context / kind of not quite and the problem here is if this function we're just marked Const X / and there were no maybe constructs / here I couldn't static assert in the body you can't treat the parameters of a context or function as compile time constants because it could be called at runtime the compiler just rejects that and says or do you mean static assert X is greater than zero you just said it was int it's just an int that's all it is I can't stand a concern on that so kind of the the existing existing wording this existing semantics mean that inside the body those are just regular values so the question was could you just do if cost expert is constant expression not quite the reason is that that would always be false because that's not a constant expression in the body of a function in C++ xx we're adding a new functionality called is constant evaluated and I'll get to that a little bit later and explain why it also can't solve this problem but so kind of hold that thought and a little bit later I'll talk a little bit more about that okay so where is this in standardization right now first let's talk about kind of how the committee works to give you just a little bit of context here before each meeting we meet about three times a year and before each meeting everybody sends in all of their paper all their proposals in paper form and this gives people kind of some time to read over the papers to think about them and in general we don't discuss things at the committee that don't have a paper we discuss papers in between meetings through email lists kind of answer some questions but sometimes that kind of has lower throughput than we would like which is why we meet and we remember of ISO so we have certain ISO rules to follow which basically says that we have to be at an actual convene meeting to make certain types of decisions so at the meeting we discussed papers and we vote everyone in the room gets one vote but you are encouraged to not vote if you haven't been following the discussion or if you don't understand and the way that the voting works is we vote strongly in favor in favor neutral against or strongly against typically we require at least a two to one vote in favor of something for it to move forward the idea here is we don't just want a simple majority we don't want thirteen people to say yes and twelve people to say no and we say good enough move it through we don't want things getting by on a on a razor-thin margin because if you can't convince at least two-thirds of the room that your proposal is a good idea that might be a suggestion that you should work on it some more talk to the people who voted no understand their objections and see if there's a way to kind of improve things address some of their concerns something like that so we have a very strong consensus based model sometimes the design changes at the meeting we try to not do that but sometimes we do the idea instead is that if we decide we don't like this design this other design seems really promising the answer is okay write a paper and we can talk about it next meeting because maybe there actually is a subtle flaw of this design that we didn't realize we don't want to be voting on something that we haven't to think about so for this paper the standardization progress it was discussed in ewg which is the evolution working group that's the big group that all new language proposals have to go through in last year in June we voted not to merge the the original version of this paper into C++ 20 on that vote I actually voted neutral because I didn't feel like my paper was quite developed enough to say yes vote on it put it into the standard shipit I was more looking for for feedback to make sure like is this the right direction we like kind of the general concepts how could we make it better so we also took a vote of encourage more work in this direction we we like the way this paper is going and for that it was 27 in favor and 4 against so it was pretty strong encouragement of you know try to solve this problem with something in this area so related work what what other things are coming in C++ 20 what are their proposals have we had what are the things that this features commonly compared against one of them is constable functions this was added in C++ 20 it's really similar to context per but it requires that the function is evaluated at compile time remember that context per on a function means potentially evaluated at compile time it's it's at compile time if it's possible and if it's required Const eval says no this must be evaluated at compile time or it's an error it's similar to the meaning of context but on a variable these functions do not exist at runtime so this means it does not help if only some of the code is compiled time for instance indexing a tuple when you index into a tuple the tuple value could be a runtime value you don't know the contents of the tuple but you know its size and you know the index you're using so if we tried to use constant to implement this we would say you cannot index a tuple that has a runtime value and that's not right you can't use it to add static assert into an array again for the same reason and also because the parameters themselves are still not compiled time constants within the function so comedy Bell actually solves none of the motivating examples in this paper it's like it's a reasonable feature it has lots of problems that it solves just not these problems you cannot overload a const eval function with a non constable function where that's the only difference you have to use just regular overload resolution and it's either quantity valour it's not then I mentioned you cannot use the parameters and comedy eval function as compiled time constants so is constant evaluated this kind of gets to gets back to your question of it here's constant its constant valuated it has many of the same problems of const eval because it requires the entire statement to be a constant expression a compile-time constant so comp is constant evaluated is saying am i using this code in a context that requires a compile-time constant so if you say static assert some context per function then is constant evaluate is going to be true in there but if you say int x equals some context per function is constant value is gonna be false in there it's based on how you use the result not based on whether the inputs are known at compile time and that means that we actually don't have the same kind of fine-grained control the context where parameters give you so kind of here's an example if we go back to that inverse function that accepts the double and returns a double and if you pass in 0 it froze so how do you solve this problem with this constant evaluated it's valid what do you do there there's nothing you can do because you don't know the value of x at compile time even if you do you can't access it in a way that's a compile time you can't put a static assert in there because double x is just a regular old double as far as the compiler is concerned whereas if we had punched x four parameters you could say maybe it's const expert and if it is static assert false now it doesn't matter how the caller uses the result you still get a compile there does that kind of answer your question or are you still confused I know okay there's another proposal parametric expressions by Jason rice it's p1 one to one it's a proposal that he's working on that introduces the concept of hygienic macros so this would be a sample syntax it'd be a new type of function that has macros that are that are properly scoped and are complete units of code supports evaluating exactly once or 0 through n times depending on the syntax you use and he actually does proposed allowing context for parameters on his parametric expressions but only for parametric expressions it solves many of the same problems but not all but it creates yet another shadow world so now in addition to regular functions called stats for functions template functions and macros will have parametric expressions we'll have five ways to write a function in C++ none of them quite solving all of the problems perfectly it would not replace existing functions the body is always in line like macros which you sometimes don't want it doesn't support overloading it requires deduction of the types so like overall I think that this is kind of the wrong direction even if it happens to solve some of the same problems because the principles are wrong so now let's get to the syntax that maybe comes Dex % acts in particular strawman constructs for strawman in this presentation so these are some possibilities caused expert this was my first thought const eval when it was first proposed was context for with an exclamation mark at the end but you know they're not supposed like how do you pronounce that is it comes to expert not like that that isn't quite what we wanted to do it would be the first example of a keyword with these like other tokens in it that aren't just alpha numerics so strike that that was my original idea but I don't think it's a good idea anymore what about maybe Const extra test I've been calling it this whole presentation seems like a good use of the value but there's another idea first we need to go back to our principles our principles our consistency we want to make sure that we have a consistent set of rules not just a set of rules that are complete that cover all of the use cases but that are the same thing everywhere so let's look at what context were in constable mean today context were on a variable means must be done at compile time context bear out a function means done in compile time if necessary usable at compile time if possible but cost evil on a variable not allowed cost eval on a function means must be done at compile time now I noticed an asymmetry in this table so what if we were to change it to be this we would change the meaning of context for unvarying to say done at compile time if necessary usable at compile time if possible so a Const exper variable would become a maybe context burberry this is an interesting property that existing code does not change meaning because we know all uses of context where variables are initialized by compile time constants but it gives us this nice symmetric table where we can actually simplify our rules and do this this is really easy Const expert means maybe cost eval means definitely this is what we have today is what we could have it all comes down to how do we want to program our weed discord ian's the comment was oh yeah do we want to drive on the left side of the road if we're a semi truck on Friday because nobody really drives on Friday anyway so it doesn't matter maybe the reason nobody drives on Friday is because there's semi trucks driving on the left-hand side of the road maybe if we fix the rules we would find out that actually this whole time we've been wanting to drive on Fridays we've been avoiding it because it was dangerous or it was too hard or too confusing or some of us could do it other people couldn't and the people who couldn't didn't really want to ride with people who could because they actually couldn't they just thought they could like I think syntax is really important I think that form follows function but function follows form the way that we write things determines the way that we think about things in the way we express ourselves do we care about consistency so I'd like to leave you with a quote by Bruce Lee I'm paraphrasing before I learned the arts a parameter was just a parameter and an argument just an argument after I learned the art a parameter was no longer a parameter and an argument no longer an argument now that I understand the art a parameter is just a parameter and an argument is just an argument thank you any any other questions yes okay it's pretty far back so the question was stood get would that have to be a constant eval in addition to a contacts per index that we were saying no it wouldn't so if we were to mark stood get as Const eval then that would mean that it must be evaluated at compile time which means that all of its arguments must be compiled time constants which means that the tuple or array that you're indexing would have to be a compile time constant the only thing that we care about is that the index itself is a compiled time constant we don't care how you use the result we don't care about the the compile time this of the thing that we're indexing into so stood get would remain constant as it is today and then the index parameter would be marked or we would create a bracket operator that does the same thing as stood get and make the index a constant value yes wouldn't mean part of the type like the function template types yeah okay so the question was like basically what happens to the template parameters on on class templates where right now they're part of the type what do we do with them I'm not proposing making any change there and the reason is that those are actually serving a different function they are parameterizing the type itself you're you aren't thinking of it necessarily as a a computation that returns you're saying this is an imager type from zero to ten you're not saying I pass in zero and ten and I get back in and any type that's not that's not how we're thinking about it today necessarily so the the general idea here is that the the template parameters like they don't need any storage they it actually is a different type but it's part of the same family of types does that make sense other questions yes yes yes okay so the comment was that I I stated that it would work just like templates but it would reduce compile times compared to the current world I expect it to have pretty much the same compile times as specifying an explicit template parameter where it saves you is when you start using things like Hanna or MPL where you're wrapping the value in a type so that you can use regular function syntax okay I believe that is all the time I have so I'll be here to answer a few more questions but thank you all for coming [Applause]
Info
Channel: CppCon
Views: 11,901
Rating: undefined out of 5
Keywords: David Stone, CppCon 2019, Computer Science (Field), + C (Programming Language), Bash Films, conference video recording services, conference recording services, nationwide conference recording services, conference videography services, conference video recording, conference filming services, conference services, conference recording, event videographers, capture presentation slides, record presentation slides, event video recording, video services
Id: bIc5ZxFL198
Channel Id: undefined
Length: 60min 7sec (3607 seconds)
Published: Fri Oct 11 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.