mirror of
https://github.com/boostorg/boostlook.git
synced 2026-02-25 04:12:21 +00:00
665 lines
26 KiB
HTML
665 lines
26 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
|
|
<style>html.fonts-loading{visibility:hidden;opacity:0}</style>
|
|
<script>document.documentElement.classList.add('fonts-loading');</script>
|
|
|
|
<link rel="preload" href="../../_/font/NotoSansDisplay.woff2" as="font" type="font/woff2" crossorigin="anonymous" />
|
|
<link rel="preload" href="../../_/font/NotoSansDisplay-Italic.woff2" as="font" type="font/woff2" crossorigin="anonymous" />
|
|
<link rel="preload" href="../../_/font/MonaspaceNeon-Var.woff2" as="font" type="font/woff2" crossorigin="anonymous" />
|
|
<link rel="preload" href="../../_/font/MonaspaceXenon-Var.woff2" as="font" type="font/woff2" crossorigin="anonymous" />
|
|
|
|
<script>
|
|
(function() {
|
|
'use strict';
|
|
|
|
var revealed = false;
|
|
|
|
var reveal = function() {
|
|
if (revealed) return;
|
|
revealed = true;
|
|
document.documentElement.classList.remove('fonts-loading');
|
|
};
|
|
|
|
setTimeout(reveal, 3000);
|
|
|
|
if (!('FontFace' in window) || !('fonts' in document)) {
|
|
setTimeout(reveal, 100);
|
|
return;
|
|
}
|
|
|
|
var uiRoot = '../../_';
|
|
var fonts = [
|
|
{
|
|
family: 'Noto Sans',
|
|
url: uiRoot + '/font/NotoSansDisplay.woff2',
|
|
descriptors: { style: 'normal', weight: '100 900', stretch: '62.5% 100%' }
|
|
},
|
|
{
|
|
family: 'Noto Sans',
|
|
url: uiRoot + '/font/NotoSansDisplay-Italic.woff2',
|
|
descriptors: { style: 'italic', weight: '100 900', stretch: '62.5% 100%' }
|
|
},
|
|
{
|
|
family: 'Monaspace Neon',
|
|
url: uiRoot + '/font/MonaspaceNeon-Var.woff2',
|
|
descriptors: { style: 'normal', weight: '400' }
|
|
},
|
|
{
|
|
family: 'Monaspace Xenon',
|
|
url: uiRoot + '/font/MonaspaceXenon-Var.woff2',
|
|
descriptors: { style: 'italic', weight: '400' }
|
|
}
|
|
];
|
|
|
|
var loadPromises = fonts.map(function(f) {
|
|
try {
|
|
var face = new FontFace(f.family, 'url("' + f.url + '")', f.descriptors);
|
|
return face.load().then(function(loaded) {
|
|
document.fonts.add(loaded);
|
|
return loaded;
|
|
}).catch(function() {
|
|
return null;
|
|
});
|
|
} catch (e) {
|
|
return Promise.resolve(null);
|
|
}
|
|
});
|
|
|
|
Promise.all(loadPromises)
|
|
.then(function() {
|
|
return document.fonts.ready;
|
|
})
|
|
.then(reveal)
|
|
.catch(reveal);
|
|
})();
|
|
</script> <title>Part II: C++20 Syntax :: Boost Libraries Documentation</title>
|
|
<link rel="canonical" href="https://antora.cppalliance.org/develop/lib/doc/capy/2.cpp20-coroutines/2b.syntax.html">
|
|
<link rel="prev" href="2a.foundations.html">
|
|
<link rel="next" href="2c.machinery.html">
|
|
<meta name="generator" content="Antora 3.1.14">
|
|
<link rel="stylesheet" href="../../_/css/boostlook.css">
|
|
<link rel="stylesheet" href="../../_/css/site.css">
|
|
<link rel="stylesheet" href="../../_/css/vendor/tabs.css">
|
|
<script>
|
|
(function() {
|
|
if (window.self !== window.top) return;
|
|
var theme = localStorage.getItem('antora-theme');
|
|
if (!theme && window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
theme = 'dark';
|
|
}
|
|
if (theme === 'dark') document.documentElement.classList.add('dark');
|
|
})();
|
|
</script>
|
|
<script>var uiRootPath = '../../_'</script>
|
|
<link rel="icon" href="../../_/img/favicons/favicon.ico" type="image/x-icon">
|
|
<!-- Favicon configuration -->
|
|
<link rel="apple-touch-icon" sizes="180x180" href="../../_/img/favicons/apple-touch-icon.png">
|
|
<link rel="icon" type="image/png" sizes="32x32" href="../../_/img/favicons/favicon-32x32.png">
|
|
<link rel="icon" type="image/png" sizes="16x16" href="../../_/img/favicons/favicon-16x16.png">
|
|
<link rel="manifest" href="../../_/img/favicons/site.webmanifest">
|
|
<link rel="shortcut icon" href="../../_/img/favicons/favicon.ico">
|
|
</head>
|
|
<body class="article toc2 toc-left">
|
|
<div class="boostlook">
|
|
<script type="module">import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs'; mermaid.initialize({"startOnLoad":true});</script> <div id="header">
|
|
<div id="toc" class="nav-container toc2" data-component="capy" data-version="">
|
|
<aside class="nav">
|
|
<button class="nav-close"></button>
|
|
<div class="panels">
|
|
<div class="nav-panel-menu is-active" data-panel="menu">
|
|
<nav class="nav-menu">
|
|
<div class="title-row">
|
|
<h3 class="title"><a href="../index.html">Boost.Capy</a></h3>
|
|
<button class="theme-toggle" aria-label="Toggle dark mode" title="Toggle theme" style="display:none">
|
|
<i class="fas fa-sun theme-icon-light"></i>
|
|
<i class="fas fa-moon theme-icon-dark"></i>
|
|
</button> </div>
|
|
<ul class="nav-list">
|
|
<ul class="nav-list">
|
|
<li class="" data-depth="1">
|
|
<a class="nav-link" href="../index.html">Introduction</a>
|
|
</li>
|
|
<li class="" data-depth="1">
|
|
<a class="nav-link" href="../why-capy.html">Why Capy?</a>
|
|
</li>
|
|
<li class="" data-depth="1">
|
|
<a class="nav-link" href="../quick-start.html">Quick Start</a>
|
|
</li>
|
|
<li class="" data-depth="1">
|
|
<a class="nav-link" href="2.intro.html">Introduction To C++20 Coroutines</a>
|
|
</li>
|
|
<ul class="nav-list">
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="2a.foundations.html">Part I: Foundations</a>
|
|
</li>
|
|
<li class=" is-current-page" data-depth="2">
|
|
<a class="nav-link" href="2b.syntax.html">Part II: C++20 Syntax</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="2c.machinery.html">Part III: Coroutine Machinery</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="2d.advanced.html">Part IV: Advanced Topics</a>
|
|
</li>
|
|
</ul>
|
|
<li class="" data-depth="1">
|
|
<a class="nav-link" href="../3.concurrency/3.intro.html">Introduction to Concurrency</a>
|
|
</li>
|
|
<ul class="nav-list">
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../3.concurrency/3a.foundations.html">Part I: Foundations</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../3.concurrency/3b.synchronization.html">Part II: Synchronization</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../3.concurrency/3c.advanced.html">Part III: Advanced Primitives</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../3.concurrency/3d.patterns.html">Part IV: Communication & Patterns</a>
|
|
</li>
|
|
</ul>
|
|
<li class="" data-depth="1">
|
|
<a class="nav-link" href="../4.coroutines/4.intro.html">Coroutines in Capy</a>
|
|
</li>
|
|
<ul class="nav-list">
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../4.coroutines/4a.tasks.html">The task Type</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../4.coroutines/4b.launching.html">Launching Coroutines</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../4.coroutines/4c.executors.html">Executors and Execution Contexts</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../4.coroutines/4d.io-awaitable.html">The IoAwaitable Protocol</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../4.coroutines/4e.cancellation.html">Stop Tokens and Cancellation</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../4.coroutines/4f.composition.html">Concurrent Composition</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../4.coroutines/4g.allocators.html">Frame Allocators</a>
|
|
</li>
|
|
</ul>
|
|
<li class="" data-depth="1">
|
|
<a class="nav-link" href="../5.buffers/5.intro.html">Buffer Sequences</a>
|
|
</li>
|
|
<ul class="nav-list">
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../5.buffers/5a.overview.html">Why Concepts, Not Spans</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../5.buffers/5b.types.html">Buffer Types</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../5.buffers/5c.sequences.html">Buffer Sequences</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../5.buffers/5d.system-io.html">System I/O Integration</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../5.buffers/5e.algorithms.html">Buffer Algorithms</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../5.buffers/5f.dynamic.html">Dynamic Buffers</a>
|
|
</li>
|
|
</ul>
|
|
<li class="" data-depth="1">
|
|
<a class="nav-link" href="../6.streams/6.intro.html">Stream Concepts</a>
|
|
</li>
|
|
<ul class="nav-list">
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../6.streams/6a.overview.html">Overview</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../6.streams/6b.streams.html">Streams (Partial I/O)</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../6.streams/6c.sources-sinks.html">Sources and Sinks (Complete I/O)</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../6.streams/6d.buffer-concepts.html">Buffer Sources and Sinks</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../6.streams/6e.algorithms.html">Transfer Algorithms</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../6.streams/6f.isolation.html">Physical Isolation</a>
|
|
</li>
|
|
</ul>
|
|
<li class="" data-depth="1">
|
|
<a class="nav-link" href="../7.examples/7.intro.html">Example Programs</a>
|
|
</li>
|
|
<ul class="nav-list">
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../7.examples/7a.hello-task.html">Hello Task</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../7.examples/7b.producer-consumer.html">Producer-Consumer</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../7.examples/7c.buffer-composition.html">Buffer Composition</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../7.examples/7d.mock-stream-testing.html">Mock Stream Testing</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../7.examples/7e.type-erased-echo.html">Type-Erased Echo</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../7.examples/7f.timeout-cancellation.html">Timeout with Cancellation</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../7.examples/7g.parallel-fetch.html">Parallel Fetch</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../7.examples/7h.custom-dynamic-buffer.html">Custom Dynamic Buffer</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../7.examples/7i.echo-server-corosio.html">Echo Server with Corosio</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../7.examples/7j.stream-pipeline.html">Stream Pipeline</a>
|
|
</li>
|
|
</ul>
|
|
<li class="" data-depth="1">
|
|
<a class="nav-link" href="../8.design/8.intro.html">Design</a>
|
|
</li>
|
|
<ul class="nav-list">
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../8.design/8a.CapyLayering.html">Layered Abstractions</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../8.design/8b.Separation.html">Why Capy Is Separate</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../8.design/8c.ReadStream.html">ReadStream</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../8.design/8d.ReadSource.html">ReadSource</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../8.design/8e.BufferSource.html">BufferSource</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../8.design/8f.WriteStream.html">WriteStream</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../8.design/8g.WriteSink.html">WriteSink</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../8.design/8h.BufferSink.html">BufferSink</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../8.design/8i.TypeEraseAwaitable.html">Type-Erasing Awaitables</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../8.design/8j.any_buffer_sink.html">AnyBufferSink</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../8.design/8k.Executor.html">Executor</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../8.design/8l.RunApi.html">Run API</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../8.design/8m.WhyNotCobalt.html">Why Not Cobalt?</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../8.design/8n.WhyNotCobaltConcepts.html">Why Not Cobalt Concepts?</a>
|
|
</li>
|
|
<li class="" data-depth="2">
|
|
<a class="nav-link" href="../8.design/8o.WhyNotTMC.html">Why Not TooManyCooks?</a>
|
|
</li>
|
|
</ul>
|
|
<li class="" data-depth="1">
|
|
<a class="nav-link" href="../reference/boost/capy.html">Reference</a>
|
|
</li>
|
|
</ul>
|
|
</ul>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
</div>
|
|
</div> <div id="content">
|
|
<article class="doc max-width-reset">
|
|
<div class="toolbar" role="navigation">
|
|
<button class="nav-toggle"></button>
|
|
<nav class="breadcrumbs" aria-label="breadcrumbs">
|
|
<ul>
|
|
<li>
|
|
<a href="../index.html" aria-label="Home: Boost.Capy">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" viewBox="0 -960 960 960" fill="#000000" aria-hidden="true"><path d="M160-120v-480l320-240 320 240v480H560v-280H400v280H160Z"/></svg>
|
|
</a>
|
|
</li>
|
|
<li><a href="2.intro.html">Introduction To C++20 Coroutines</a></li>
|
|
<li><a href="2b.syntax.html">Part II: C++20 Syntax</a></li>
|
|
</ul>
|
|
</nav>
|
|
<div class="spirit-nav">
|
|
<a accesskey="p" href="2a.foundations.html">
|
|
<span class="material-symbols-outlined" title="Previous: Part I: Foundations">arrow_back</span>
|
|
</a>
|
|
<a accesskey="u" href="2.intro.html">
|
|
<span class="material-symbols-outlined" title="Up: Introduction To C++20 Coroutines">arrow_upward</span>
|
|
</a>
|
|
<a accesskey="n" href="2c.machinery.html">
|
|
<span class="material-symbols-outlined" title="Next: Part III: Coroutine Machinery">arrow_forward</span>
|
|
</a>
|
|
</div></div>
|
|
<h1 class="page">Part II: C++20 Syntax</h1>
|
|
<div id="preamble">
|
|
<div class="sectionbody">
|
|
<div class="paragraph">
|
|
<p>This section introduces the three C++20 keywords that create coroutines and walks you through building your first coroutine step by step.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect1">
|
|
<h2 id="_prerequisites"><a class="anchor" href="#_prerequisites"></a>Prerequisites</h2>
|
|
<div class="sectionbody">
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p>Completed <a href="2a.foundations.html" class="xref page">Part I: Foundations</a></p>
|
|
</li>
|
|
<li>
|
|
<p>Understanding of why coroutines exist and what problem they solve</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect1">
|
|
<h2 id="_the_three_keywords"><a class="anchor" href="#_the_three_keywords"></a>The Three Keywords</h2>
|
|
<div class="sectionbody">
|
|
<div class="paragraph">
|
|
<p>A function becomes a coroutine when its body contains any of three special keywords: <code>co_await</code>, <code>co_yield</code>, or <code>co_return</code>. The presence of any of these keywords signals to the compiler that the function requires coroutine machinery.</p>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_co_await"><a class="anchor" href="#_co_await"></a>co_await</h3>
|
|
<div class="paragraph">
|
|
<p>The <code>co_await</code> keyword suspends the coroutine and waits for some operation to complete. When you write <code>co_await expr</code>, the coroutine saves its state, pauses execution, and potentially allows other code to run. When the awaited operation completes, the coroutine resumes from exactly where it left off.</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-cpp hljs" data-lang="cpp">task<std::string> fetch_page(std::string url)
|
|
{
|
|
auto response = co_await http_get(url); // suspends until HTTP completes
|
|
return response.body; // continues after resumption
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_co_yield"><a class="anchor" href="#_co_yield"></a>co_yield</h3>
|
|
<div class="paragraph">
|
|
<p>The <code>co_yield</code> keyword produces a value and suspends the coroutine. This pattern creates <strong>generators</strong>—functions that produce sequences of values one at a time. After yielding a value, the coroutine pauses until someone asks for the next value.</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-cpp hljs" data-lang="cpp">generator<int> count_to(int n)
|
|
{
|
|
for (int i = 1; i <= n; ++i)
|
|
{
|
|
co_yield i; // produce value, suspend, resume when next value requested
|
|
}
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_co_return"><a class="anchor" href="#_co_return"></a>co_return</h3>
|
|
<div class="paragraph">
|
|
<p>The <code>co_return</code> keyword completes the coroutine and optionally provides a final result. Unlike a regular <code>return</code> statement, <code>co_return</code> interacts with the coroutine machinery to properly finalize the coroutine’s state.</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-cpp hljs" data-lang="cpp">task<int> compute()
|
|
{
|
|
int result = 42;
|
|
co_return result; // completes the coroutine with value 42
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>For coroutines that do not return a value, use <code>co_return;</code> without an argument.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect1">
|
|
<h2 id="_your_first_coroutine"><a class="anchor" href="#_your_first_coroutine"></a>Your First Coroutine</h2>
|
|
<div class="sectionbody">
|
|
<div class="paragraph">
|
|
<p>The distinction between regular functions and coroutines matters because they behave fundamentally differently at runtime:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p>A regular function allocates its local variables on the stack. When it returns, those variables are gone.</p>
|
|
</li>
|
|
<li>
|
|
<p>A coroutine allocates its local variables in a heap-allocated <strong>coroutine frame</strong>. When it suspends, those variables persist. When it resumes, they are still there.</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Here is the minimal structure needed to create a coroutine:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-cpp hljs" data-lang="cpp">#include <coroutine>
|
|
|
|
struct SimpleCoroutine
|
|
{
|
|
struct promise_type
|
|
{
|
|
SimpleCoroutine get_return_object() { return {}; }
|
|
std::suspend_never initial_suspend() { return {}; }
|
|
std::suspend_never final_suspend() noexcept { return {}; }
|
|
void return_void() {}
|
|
void unhandled_exception() {}
|
|
};
|
|
};
|
|
|
|
SimpleCoroutine my_first_coroutine()
|
|
{
|
|
co_return; // This makes it a coroutine
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The <code>promise_type</code> nested structure provides the minimum scaffolding the compiler needs. You will learn what each method does in <a href="2c.machinery.html" class="xref page">Part III: Coroutine Machinery</a>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>For now, observe that the presence of <code>co_return</code> transforms what looks like a regular function into a coroutine. If you try to compile a function with coroutine keywords but without proper infrastructure, the compiler will produce errors.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect1">
|
|
<h2 id="_awaitables_and_awaiters"><a class="anchor" href="#_awaitables_and_awaiters"></a>Awaitables and Awaiters</h2>
|
|
<div class="sectionbody">
|
|
<div class="paragraph">
|
|
<p>When you write <code>co_await expr</code>, the expression <code>expr</code> must be an <strong>awaitable</strong>—something that knows how to suspend and resume a coroutine. The awaitable produces an <strong>awaiter</strong> object that implements three methods:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p><code>await_ready()</code> — Returns <code>true</code> if the result is immediately available and no suspension is needed</p>
|
|
</li>
|
|
<li>
|
|
<p><code>await_suspend(handle)</code> — Called when the coroutine suspends; receives a handle to the coroutine for later resumption</p>
|
|
</li>
|
|
<li>
|
|
<p><code>await_resume()</code> — Called when the coroutine resumes; its return value becomes the value of the <code>co_await</code> expression</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_example_understanding_the_awaiter_protocol"><a class="anchor" href="#_example_understanding_the_awaiter_protocol"></a>Example: Understanding the Awaiter Protocol</h3>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-cpp hljs" data-lang="cpp">#include <coroutine>
|
|
#include <iostream>
|
|
|
|
struct ReturnObject
|
|
{
|
|
struct promise_type
|
|
{
|
|
ReturnObject get_return_object() { return {}; }
|
|
std::suspend_never initial_suspend() { return {}; }
|
|
std::suspend_never final_suspend() noexcept { return {}; }
|
|
void return_void() {}
|
|
void unhandled_exception() {}
|
|
};
|
|
};
|
|
|
|
struct Awaiter
|
|
{
|
|
std::coroutine_handle<>* handle_out;
|
|
|
|
bool await_ready() { return false; } // always suspend
|
|
|
|
void await_suspend(std::coroutine_handle<> h)
|
|
{
|
|
*handle_out = h; // store handle for later resumption
|
|
}
|
|
|
|
void await_resume() {} // nothing to return
|
|
};
|
|
|
|
ReturnObject counter(std::coroutine_handle<>* handle)
|
|
{
|
|
Awaiter awaiter{handle};
|
|
|
|
for (unsigned i = 0; ; ++i)
|
|
{
|
|
std::cout << "counter: " << i << std::endl;
|
|
co_await awaiter;
|
|
}
|
|
}
|
|
|
|
int main()
|
|
{
|
|
std::coroutine_handle<> h;
|
|
counter(&h);
|
|
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
std::cout << "main: resuming" << std::endl;
|
|
h();
|
|
}
|
|
|
|
h.destroy();
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p><strong>Output:</strong></p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-asciidoc hljs" data-lang="asciidoc">counter: 0
|
|
main: resuming
|
|
counter: 1
|
|
main: resuming
|
|
counter: 2
|
|
main: resuming
|
|
counter: 3</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Study this execution flow:</p>
|
|
</div>
|
|
<div class="olist arabic">
|
|
<ol class="arabic">
|
|
<li>
|
|
<p><code>main</code> calls <code>counter</code>, passing the address of a coroutine handle</p>
|
|
</li>
|
|
<li>
|
|
<p><code>counter</code> begins executing, prints "counter: 0", and reaches <code>co_await awaiter</code></p>
|
|
</li>
|
|
<li>
|
|
<p><code>await_ready()</code> returns <code>false</code>, so suspension proceeds</p>
|
|
</li>
|
|
<li>
|
|
<p><code>await_suspend</code> receives a handle to the suspended coroutine and stores it in <code>main’s variable `h</code></p>
|
|
</li>
|
|
<li>
|
|
<p>Control returns to <code>main</code>, which now holds a handle to the suspended coroutine</p>
|
|
</li>
|
|
<li>
|
|
<p><code>main</code> calls <code>h()</code>, which resumes the coroutine</p>
|
|
</li>
|
|
<li>
|
|
<p>The coroutine continues from where it left off, increments <code>i</code>, prints "counter: 1", and suspends again</p>
|
|
</li>
|
|
<li>
|
|
<p>This cycle repeats until <code>main</code> destroys the coroutine</p>
|
|
</li>
|
|
</ol>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The variable <code>i</code> inside <code>counter</code> maintains its value across all these suspension and resumption cycles.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_standard_awaiters"><a class="anchor" href="#_standard_awaiters"></a>Standard Awaiters</h3>
|
|
<div class="paragraph">
|
|
<p>The C++ standard library provides two predefined awaiters:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p><code>std::suspend_always</code> — <code>await_ready()</code> returns <code>false</code> (always suspend)</p>
|
|
</li>
|
|
<li>
|
|
<p><code>std::suspend_never</code> — <code>await_ready()</code> returns <code>true</code> (never suspend)</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>These are useful building blocks for promise types and custom awaitables.</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-cpp hljs" data-lang="cpp">// suspend_always causes suspension at this point
|
|
co_await std::suspend_always{};
|
|
|
|
// suspend_never continues immediately without suspending
|
|
co_await std::suspend_never{};</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>You have now learned the three coroutine keywords and how awaitables work. In the next section, you will learn about the promise type and coroutine handle—the machinery that makes coroutines function.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="edit-this-page">
|
|
<a href="https://github.com/cppalliance/capy/edit/develop/doc/modules/ROOT/pages/2.cpp20-coroutines/2b.syntax.adoc">Edit this Page</a>
|
|
</div>
|
|
<nav class="pagination">
|
|
<span class="prev"><a href="2a.foundations.html">Part I: Foundations</a></span>
|
|
<span class="next"><a href="2c.machinery.html">Part III: Coroutine Machinery</a></span>
|
|
</nav>
|
|
</article>
|
|
</div>
|
|
<div id="footer">
|
|
<script id="site-script" src="../../_/js/site.js" data-ui-root-path="../../_"></script>
|
|
<script async src="../../_/js/vendor/highlight.js"></script>
|
|
<script async src="../../_/js/vendor/tabs.js" data-sync-storage-key="preferred-tab"></script>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|