├── README.md ├── code ├── await_co_future.cpp ├── await_msvc_future.cpp ├── fibonacci.cpp ├── fibonacci.hpp ├── fibonacci.py ├── fibonacci_coroutine2.cpp ├── fibonacci_coroutines_ts.cpp ├── fibonacci_cppcoro_generator.cpp ├── fibonacci_msvc_generator.cpp ├── fibonacci_range_v3.cpp └── fibonacci_ranges.cpp └── presentation └── C++ Coroutines.pdf /README.md: -------------------------------------------------------------------------------- 1 | # C++ 20 Coroutines—An Introduction 2 | 3 | This repository contains the presentation file and example code for my 4 | presentation at the C++ Summit 2019 held in Shanghai, China on 2–3 November 5 | 2019. 6 | 7 | The presentation content is shared under a [Creative Commons 8 | Attribution-ShareAlike 2.5 Licence][1] (CC BY-SA 2.5). The code is put 9 | in the public domain (i.e. do whatever you like with it), though an 10 | acknowledgement will be appreciated (but not required). 11 | 12 | [1]: http://creativecommons.org/licenses/by-sa/2.5/ 13 | -------------------------------------------------------------------------------- /code/await_co_future.cpp: -------------------------------------------------------------------------------- 1 | // Clang: clang++ -std=c++17 -fcoroutines-ts await_co_future.cpp 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std; 10 | using std::experimental::coroutine_handle; 11 | using std::experimental::coroutine_traits; 12 | 13 | template 14 | struct co_future : future { 15 | using future::get; 16 | using future::wait; 17 | using future::wait_for; 18 | 19 | co_future(future&& fut) : future(std::move(fut)) {} 20 | 21 | bool await_ready() 22 | { 23 | return wait_for(std::chrono::seconds(0)) == 24 | std::future_status::ready; 25 | } 26 | 27 | void await_suspend(coroutine_handle<> handle) 28 | { 29 | wait(); 30 | handle.resume(); 31 | } 32 | 33 | auto await_resume() 34 | { 35 | return get(); 36 | } 37 | }; 38 | 39 | template 40 | struct coroutine_traits, Args...> { 41 | struct promise_type { 42 | promise promise_; 43 | co_future get_return_object() 44 | { 45 | return promise_.get_future(); 46 | } 47 | 48 | auto initial_suspend() { return suspend_never(); } 49 | auto final_suspend() { return suspend_never(); } 50 | 51 | template 52 | void return_value(U&& value) 53 | { 54 | promise_.set_value(std::forward(value)); 55 | } 56 | 57 | void unhandled_exception() 58 | { 59 | promise_.set_exception(current_exception()); 60 | } 61 | }; 62 | }; 63 | 64 | template 65 | auto co_async(T&& func) 66 | { 67 | return co_future(async(std::forward(func))); 68 | } 69 | 70 | co_future compute_value() 71 | { 72 | int result = co_await co_async([] { return 42; }); 73 | co_return result; 74 | } 75 | 76 | int main() 77 | { 78 | auto value = compute_value(); 79 | cout << value.get() << endl; 80 | } 81 | -------------------------------------------------------------------------------- /code/await_msvc_future.cpp: -------------------------------------------------------------------------------- 1 | // MSVC: cl /std:c++17 /await /EHsc await_future.cpp 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | future compute_value() 10 | { 11 | int result = co_await async([] { return 42; }); 12 | co_return result; 13 | } 14 | 15 | int main() 16 | { 17 | auto value = compute_value(); 18 | cout << value.get() << endl; 19 | } 20 | -------------------------------------------------------------------------------- /code/fibonacci.cpp: -------------------------------------------------------------------------------- 1 | // Clang: clang++ -std=c++17 fibonacci.cpp 2 | // GCC: g++ -std=c++17 fibonacci.cpp 3 | // MSVC: cl /std:c++17 /EHsc fibonacci.cpp 4 | 5 | #include 6 | #include "fibonacci.hpp" 7 | 8 | int main() 9 | { 10 | for (auto i : fibonacci()) { 11 | if (i > 10000) { 12 | break; 13 | } 14 | std::cout << i << std::endl; 15 | } 16 | int count = 0; 17 | for (auto i : fibonacci()) { 18 | std::cout << i << std::endl; 19 | if (++count == 20) { 20 | break; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /code/fibonacci.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FIBONACCI_HPP 2 | #define FIBONACCI_HPP 3 | 4 | #include 5 | #include 6 | 7 | class fibonacci { 8 | public: 9 | class sentinel; 10 | class iterator; 11 | iterator begin() noexcept; 12 | sentinel end() noexcept; 13 | }; 14 | 15 | class fibonacci::sentinel {}; 16 | 17 | class fibonacci::iterator { 18 | public: 19 | // Required to satisfy iterator concept 20 | typedef ptrdiff_t difference_type; 21 | typedef uint64_t value_type; 22 | typedef const uint64_t* pointer; 23 | typedef const uint64_t& reference; 24 | typedef std::input_iterator_tag iterator_category; 25 | 26 | value_type operator*() const 27 | { 28 | return b_; 29 | } 30 | pointer operator->() const // For completeness 31 | { 32 | return &b_; 33 | } 34 | iterator& operator++() 35 | { 36 | auto tmp = a_; 37 | a_ = b_; 38 | b_ += tmp; 39 | return *this; 40 | } 41 | iterator operator++(int) // Required to satisfy iterator concept 42 | { 43 | auto tmp = *this; 44 | ++*this; 45 | return tmp; 46 | } 47 | bool operator==(const iterator& rhs) const 48 | { 49 | return b_ == rhs.b_; 50 | } 51 | bool operator!=(const iterator& rhs) const 52 | { 53 | return !operator==(rhs); 54 | } 55 | bool operator==(const sentinel&) const 56 | { 57 | return false; 58 | } 59 | bool operator!=(const sentinel&) const 60 | { 61 | return true; 62 | } 63 | 64 | private: 65 | uint64_t a_{0}; 66 | uint64_t b_{1}; 67 | }; 68 | 69 | // sentinel needs to be equality_comparable_with iterator 70 | bool operator==(const fibonacci::sentinel& lhs, 71 | const fibonacci::iterator& rhs) 72 | { 73 | return rhs == lhs; 74 | } 75 | bool operator!=(const fibonacci::sentinel& lhs, 76 | const fibonacci::iterator& rhs) 77 | { 78 | return rhs != lhs; 79 | } 80 | 81 | inline fibonacci::iterator fibonacci::begin() noexcept 82 | { 83 | return iterator(); 84 | } 85 | 86 | inline fibonacci::sentinel fibonacci::end() noexcept 87 | { 88 | return sentinel(); 89 | } 90 | 91 | #endif // FIBONACCI_HPP 92 | -------------------------------------------------------------------------------- /code/fibonacci.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from itertools import islice, takewhile 3 | 4 | 5 | def fibonacci(): 6 | a = 0 7 | b = 1 8 | while True: 9 | yield b 10 | a, b = b, a + b 11 | 12 | 13 | def main(): 14 | # Print first 20 numbers in the sequence 15 | for i in islice(fibonacci(), 20): 16 | print(i) 17 | 18 | # Print all numbers in the sequence that are less than 10000 19 | for i in takewhile(lambda x: x < 10000, fibonacci()): 20 | print(i) 21 | 22 | 23 | if __name__ == '__main__': 24 | main() 25 | -------------------------------------------------------------------------------- /code/fibonacci_coroutine2.cpp: -------------------------------------------------------------------------------- 1 | // Clang: clang++ -std=c++17 fibonacci_coroutine2.cpp -lboost_context-mt 2 | // GCC: g++ -std=c++17 fibonacci_coroutine2.cpp -lboost_context 3 | // MSVC: cl /std:c++17 /EHsc fibonacci_coroutine2.cpp 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | typedef boost::coroutines2::coroutine coro_t; 10 | 11 | void fibonacci(coro_t::push_type& yield) 12 | { 13 | uint64_t a = 0; 14 | uint64_t b = 1; 15 | while (true) { 16 | yield(b); 17 | auto tmp = a; 18 | a = b; 19 | b += tmp; 20 | } 21 | } 22 | 23 | int main() 24 | { 25 | for (auto i : coro_t::pull_type( 26 | boost::coroutines2::fixedsize_stack(), 27 | fibonacci)) { 28 | if (i >= 10000) { 29 | break; 30 | } 31 | std::cout << i << std::endl; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /code/fibonacci_coroutines_ts.cpp: -------------------------------------------------------------------------------- 1 | // Clang: clang++ -std=c++17 -fcoroutines-ts fibonacci_coroutines_ts.cpp 2 | // MSVC: cl /std:c++17 /await /EHsc fibonacci_coroutines_ts.cpp 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | using std::experimental::coroutine_handle; 9 | using std::experimental::suspend_always; 10 | 11 | class uint64_resumable { 12 | public: 13 | struct promise_type { 14 | uint64_t value_; 15 | using coro_handle = coroutine_handle; 16 | auto get_return_object() 17 | { 18 | return uint64_resumable{coro_handle::from_promise(*this)}; 19 | } 20 | constexpr auto initial_suspend() { return suspend_always(); } 21 | constexpr auto final_suspend() { return suspend_always(); } 22 | auto yield_value(uint64_t value) 23 | { 24 | value_ = value; 25 | return suspend_always(); 26 | } 27 | void return_void() {} 28 | void unhandled_exception() { std::terminate(); } 29 | }; 30 | 31 | using coro_handle = coroutine_handle; 32 | explicit uint64_resumable(coro_handle handle) : handle_(handle) {} 33 | ~uint64_resumable() { handle_.destroy(); } 34 | uint64_resumable(const uint64_resumable&) = delete; 35 | uint64_resumable(uint64_resumable&&) = default; 36 | bool resume(); 37 | uint64_t get(); 38 | 39 | private: 40 | coro_handle handle_; 41 | }; 42 | 43 | bool uint64_resumable::resume() 44 | { 45 | if (!handle_.done()) 46 | handle_.resume(); 47 | return !handle_.done(); 48 | } 49 | 50 | uint64_t uint64_resumable::get() 51 | { 52 | return handle_.promise().value_; 53 | } 54 | 55 | uint64_resumable fibonacci() 56 | { 57 | uint64_t a = 0; 58 | uint64_t b = 1; 59 | while (true) { 60 | co_yield b; 61 | auto tmp = a; 62 | a = b; 63 | b += tmp; 64 | } 65 | } 66 | 67 | int main() 68 | { 69 | uint64_resumable res = fibonacci(); 70 | while (res.resume()) { 71 | auto i = res.get(); 72 | if (i >= 10000) { 73 | break; 74 | } 75 | std::cout << i << std::endl; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /code/fibonacci_cppcoro_generator.cpp: -------------------------------------------------------------------------------- 1 | // Clang: clang++ -std=c++17 -fcoroutines-ts -I/path/to/cppcoro/include fibonacci_cppcoro_generator.cpp 2 | // MSVC: cl /std:c++17 /await /EHsc /I/path/to/cppcoro/include fibonacci_cppcoro_generator.cpp 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | using cppcoro::generator; 9 | 10 | generator fibonacci() 11 | { 12 | uint64_t a = 0; 13 | uint64_t b = 1; 14 | while (true) { 15 | co_yield b; 16 | auto tmp = a; 17 | a = b; 18 | b += tmp; 19 | } 20 | } 21 | 22 | int main() 23 | { 24 | for (auto i : fibonacci()) { 25 | if (i >= 10000) { 26 | break; 27 | } 28 | std::cout << i << std::endl; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /code/fibonacci_msvc_generator.cpp: -------------------------------------------------------------------------------- 1 | // MSVC: cl /std:c++17 /await /EHsc fibonacci_msvc_generator.cpp 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using std::experimental::generator; 8 | 9 | generator fibonacci() 10 | { 11 | uint64_t a = 0; 12 | uint64_t b = 1; 13 | while (true) { 14 | co_yield b; 15 | auto tmp = a; 16 | a = b; 17 | b += tmp; 18 | } 19 | } 20 | 21 | int main() 22 | { 23 | for (auto i : fibonacci()) { 24 | if (i >= 10000) { 25 | break; 26 | } 27 | std::cout << i << std::endl; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /code/fibonacci_range_v3.cpp: -------------------------------------------------------------------------------- 1 | // Clang: clang++ -std=c++17 -I/path/to/range-v3/include fibonacci_range_v3.cpp 2 | // GCC: g++ -std=c++17 -I/path/to/range-v3/include fibonacci_range_v3.cpp 3 | // MSVC: cl /std:c++17 /permissive- /experimental:preprocessor /EHsc /I/path/to/range-v3/include fibonacci_range_v3.cpp 4 | 5 | #include 6 | #include 7 | #include "fibonacci.hpp" 8 | 9 | int main() 10 | { 11 | for (auto i : fibonacci() | 12 | ranges::views::take_while([](uint64_t x) { 13 | return x < 10000; 14 | })) { 15 | std::cout << i << std::endl; 16 | } 17 | for (auto i : fibonacci() | ranges::views::take(20)) { 18 | std::cout << i << std::endl; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /code/fibonacci_ranges.cpp: -------------------------------------------------------------------------------- 1 | // GCC: g++ -std=c++17 -fconcepts -I/path/to/cmcstl2/include fibonacci_ranges.cpp 2 | 3 | #include 4 | #include 5 | #include "fibonacci.hpp" 6 | 7 | int main() 8 | { 9 | namespace ranges = std::experimental::ranges; 10 | for (auto i : fibonacci() | 11 | ranges::views::take_while([](uint64_t x) { 12 | return x < 10000; 13 | })) { 14 | std::cout << i << std::endl; 15 | } 16 | for (auto i : fibonacci() | ranges::views::take(20)) { 17 | std::cout << i << std::endl; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /presentation/C++ Coroutines.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adah1972/cpp_summit_2019/710d175ed5ecaf394ee5ce0cea1ad69a46a05d93/presentation/C++ Coroutines.pdf --------------------------------------------------------------------------------