C++ Weekly - Ep 428 - C++23's Coroutine Support: std::generator
Вставка
- Опубліковано 12 тра 2024
- ☟☟ Awesome T-Shirts! Sponsors! Books! ☟☟
Upcoming Workshop: Understanding Object Lifetime, C++ On Sea, July 2, 2024
► cpponsea.uk/2024/sessions/und...
Upcoming Workshop: C++ Best Practices, NDC TechTown, Sept 9-10, 2024
► ndctechtown.com/workshops/c-b...
CLion is a cross-platform JetBrains IDE for C and C++ with:
- A smart C and C++ editor to navigate and maintain your code base productively.
- Code analysis with quick-fixes to identify and fix bugs and style inconsistencies.
- An integrated debugger - along with other essential tools from the ecosystem - available
straight out of the box.
- And much more!
jb.gg/clion_ide code: CppWeeklyCLion
Episode details: github.com/lefticus/cpp_weekl...
T-SHIRTS AVAILABLE!
► The best C++ T-Shirts anywhere! my-store-d16a2f.creator-sprin...
WANT MORE JASON?
► My Training Classes: emptycrate.com/training.html
► Follow me on twitter: / lefticus
SUPPORT THE CHANNEL
► Patreon: / lefticus
► Github Sponsors: github.com/sponsors/lefticus
► Paypal Donation: www.paypal.com/donate/?hosted...
GET INVOLVED
► Video Idea List: github.com/lefticus/cpp_weekl...
JASON'S BOOKS
► C++23 Best Practices
Leanpub Ebook: leanpub.com/cpp23_best_practi...
► C++ Best Practices
Amazon Paperback: amzn.to/3wpAU3Z
Leanpub Ebook: leanpub.com/cppbestpractices
JASON'S PUZZLE BOOKS
► Object Lifetime Puzzlers Book 1
Amazon Paperback: amzn.to/3g6Ervj
Leanpub Ebook: leanpub.com/objectlifetimepuz...
► Object Lifetime Puzzlers Book 2
Amazon Paperback: amzn.to/3whdUDU
Leanpub Ebook: leanpub.com/objectlifetimepuz...
► Object Lifetime Puzzlers Book 3
Leanpub Ebook: leanpub.com/objectlifetimepuz...
► Copy and Reference Puzzlers Book 1
Amazon Paperback: amzn.to/3g7ZVb9
Leanpub Ebook: leanpub.com/copyandreferencep...
► Copy and Reference Puzzlers Book 2
Amazon Paperback: amzn.to/3X1LOIx
Leanpub Ebook: leanpub.com/copyandreferencep...
► Copy and Reference Puzzlers Book 3
Leanpub Ebook: leanpub.com/copyandreferencep...
► OpCode Puzzlers Book 1
Amazon Paperback: amzn.to/3KCNJg6
Leanpub Ebook: leanpub.com/opcodepuzzlers_book1
RECOMMENDED BOOKS
► Bjarne Stroustrup's A Tour of C++ (now with C++20/23!): amzn.to/3X4Wypr
AWESOME PROJECTS
► The C++ Starter Project - Gets you started with Best Practices Quickly - github.com/cpp-best-practices...
► C++ Best Practices Forkable Coding Standards - github.com/cpp-best-practices...
O'Reilly VIDEOS
► Inheritance and Polymorphism in C++ - www.oreilly.com/library/view/...
► Learning C++ Best Practices - www.oreilly.com/library/view/... - Наука та технологія
Imagine this combined with the upcoming networking library! I don't even know what exactly is possible.
I never liked this approach. The default procedural approach working together with OOP is much easier to understand than this functional programming approach.
I've seen even people who are proponents of functional programming admit that the default procedural + OOP is much easier to understand than this.
Very nice to see generators landing but I’m not convinced that std::exchange is contributing to the transparency or readability of this code
He did say tersest way possible
That is what I said. Also, exchange is super helpful when you need it, so learning how to read it is super helpful.
I think that our brains get use to unusual syntax. When you learn C++ - lambdas look terrible. After a while you get use to it. The more you see std::exchange, it will be more natural.
Awesome, I hope other compilers will implement it soon as well so that people can start adapting it
I know ranges are cool and offer many more functionalities (constexpr, potential random access, and so on). But it is such *much* easier to actually author generators than either traditional iterators or ranges.
Going to have to get out of the habit of reaching for std::cout and use std::println instead.
Its also interesting how much code hello world with std::println produces with -O(2/3), maybe an episode on why that is the case would be interesting. At compile time it should be known I'm printing a basic string and not using any formatting and remove all the formatting boilerplate etc.
This is why I'm tending to not use it in C++ Weekly. It's unfortunately slow to compile and obfuscates the binary.
I hope they make it better soon
I would make some observation, code like `for (auto i : foo() | bar())` was tricky in older C++ as value returned from `foo()` could be destroyed as lifetime was bound only to `foo() | bar()` expression, but recent C++ fix it and make that temps in range `for` live as long this `for` is in scope.
Are you referring to C++23 'Extending the lifetime of temporaries in range-based for loop initializer' ?
@@testtest-qm7cj Yes, this, small change but how much it improve life
also compile with just released gcc 14.1
The difference in assembly output is quite large. I imagine the optimization maniacs have not had their fun yet with std generator
std::generator allocates. The lambda function in the previous video returns a very weird type, since it is auto. Is there a way to have the best of both: a generator class/template, to which I tell to use a certain amount of memory to work with on the stack. kind of like the new flatmaps with arrays. e.g. std::generator ?
It doesn't return "a very weird type", actually. It just returns an _unknown_ type. You just have to remember that lambdas are just anonymous function classes.
Nothing built in, but it might be possible to construct something.
I'm a C++ beginner/intermediate (~3 years experience, 2 of which on the job (take from that what you will)), and it's my only language so far.
I gotta say, it seems like the perfect time to be getting deeper into it: in the back of my mind, I've had "I must sometime learn about coroutines" - now I have the fancy new features to do it!
I am starting to take learning this language more seriously and I feel like your videos are gonna help me learn fast.
-Fanalyzer does not like std::generator one bit.
Also, here's a version of fib() with no UB that stops after the addition overflows:
constexpr int wrapping_add(int a, int b) noexcept {
return static_cast(a) + static_cast(b);
}
std::generator fib() {
int i = 0;
int j = 1;
while (true) {
co_yield (i = std::exchange(j, wrapping_add(i, j)));
if (j < i) {
break;
}
}
}
And if you add a co_yield 0; before the loop it'll yield the classical version of the sequence.
Actually, I made this too complicated, just yield I before you compute the next number in the sequence:
std::generator fib() {
int i = 0;
int j = 1;
while (i >= 0) {
co_yield i;
i = std::exchange(j, wrapping_add(i, j));
}
}
Don’t you need a return outside the loop if you’re gonna break?
@@driedurchin Nope. The generator stops yielding values when it reaches the end of the function. I think it returns some sort of stop token when you do that, so that loops over the generator know that you're done.
@@Nobody1707 So it is different from a regular function in that control flow analysis doesn't care that you reach the end of a non-void function?
Or you could write a for loop and reduce the generated assembly from 338 lines of unreadable asm to 30 lines of pretty readable asm. Probably faster too, though quick-bench doesn't support GCC 14.1+ so I wasn't able to verify.
1. Copy to godbolt
2. Flip #if 1 to #if 0
3. See the assembly reduce by 90%
```
#if 1
#include
#include
#include // std::exchange
#include
std::generator fib()
{
int i = 0;
int j = 1;
while (true)
{
co_yield i = std::exchange(j, i + j);
}
}
int main()
{
for (const auto i: fib() | std::views::take(20))
{
std::cout
Yes, probably much faster for this simple use case to not use std::generator. ua-cam.com/video/F37h3FuA8kM/v-deo.html
What are the good use cases of coroutines? Where can I learn more about them? Maybe .pdf book or something
I think Donald Knuth's The Art of Computer Programming is probably the best known motivation for the need for coroutines, although there surely are more modern books
Iterative Tasking.
Sometimes, you don't want to do all of your work up front. You want to do it in chunks.
Writing code to support something like this is an absolute pain once it gets complicated.
Yay for coroutines!
I've exploited to death `std::generator` in ways that works surprisingly through the years, it's very interesting what you can do with that as soon as you understand how to manipulate manually the iterator from `begin()`
The infinite loop is UB, isn’t it?
I think not, because co_yield is a side effect. Also, I think C++26 makes while(true) { } defined anyway.
@@Nobody1707 yes but not while(true) { int i = 0; } afaik
@@arthapz No, C++26 I believe is aligning the rules to be closer to C's. If the loop condition is the literal true then the infinite loop is defined behavior. Other forms of infinite loops will still not be.
I still say that the functional paradigm seeping into C++ is a bad thing. It's ugly, harder to understand than its procedural counterpart and requires much more compiler support to optimize it than the alternative. If anyone has to ask why requiring more compiler support is a bad thing, consider that one of the chief complaints against Rust is the compilation speed and how this would make C++'s compilation speed worse than it already is and put it more on par with Rust. Consider also that the harder a language is to implement that the fewer vendors we'll have. Perhaps that doesn't matter to anyone and they're fine with only having three vendors seriously competing, but monoculture in nature is a bad thing just as it is here. You may not see the ill effects today, but transitioning in the future will be harder still than it already is. Think of all the legacy codebases out there, especially the ones that are held together with duct tape and chewing gum.