| <html> |
| <head> |
| <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII"> |
| <title>Motivation</title> |
| <link rel="stylesheet" href="../../../../../doc/src/boostbook.css" type="text/css"> |
| <meta name="generator" content="DocBook XSL Stylesheets V1.78.1"> |
| <link rel="home" href="../index.html" title="Chapter 1. Coroutine"> |
| <link rel="up" href="../index.html" title="Chapter 1. Coroutine"> |
| <link rel="prev" href="intro.html" title="Introduction"> |
| <link rel="next" href="coroutine.html" title="Coroutine"> |
| </head> |
| <body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"> |
| <table cellpadding="2" width="100%"><tr> |
| <td valign="top"><img alt="Boost C++ Libraries" width="277" height="86" src="../../../../../boost.png"></td> |
| <td align="center"><a href="../../../../../index.html">Home</a></td> |
| <td align="center"><a href="../../../../../libs/libraries.htm">Libraries</a></td> |
| <td align="center"><a href="http://www.boost.org/users/people.html">People</a></td> |
| <td align="center"><a href="http://www.boost.org/users/faq.html">FAQ</a></td> |
| <td align="center"><a href="../../../../../more/index.htm">More</a></td> |
| </tr></table> |
| <hr> |
| <div class="spirit-nav"> |
| <a accesskey="p" href="intro.html"><img src="../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../index.html"><img src="../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../index.html"><img src="../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="coroutine.html"><img src="../../../../../doc/src/images/next.png" alt="Next"></a> |
| </div> |
| <div class="section"> |
| <div class="titlepage"><div><div><h2 class="title" style="clear: both"> |
| <a name="coroutine.motivation"></a><a class="link" href="motivation.html" title="Motivation">Motivation</a> |
| </h2></div></div></div> |
| <p> |
| In order to support a broad range of execution control behaviour the coroutine |
| types of <span class="emphasis"><em>symmetric_coroutine<></em></span> and <span class="emphasis"><em>asymmetric_coroutine<></em></span> |
| can be used to <span class="emphasis"><em>escape-and-reenter</em></span> loops, to <span class="emphasis"><em>escape-and-reenter</em></span> |
| recursive computations and for <span class="emphasis"><em>cooperative</em></span> multitasking |
| helping to solve problems in a much simpler and more elegant way than with |
| only a single flow of control. |
| </p> |
| <h4> |
| <a name="coroutine.motivation.h0"></a> |
| <span class="phrase"><a name="coroutine.motivation.event_driven_model"></a></span><a class="link" href="motivation.html#coroutine.motivation.event_driven_model">event-driven |
| model</a> |
| </h4> |
| <p> |
| The event-driven model is a programming paradigm where the flow of a program |
| is determined by events. The events are generated by multiple independent sources |
| and an event-dispatcher, waiting on all external sources, triggers callback |
| functions (event-handlers) whenever one of those events is detected (event-loop). |
| The application is divided into event selection (detection) and event handling. |
| </p> |
| <p> |
| <span class="inlinemediaobject"><img src="../../../../../libs/coroutine/doc/images/event_model.png" align="middle" alt="event_model"></span> |
| </p> |
| <p> |
| The resulting applications are highly scalable, flexible, have high responsiveness |
| and the components are loosely coupled. This makes the event-driven model suitable |
| for user interface applications, rule-based productions systems or applications |
| dealing with asynchronous I/O (for instance network servers). |
| </p> |
| <h4> |
| <a name="coroutine.motivation.h1"></a> |
| <span class="phrase"><a name="coroutine.motivation.event_based_asynchronous_paradigm"></a></span><a class="link" href="motivation.html#coroutine.motivation.event_based_asynchronous_paradigm">event-based |
| asynchronous paradigm</a> |
| </h4> |
| <p> |
| A classic synchronous console program issues an I/O request (e.g. for user |
| input or filesystem data) and blocks until the request is complete. |
| </p> |
| <p> |
| In contrast, an asynchronous I/O function initiates the physical operation |
| but immediately returns to its caller, even though the operation is not yet |
| complete. A program written to leverage this functionality does not block: |
| it can proceed with other work (including other I/O requests in parallel) while |
| the original operation is still pending. When the operation completes, the |
| program is notified. Because asynchronous applications spend less overall time |
| waiting for operations, they can outperform synchronous programs. |
| </p> |
| <p> |
| Events are one of the paradigms for asynchronous execution, but not all asynchronous |
| systems use events. Although asynchronous programming can be done using threads, |
| they come with their own costs: |
| </p> |
| <div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "> |
| <li class="listitem"> |
| hard to program (traps for the unwary) |
| </li> |
| <li class="listitem"> |
| memory requirements are high |
| </li> |
| <li class="listitem"> |
| large overhead with creation and maintenance of thread state |
| </li> |
| <li class="listitem"> |
| expensive context switching between threads |
| </li> |
| </ul></div> |
| <p> |
| The event-based asynchronous model avoids those issues: |
| </p> |
| <div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "> |
| <li class="listitem"> |
| simpler because of the single stream of instructions |
| </li> |
| <li class="listitem"> |
| much less expensive context switches |
| </li> |
| </ul></div> |
| <p> |
| The downside of this paradigm consists in a sub-optimal program structure. |
| An event-driven program is required to split its code into multiple small callback |
| functions, i.e. the code is organized in a sequence of small steps that execute |
| intermittently. An algorithm that would usually be expressed as a hierarchy |
| of functions and loops must be transformed into callbacks. The complete state |
| has to be stored into a data structure while the control flow returns to the |
| event-loop. As a consequence, event-driven applications are often tedious and |
| confusing to write. Each callback introduces a new scope, error callback etc. |
| The sequential nature of the algorithm is split into multiple callstacks, making |
| the application hard to debug. Exception handlers are restricted to local handlers: |
| it is impossible to wrap a sequence of events into a single try-catch block. |
| The use of local variables, while/for loops, recursions etc. together with |
| the event-loop is not possible. The code becomes less expressive. |
| </p> |
| <p> |
| In the past, code using asio's <span class="emphasis"><em>asynchronous operations</em></span> |
| was convoluted by callback functions. |
| </p> |
| <pre class="programlisting"><span class="keyword">class</span> <span class="identifier">session</span> |
| <span class="special">{</span> |
| <span class="keyword">public</span><span class="special">:</span> |
| <span class="identifier">session</span><span class="special">(</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_service</span><span class="special">&</span> <span class="identifier">io_service</span><span class="special">)</span> <span class="special">:</span> |
| <span class="identifier">socket_</span><span class="special">(</span><span class="identifier">io_service</span><span class="special">)</span> <span class="comment">// construct a TCP-socket from io_service</span> |
| <span class="special">{}</span> |
| |
| <span class="identifier">tcp</span><span class="special">::</span><span class="identifier">socket</span><span class="special">&</span> <span class="identifier">socket</span><span class="special">(){</span> |
| <span class="keyword">return</span> <span class="identifier">socket_</span><span class="special">;</span> |
| <span class="special">}</span> |
| |
| <span class="keyword">void</span> <span class="identifier">start</span><span class="special">(){</span> |
| <span class="comment">// initiate asynchronous read; handle_read() is callback-function</span> |
| <span class="identifier">socket_</span><span class="special">.</span><span class="identifier">async_read_some</span><span class="special">(</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">buffer</span><span class="special">(</span><span class="identifier">data_</span><span class="special">,</span><span class="identifier">max_length</span><span class="special">),</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">bind</span><span class="special">(&</span><span class="identifier">session</span><span class="special">::</span><span class="identifier">handle_read</span><span class="special">,</span><span class="keyword">this</span><span class="special">,</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">placeholders</span><span class="special">::</span><span class="identifier">error</span><span class="special">,</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">placeholders</span><span class="special">::</span><span class="identifier">bytes_transferred</span><span class="special">));</span> |
| <span class="special">}</span> |
| |
| <span class="keyword">private</span><span class="special">:</span> |
| <span class="keyword">void</span> <span class="identifier">handle_read</span><span class="special">(</span><span class="keyword">const</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">system</span><span class="special">::</span><span class="identifier">error_code</span><span class="special">&</span> <span class="identifier">error</span><span class="special">,</span> |
| <span class="identifier">size_t</span> <span class="identifier">bytes_transferred</span><span class="special">){</span> |
| <span class="keyword">if</span> <span class="special">(!</span><span class="identifier">error</span><span class="special">)</span> |
| <span class="comment">// initiate asynchronous write; handle_write() is callback-function</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">async_write</span><span class="special">(</span><span class="identifier">socket_</span><span class="special">,</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">buffer</span><span class="special">(</span><span class="identifier">data_</span><span class="special">,</span><span class="identifier">bytes_transferred</span><span class="special">),</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">bind</span><span class="special">(&</span><span class="identifier">session</span><span class="special">::</span><span class="identifier">handle_write</span><span class="special">,</span><span class="keyword">this</span><span class="special">,</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">placeholders</span><span class="special">::</span><span class="identifier">error</span><span class="special">));</span> |
| <span class="keyword">else</span> |
| <span class="keyword">delete</span> <span class="keyword">this</span><span class="special">;</span> |
| <span class="special">}</span> |
| |
| <span class="keyword">void</span> <span class="identifier">handle_write</span><span class="special">(</span><span class="keyword">const</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">system</span><span class="special">::</span><span class="identifier">error_code</span><span class="special">&</span> <span class="identifier">error</span><span class="special">){</span> |
| <span class="keyword">if</span> <span class="special">(!</span><span class="identifier">error</span><span class="special">)</span> |
| <span class="comment">// initiate asynchronous read; handle_read() is callback-function</span> |
| <span class="identifier">socket_</span><span class="special">.</span><span class="identifier">async_read_some</span><span class="special">(</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">buffer</span><span class="special">(</span><span class="identifier">data_</span><span class="special">,</span><span class="identifier">max_length</span><span class="special">),</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">bind</span><span class="special">(&</span><span class="identifier">session</span><span class="special">::</span><span class="identifier">handle_read</span><span class="special">,</span><span class="keyword">this</span><span class="special">,</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">placeholders</span><span class="special">::</span><span class="identifier">error</span><span class="special">,</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">placeholders</span><span class="special">::</span><span class="identifier">bytes_transferred</span><span class="special">));</span> |
| <span class="keyword">else</span> |
| <span class="keyword">delete</span> <span class="keyword">this</span><span class="special">;</span> |
| <span class="special">}</span> |
| |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">ip</span><span class="special">::</span><span class="identifier">tcp</span><span class="special">::</span><span class="identifier">socket</span> <span class="identifier">socket_</span><span class="special">;</span> |
| <span class="keyword">enum</span> <span class="special">{</span> <span class="identifier">max_length</span><span class="special">=</span><span class="number">1024</span> <span class="special">};</span> |
| <span class="keyword">char</span> <span class="identifier">data_</span><span class="special">[</span><span class="identifier">max_length</span><span class="special">];</span> |
| <span class="special">};</span> |
| </pre> |
| <p> |
| In this example, a simple echo server, the logic is split into three member |
| functions - local state (such as data buffer) is moved to member variables. |
| </p> |
| <p> |
| <span class="bold"><strong>Boost.Asio</strong></span> provides with its new <span class="emphasis"><em>asynchronous |
| result</em></span> feature a new framework combining event-driven model and |
| coroutines, hiding the complexity of event-driven programming and permitting |
| the style of classic sequential code. The application is not required to pass |
| callback functions to asynchronous operations and local state is kept as local |
| variables. Therefore the code is much easier to read and understand. <a href="#ftn.coroutine.motivation.f0" class="footnote" name="coroutine.motivation.f0"><sup class="footnote">[4]</sup></a>. <span class="emphasis"><em>boost::asio::yield_context</em></span> internally uses |
| <span class="bold"><strong>Boost.Coroutine</strong></span>: |
| </p> |
| <pre class="programlisting"><span class="keyword">void</span> <span class="identifier">session</span><span class="special">(</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_service</span><span class="special">&</span> <span class="identifier">io_service</span><span class="special">){</span> |
| <span class="comment">// construct TCP-socket from io_service</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">ip</span><span class="special">::</span><span class="identifier">tcp</span><span class="special">::</span><span class="identifier">socket</span> <span class="identifier">socket</span><span class="special">(</span><span class="identifier">io_service</span><span class="special">);</span> |
| |
| <span class="keyword">try</span><span class="special">{</span> |
| <span class="keyword">for</span><span class="special">(;;){</span> |
| <span class="comment">// local data-buffer</span> |
| <span class="keyword">char</span> <span class="identifier">data</span><span class="special">[</span><span class="identifier">max_length</span><span class="special">];</span> |
| |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">system</span><span class="special">::</span><span class="identifier">error_code</span> <span class="identifier">ec</span><span class="special">;</span> |
| |
| <span class="comment">// read asynchronous data from socket</span> |
| <span class="comment">// execution context will be suspended until</span> |
| <span class="comment">// some bytes are read from socket</span> |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">size_t</span> <span class="identifier">length</span><span class="special">=</span><span class="identifier">socket</span><span class="special">.</span><span class="identifier">async_read_some</span><span class="special">(</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">buffer</span><span class="special">(</span><span class="identifier">data</span><span class="special">),</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">yield</span><span class="special">[</span><span class="identifier">ec</span><span class="special">]);</span> |
| <span class="keyword">if</span> <span class="special">(</span><span class="identifier">ec</span><span class="special">==</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">error</span><span class="special">::</span><span class="identifier">eof</span><span class="special">)</span> |
| <span class="keyword">break</span><span class="special">;</span> <span class="comment">//connection closed cleanly by peer</span> |
| <span class="keyword">else</span> <span class="keyword">if</span><span class="special">(</span><span class="identifier">ec</span><span class="special">)</span> |
| <span class="keyword">throw</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">system</span><span class="special">::</span><span class="identifier">system_error</span><span class="special">(</span><span class="identifier">ec</span><span class="special">);</span> <span class="comment">//some other error</span> |
| |
| <span class="comment">// write some bytes asynchronously</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">async_write</span><span class="special">(</span> |
| <span class="identifier">socket</span><span class="special">,</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">buffer</span><span class="special">(</span><span class="identifier">data</span><span class="special">,</span><span class="identifier">length</span><span class="special">),</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">yield</span><span class="special">[</span><span class="identifier">ec</span><span class="special">]);</span> |
| <span class="keyword">if</span> <span class="special">(</span><span class="identifier">ec</span><span class="special">==</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">error</span><span class="special">::</span><span class="identifier">eof</span><span class="special">)</span> |
| <span class="keyword">break</span><span class="special">;</span> <span class="comment">//connection closed cleanly by peer</span> |
| <span class="keyword">else</span> <span class="keyword">if</span><span class="special">(</span><span class="identifier">ec</span><span class="special">)</span> |
| <span class="keyword">throw</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">system</span><span class="special">::</span><span class="identifier">system_error</span><span class="special">(</span><span class="identifier">ec</span><span class="special">);</span> <span class="comment">//some other error</span> |
| <span class="special">}</span> |
| <span class="special">}</span> <span class="keyword">catch</span><span class="special">(</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">exception</span> <span class="keyword">const</span><span class="special">&</span> <span class="identifier">e</span><span class="special">){</span> |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">cerr</span><span class="special"><<</span><span class="string">"Exception: "</span><span class="special"><<</span><span class="identifier">e</span><span class="special">.</span><span class="identifier">what</span><span class="special">()<<</span><span class="string">"\n"</span><span class="special">;</span> |
| <span class="special">}</span> |
| <span class="special">}</span> |
| </pre> |
| <p> |
| In contrast to the previous example this one gives the impression of sequential |
| code and local data (<span class="emphasis"><em>data</em></span>) while using asynchronous operations |
| (<span class="emphasis"><em>async_read()</em></span>, <span class="emphasis"><em>async_write()</em></span>). The |
| algorithm is implemented in one function and error handling is done by one |
| try-catch block. |
| </p> |
| <h4> |
| <a name="coroutine.motivation.h2"></a> |
| <span class="phrase"><a name="coroutine.motivation.recursive_sax_parsing"></a></span><a class="link" href="motivation.html#coroutine.motivation.recursive_sax_parsing">recursive |
| SAX parsing</a> |
| </h4> |
| <p> |
| To someone who knows SAX, the phrase "recursive SAX parsing" might |
| sound nonsensical. You get callbacks from SAX; you have to manage the element |
| stack yourself. If you want recursive XML processing, you must first read the |
| entire DOM into memory, then walk the tree. |
| </p> |
| <p> |
| But coroutines let you invert the flow of control so you can ask for SAX events. |
| Once you can do that, you can process them recursively. |
| </p> |
| <pre class="programlisting"><span class="comment">// Represent a subset of interesting SAX events</span> |
| <span class="keyword">struct</span> <span class="identifier">BaseEvent</span><span class="special">{</span> |
| <span class="identifier">BaseEvent</span><span class="special">(</span><span class="keyword">const</span> <span class="identifier">BaseEvent</span><span class="special">&)=</span><span class="keyword">delete</span><span class="special">;</span> |
| <span class="identifier">BaseEvent</span><span class="special">&</span> <span class="keyword">operator</span><span class="special">=(</span><span class="keyword">const</span> <span class="identifier">BaseEvent</span><span class="special">&)=</span><span class="keyword">delete</span><span class="special">;</span> |
| <span class="special">};</span> |
| |
| <span class="comment">// End of document or element</span> |
| <span class="keyword">struct</span> <span class="identifier">CloseEvent</span><span class="special">:</span> <span class="keyword">public</span> <span class="identifier">BaseEvent</span><span class="special">{</span> |
| <span class="comment">// CloseEvent binds (without copying) the TagType reference.</span> |
| <span class="identifier">CloseEvent</span><span class="special">(</span><span class="keyword">const</span> <span class="identifier">xml</span><span class="special">::</span><span class="identifier">sax</span><span class="special">::</span><span class="identifier">Parser</span><span class="special">::</span><span class="identifier">TagType</span><span class="special">&</span> <span class="identifier">name</span><span class="special">):</span> |
| <span class="identifier">mName</span><span class="special">(</span><span class="identifier">name</span><span class="special">)</span> |
| <span class="special">{}</span> |
| |
| <span class="keyword">const</span> <span class="identifier">xml</span><span class="special">::</span><span class="identifier">sax</span><span class="special">::</span><span class="identifier">Parser</span><span class="special">::</span><span class="identifier">TagType</span><span class="special">&</span> <span class="identifier">mName</span><span class="special">;</span> |
| <span class="special">};</span> |
| |
| <span class="comment">// Start of document or element</span> |
| <span class="keyword">struct</span> <span class="identifier">OpenEvent</span><span class="special">:</span> <span class="keyword">public</span> <span class="identifier">CloseEvent</span><span class="special">{</span> |
| <span class="comment">// In addition to CloseEvent's TagType, OpenEvent binds AttributeIterator.</span> |
| <span class="identifier">OpenEvent</span><span class="special">(</span><span class="keyword">const</span> <span class="identifier">xml</span><span class="special">::</span><span class="identifier">sax</span><span class="special">::</span><span class="identifier">Parser</span><span class="special">::</span><span class="identifier">TagType</span><span class="special">&</span> <span class="identifier">name</span><span class="special">,</span> |
| <span class="identifier">xml</span><span class="special">::</span><span class="identifier">sax</span><span class="special">::</span><span class="identifier">AttributeIterator</span><span class="special">&</span> <span class="identifier">attrs</span><span class="special">):</span> |
| <span class="identifier">CloseEvent</span><span class="special">(</span><span class="identifier">name</span><span class="special">),</span> |
| <span class="identifier">mAttrs</span><span class="special">(</span><span class="identifier">attrs</span><span class="special">)</span> |
| <span class="special">{}</span> |
| |
| <span class="identifier">xml</span><span class="special">::</span><span class="identifier">sax</span><span class="special">::</span><span class="identifier">AttributeIterator</span><span class="special">&</span> <span class="identifier">mAttrs</span><span class="special">;</span> |
| <span class="special">};</span> |
| |
| <span class="comment">// text within an element</span> |
| <span class="keyword">struct</span> <span class="identifier">TextEvent</span><span class="special">:</span> <span class="keyword">public</span> <span class="identifier">BaseEvent</span><span class="special">{</span> |
| <span class="comment">// TextEvent binds the CharIterator.</span> |
| <span class="identifier">TextEvent</span><span class="special">(</span><span class="identifier">xml</span><span class="special">::</span><span class="identifier">sax</span><span class="special">::</span><span class="identifier">CharIterator</span><span class="special">&</span> <span class="identifier">text</span><span class="special">):</span> |
| <span class="identifier">mText</span><span class="special">(</span><span class="identifier">text</span><span class="special">)</span> |
| <span class="special">{}</span> |
| |
| <span class="identifier">xml</span><span class="special">::</span><span class="identifier">sax</span><span class="special">::</span><span class="identifier">CharIterator</span><span class="special">&</span> <span class="identifier">mText</span><span class="special">;</span> |
| <span class="special">};</span> |
| |
| <span class="comment">// The parsing coroutine instantiates BaseEvent subclass instances and</span> |
| <span class="comment">// successively shows them to the main program. It passes a reference so we</span> |
| <span class="comment">// don't slice the BaseEvent subclass.</span> |
| <span class="keyword">typedef</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">coroutines</span><span class="special">::</span><span class="identifier">asymmetric_coroutine</span><span class="special"><</span><span class="keyword">const</span> <span class="identifier">BaseEvent</span><span class="special">&></span> <span class="identifier">coro_t</span><span class="special">;</span> |
| |
| <span class="keyword">void</span> <span class="identifier">parser</span><span class="special">(</span><span class="identifier">coro_t</span><span class="special">::</span><span class="identifier">push_type</span><span class="special">&</span> <span class="identifier">sink</span><span class="special">,</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">istream</span><span class="special">&</span> <span class="identifier">in</span><span class="special">){</span> |
| <span class="identifier">xml</span><span class="special">::</span><span class="identifier">sax</span><span class="special">::</span><span class="identifier">Parser</span> <span class="identifier">xparser</span><span class="special">;</span> |
| <span class="comment">// startDocument() will send OpenEvent</span> |
| <span class="identifier">xparser</span><span class="special">.</span><span class="identifier">startDocument</span><span class="special">([&</span><span class="identifier">sink</span><span class="special">](</span><span class="keyword">const</span> <span class="identifier">xml</span><span class="special">::</span><span class="identifier">sax</span><span class="special">::</span><span class="identifier">Parser</span><span class="special">::</span><span class="identifier">TagType</span><span class="special">&</span> <span class="identifier">name</span><span class="special">,</span> |
| <span class="identifier">xml</span><span class="special">::</span><span class="identifier">sax</span><span class="special">::</span><span class="identifier">AttributeIterator</span><span class="special">&</span> <span class="identifier">attrs</span><span class="special">)</span> |
| <span class="special">{</span> |
| <span class="identifier">sink</span><span class="special">(</span><span class="identifier">OpenEvent</span><span class="special">(</span><span class="identifier">name</span><span class="special">,</span><span class="identifier">attrs</span><span class="special">));</span> |
| <span class="special">});</span> |
| <span class="comment">// startTag() will likewise send OpenEvent</span> |
| <span class="identifier">xparser</span><span class="special">.</span><span class="identifier">startTag</span><span class="special">([&</span><span class="identifier">sink</span><span class="special">](</span><span class="keyword">const</span> <span class="identifier">xml</span><span class="special">::</span><span class="identifier">sax</span><span class="special">::</span><span class="identifier">Parser</span><span class="special">::</span><span class="identifier">TagType</span><span class="special">&</span> <span class="identifier">name</span><span class="special">,</span> |
| <span class="identifier">xml</span><span class="special">::</span><span class="identifier">sax</span><span class="special">::</span><span class="identifier">AttributeIterator</span><span class="special">&</span> <span class="identifier">attrs</span><span class="special">)</span> |
| <span class="special">{</span> |
| <span class="identifier">sink</span><span class="special">(</span><span class="identifier">OpenEvent</span><span class="special">(</span><span class="identifier">name</span><span class="special">,</span><span class="identifier">attrs</span><span class="special">));</span> |
| <span class="special">});</span> |
| <span class="comment">// endTag() will send CloseEvent</span> |
| <span class="identifier">xparser</span><span class="special">.</span><span class="identifier">endTag</span><span class="special">([&</span><span class="identifier">sink</span><span class="special">](</span><span class="keyword">const</span> <span class="identifier">xml</span><span class="special">::</span><span class="identifier">sax</span><span class="special">::</span><span class="identifier">Parser</span><span class="special">::</span><span class="identifier">TagType</span><span class="special">&</span> <span class="identifier">name</span><span class="special">)</span> |
| <span class="special">{</span> |
| <span class="identifier">sink</span><span class="special">(</span><span class="identifier">CloseEvent</span><span class="special">(</span><span class="identifier">name</span><span class="special">));</span> |
| <span class="special">});</span> |
| <span class="comment">// endDocument() will likewise send CloseEvent</span> |
| <span class="identifier">xparser</span><span class="special">.</span><span class="identifier">endDocument</span><span class="special">([&</span><span class="identifier">sink</span><span class="special">](</span><span class="keyword">const</span> <span class="identifier">xml</span><span class="special">::</span><span class="identifier">sax</span><span class="special">::</span><span class="identifier">Parser</span><span class="special">::</span><span class="identifier">TagType</span><span class="special">&</span> <span class="identifier">name</span><span class="special">)</span> |
| <span class="special">{</span> |
| <span class="identifier">sink</span><span class="special">(</span><span class="identifier">CloseEvent</span><span class="special">(</span><span class="identifier">name</span><span class="special">));</span> |
| <span class="special">});</span> |
| <span class="comment">// characters() will send TextEvent</span> |
| <span class="identifier">xparser</span><span class="special">.</span><span class="identifier">characters</span><span class="special">([&</span><span class="identifier">sink</span><span class="special">](</span><span class="identifier">xml</span><span class="special">::</span><span class="identifier">sax</span><span class="special">::</span><span class="identifier">CharIterator</span><span class="special">&</span> <span class="identifier">text</span><span class="special">)</span> |
| <span class="special">{</span> |
| <span class="identifier">sink</span><span class="special">(</span><span class="identifier">TextEvent</span><span class="special">(</span><span class="identifier">text</span><span class="special">));</span> |
| <span class="special">});</span> |
| <span class="keyword">try</span> |
| <span class="special">{</span> |
| <span class="comment">// parse the document, firing all the above</span> |
| <span class="identifier">xparser</span><span class="special">.</span><span class="identifier">parse</span><span class="special">(</span><span class="identifier">in</span><span class="special">);</span> |
| <span class="special">}</span> |
| <span class="keyword">catch</span> <span class="special">(</span><span class="identifier">xml</span><span class="special">::</span><span class="identifier">Exception</span> <span class="identifier">e</span><span class="special">)</span> |
| <span class="special">{</span> |
| <span class="comment">// xml::sax::Parser throws xml::Exception. Helpfully translate the</span> |
| <span class="comment">// name and provide it as the what() string.</span> |
| <span class="keyword">throw</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">runtime_error</span><span class="special">(</span><span class="identifier">exception_name</span><span class="special">(</span><span class="identifier">e</span><span class="special">));</span> |
| <span class="special">}</span> |
| <span class="special">}</span> |
| |
| <span class="comment">// Recursively traverse the incoming XML document on the fly, pulling</span> |
| <span class="comment">// BaseEvent& references from 'events'.</span> |
| <span class="comment">// 'indent' illustrates the level of recursion.</span> |
| <span class="comment">// Each time we're called, we've just retrieved an OpenEvent from 'events';</span> |
| <span class="comment">// accept that as a param.</span> |
| <span class="comment">// Return the CloseEvent that ends this element.</span> |
| <span class="keyword">const</span> <span class="identifier">CloseEvent</span><span class="special">&</span> <span class="identifier">process</span><span class="special">(</span><span class="identifier">coro_t</span><span class="special">::</span><span class="identifier">pull_type</span><span class="special">&</span> <span class="identifier">events</span><span class="special">,</span><span class="keyword">const</span> <span class="identifier">OpenEvent</span><span class="special">&</span> <span class="identifier">context</span><span class="special">,</span> |
| <span class="keyword">const</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span><span class="special">&</span> <span class="identifier">indent</span><span class="special">=</span><span class="string">""</span><span class="special">){</span> |
| <span class="comment">// Capture OpenEvent's tag name: as soon as we advance the parser, the</span> |
| <span class="comment">// TagType& reference bound in this OpenEvent will be invalidated.</span> |
| <span class="identifier">xml</span><span class="special">::</span><span class="identifier">sax</span><span class="special">::</span><span class="identifier">Parser</span><span class="special">::</span><span class="identifier">TagType</span> <span class="identifier">tagName</span> <span class="special">=</span> <span class="identifier">context</span><span class="special">.</span><span class="identifier">mName</span><span class="special">;</span> |
| <span class="comment">// Since the OpenEvent is still the current value from 'events', pass</span> |
| <span class="comment">// control back to 'events' until the next event. Of course, each time we</span> |
| <span class="comment">// come back we must check for the end of the results stream.</span> |
| <span class="keyword">while</span><span class="special">(</span><span class="identifier">events</span><span class="special">()){</span> |
| <span class="comment">// Another event is pending; retrieve it.</span> |
| <span class="keyword">const</span> <span class="identifier">BaseEvent</span><span class="special">&</span> <span class="identifier">event</span><span class="special">=</span><span class="identifier">events</span><span class="special">.</span><span class="identifier">get</span><span class="special">();</span> |
| <span class="keyword">const</span> <span class="identifier">OpenEvent</span><span class="special">*</span> <span class="identifier">oe</span><span class="special">;</span> |
| <span class="keyword">const</span> <span class="identifier">CloseEvent</span><span class="special">*</span> <span class="identifier">ce</span><span class="special">;</span> |
| <span class="keyword">const</span> <span class="identifier">TextEvent</span><span class="special">*</span> <span class="identifier">te</span><span class="special">;</span> |
| <span class="keyword">if</span><span class="special">((</span><span class="identifier">oe</span><span class="special">=</span><span class="keyword">dynamic_cast</span><span class="special"><</span><span class="keyword">const</span> <span class="identifier">OpenEvent</span><span class="special">*>(&</span><span class="identifier">event</span><span class="special">))){</span> |
| <span class="comment">// When we see OpenEvent, recursively process it.</span> |
| <span class="identifier">process</span><span class="special">(</span><span class="identifier">events</span><span class="special">,*</span><span class="identifier">oe</span><span class="special">,</span><span class="identifier">indent</span><span class="special">+</span><span class="string">" "</span><span class="special">);</span> |
| <span class="special">}</span> |
| <span class="keyword">else</span> <span class="keyword">if</span><span class="special">((</span><span class="identifier">ce</span><span class="special">=</span><span class="keyword">dynamic_cast</span><span class="special"><</span><span class="keyword">const</span> <span class="identifier">CloseEvent</span><span class="special">*>(&</span><span class="identifier">event</span><span class="special">))){</span> |
| <span class="comment">// When we see CloseEvent, validate its tag name and then return</span> |
| <span class="comment">// it. (This assert is really a check on xml::sax::Parser, since</span> |
| <span class="comment">// it already validates matching open/close tags.)</span> |
| <span class="identifier">assert</span><span class="special">(</span><span class="identifier">ce</span><span class="special">-></span><span class="identifier">mName</span> <span class="special">==</span> <span class="identifier">tagName</span><span class="special">);</span> |
| <span class="keyword">return</span> <span class="special">*</span><span class="identifier">ce</span><span class="special">;</span> |
| <span class="special">}</span> |
| <span class="keyword">else</span> <span class="keyword">if</span><span class="special">((</span><span class="identifier">te</span><span class="special">=</span><span class="keyword">dynamic_cast</span><span class="special"><</span><span class="keyword">const</span> <span class="identifier">TextEvent</span><span class="special">*>(&</span><span class="identifier">event</span><span class="special">))){</span> |
| <span class="comment">// When we see TextEvent, just report its text, along with</span> |
| <span class="comment">// indentation indicating recursion level.</span> |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">cout</span><span class="special"><<</span><span class="identifier">indent</span><span class="special"><<</span><span class="string">"text: '"</span><span class="special"><<</span><span class="identifier">te</span><span class="special">-></span><span class="identifier">mText</span><span class="special">.</span><span class="identifier">getText</span><span class="special">()<<</span><span class="string">"'\n"</span><span class="special">;</span> |
| <span class="special">}</span> |
| <span class="special">}</span> |
| <span class="special">}</span> |
| |
| <span class="comment">// pretend we have an XML file of arbitrary size</span> |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">istringstream</span> <span class="identifier">in</span><span class="special">(</span><span class="identifier">doc</span><span class="special">);</span> |
| <span class="keyword">try</span> |
| <span class="special">{</span> |
| <span class="identifier">coro_t</span><span class="special">::</span><span class="identifier">pull_type</span> <span class="identifier">events</span><span class="special">(</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">bind</span><span class="special">(</span><span class="identifier">parser</span><span class="special">,</span><span class="identifier">_1</span><span class="special">,</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">ref</span><span class="special">(</span><span class="identifier">in</span><span class="special">)));</span> |
| <span class="comment">// We fully expect at least ONE event.</span> |
| <span class="identifier">assert</span><span class="special">(</span><span class="identifier">events</span><span class="special">);</span> |
| <span class="comment">// This dynamic_cast<&> is itself an assertion that the first event is an</span> |
| <span class="comment">// OpenEvent.</span> |
| <span class="keyword">const</span> <span class="identifier">OpenEvent</span><span class="special">&</span> <span class="identifier">context</span><span class="special">=</span><span class="keyword">dynamic_cast</span><span class="special"><</span><span class="keyword">const</span> <span class="identifier">OpenEvent</span><span class="special">&>(</span><span class="identifier">events</span><span class="special">.</span><span class="identifier">get</span><span class="special">());</span> |
| <span class="identifier">process</span><span class="special">(</span><span class="identifier">events</span><span class="special">,</span> <span class="identifier">context</span><span class="special">);</span> |
| <span class="special">}</span> |
| <span class="keyword">catch</span> <span class="special">(</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">exception</span><span class="special">&</span> <span class="identifier">e</span><span class="special">)</span> |
| <span class="special">{</span> |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">cout</span> <span class="special"><<</span> <span class="string">"Parsing error: "</span> <span class="special"><<</span> <span class="identifier">e</span><span class="special">.</span><span class="identifier">what</span><span class="special">()</span> <span class="special"><<</span> <span class="char">'\n'</span><span class="special">;</span> |
| <span class="special">}</span> |
| </pre> |
| <p> |
| This problem does not map at all well to communicating between independent |
| threads. It makes no sense for either side to proceed independently of the |
| other. You want them to pass control back and forth. |
| </p> |
| <p> |
| The solution involves a small polymorphic class event hierarchy, to which we're |
| passing references. The actual instances are temporaries on the coroutine's |
| stack; the coroutine passes each reference in turn to the main logic. Copying |
| them as base-class values would slice them. |
| </p> |
| <p> |
| If we were trying to let the SAX parser proceed independently of the consuming |
| logic, one could imagine allocating event-subclass instances on the heap, passing |
| them along on a thread-safe queue of pointers. But that doesn't work either, |
| because these event classes bind references passed by the SAX parser. The moment |
| the parser moves on, those references become invalid. |
| </p> |
| <p> |
| Instead of binding a <span class="emphasis"><em>TagType&</em></span> reference, we could |
| store a copy of the <span class="emphasis"><em>TagType</em></span> in <span class="emphasis"><em>CloseEvent</em></span>. |
| But that doesn't solve the whole problem. For attributes, we get an <span class="emphasis"><em>AttributeIterator&</em></span>; |
| for text we get a <span class="emphasis"><em>CharIterator&</em></span>. Storing a copy of |
| those iterators is pointless: once the parser moves on, those iterators are |
| invalidated. You must process the attribute iterator (or character iterator) |
| during the SAX callback for that event. |
| </p> |
| <p> |
| Naturally we could retrieve and store a copy of every attribute and its value; |
| we could store a copy of every chunk of text. That would effectively be all |
| the text in the document -- a heavy price to pay, if the reason we're using |
| SAX is concern about fitting the entire DOM into memory. |
| </p> |
| <p> |
| There's yet another advantage to using coroutines. This SAX parser throws an |
| exception when parsing fails. With a coroutine implementation, you need only |
| wrap the calling code in try/catch. |
| </p> |
| <p> |
| With communicating threads, you would have to arrange to catch the exception |
| and pass along the exception pointer on the same queue you're using to deliver |
| the other events. You would then have to rethrow the exception to unwind the |
| recursive document processing. |
| </p> |
| <p> |
| The coroutine solution maps very naturally to the problem space. |
| </p> |
| <h4> |
| <a name="coroutine.motivation.h3"></a> |
| <span class="phrase"><a name="coroutine.motivation._same_fringe__problem"></a></span><a class="link" href="motivation.html#coroutine.motivation._same_fringe__problem">'same |
| fringe' problem</a> |
| </h4> |
| <p> |
| The advantages of suspending at an arbitrary call depth can be seen particularly |
| clearly with the use of a recursive function, such as traversal of trees. If |
| traversing two different trees in the same deterministic order produces the |
| same list of leaf nodes, then both trees have the same fringe. |
| </p> |
| <p> |
| <span class="inlinemediaobject"><img src="../../../../../libs/coroutine/doc/images/same_fringe.png" align="middle" alt="same_fringe"></span> |
| </p> |
| <p> |
| Both trees in the picture have the same fringe even though the structure of |
| the trees is different. |
| </p> |
| <p> |
| The same fringe problem could be solved using coroutines by iterating over |
| the leaf nodes and comparing this sequence via <span class="emphasis"><em>std::equal()</em></span>. |
| The range of data values is generated by function <span class="emphasis"><em>traverse()</em></span> |
| which recursively traverses the tree and passes each node's data value to its |
| <span class="emphasis"><em>asymmetric_coroutine<>::push_type</em></span>. <span class="emphasis"><em>asymmetric_coroutine<>::push_type</em></span> |
| suspends the recursive computation and transfers the data value to the main |
| execution context. <span class="emphasis"><em>asymmetric_coroutine<>::pull_type::iterator</em></span>, |
| created from <span class="emphasis"><em>asymmetric_coroutine<>::pull_type</em></span>, |
| steps over those data values and delivers them to <span class="emphasis"><em>std::equal()</em></span> |
| for comparison. Each increment of <span class="emphasis"><em>asymmetric_coroutine<>::pull_type::iterator</em></span> |
| resumes <span class="emphasis"><em>traverse()</em></span>. Upon return from <span class="emphasis"><em>iterator::operator++()</em></span>, |
| either a new data value is available, or tree traversal is finished (iterator |
| is invalidated). |
| </p> |
| <p> |
| In effect, the coroutine iterator presents a flattened view of the recursive |
| data structure. |
| </p> |
| <pre class="programlisting"><span class="keyword">struct</span> <span class="identifier">node</span><span class="special">{</span> |
| <span class="keyword">typedef</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">shared_ptr</span><span class="special"><</span><span class="identifier">node</span><span class="special">></span> <span class="identifier">ptr_t</span><span class="special">;</span> |
| |
| <span class="comment">// Each tree node has an optional left subtree,</span> |
| <span class="comment">// an optional right subtree and a value of its own.</span> |
| <span class="comment">// The value is considered to be between the left</span> |
| <span class="comment">// subtree and the right.</span> |
| <span class="identifier">ptr_t</span> <span class="identifier">left</span><span class="special">,</span><span class="identifier">right</span><span class="special">;</span> |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="identifier">value</span><span class="special">;</span> |
| |
| <span class="comment">// construct leaf</span> |
| <span class="identifier">node</span><span class="special">(</span><span class="keyword">const</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span><span class="special">&</span> <span class="identifier">v</span><span class="special">):</span> |
| <span class="identifier">left</span><span class="special">(),</span><span class="identifier">right</span><span class="special">(),</span><span class="identifier">value</span><span class="special">(</span><span class="identifier">v</span><span class="special">)</span> |
| <span class="special">{}</span> |
| <span class="comment">// construct nonleaf</span> |
| <span class="identifier">node</span><span class="special">(</span><span class="identifier">ptr_t</span> <span class="identifier">l</span><span class="special">,</span><span class="keyword">const</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span><span class="special">&</span> <span class="identifier">v</span><span class="special">,</span><span class="identifier">ptr_t</span> <span class="identifier">r</span><span class="special">):</span> |
| <span class="identifier">left</span><span class="special">(</span><span class="identifier">l</span><span class="special">),</span><span class="identifier">right</span><span class="special">(</span><span class="identifier">r</span><span class="special">),</span><span class="identifier">value</span><span class="special">(</span><span class="identifier">v</span><span class="special">)</span> |
| <span class="special">{}</span> |
| |
| <span class="keyword">static</span> <span class="identifier">ptr_t</span> <span class="identifier">create</span><span class="special">(</span><span class="keyword">const</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span><span class="special">&</span> <span class="identifier">v</span><span class="special">){</span> |
| <span class="keyword">return</span> <span class="identifier">ptr_t</span><span class="special">(</span><span class="keyword">new</span> <span class="identifier">node</span><span class="special">(</span><span class="identifier">v</span><span class="special">));</span> |
| <span class="special">}</span> |
| |
| <span class="keyword">static</span> <span class="identifier">ptr_t</span> <span class="identifier">create</span><span class="special">(</span><span class="identifier">ptr_t</span> <span class="identifier">l</span><span class="special">,</span><span class="keyword">const</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span><span class="special">&</span> <span class="identifier">v</span><span class="special">,</span><span class="identifier">ptr_t</span> <span class="identifier">r</span><span class="special">){</span> |
| <span class="keyword">return</span> <span class="identifier">ptr_t</span><span class="special">(</span><span class="keyword">new</span> <span class="identifier">node</span><span class="special">(</span><span class="identifier">l</span><span class="special">,</span><span class="identifier">v</span><span class="special">,</span><span class="identifier">r</span><span class="special">));</span> |
| <span class="special">}</span> |
| <span class="special">};</span> |
| |
| <span class="identifier">node</span><span class="special">::</span><span class="identifier">ptr_t</span> <span class="identifier">create_left_tree_from</span><span class="special">(</span><span class="keyword">const</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span><span class="special">&</span> <span class="identifier">root</span><span class="special">){</span> |
| <span class="comment">/* -------- |
| root |
| / \ |
| b e |
| / \ |
| a c |
| -------- */</span> |
| <span class="keyword">return</span> <span class="identifier">node</span><span class="special">::</span><span class="identifier">create</span><span class="special">(</span> |
| <span class="identifier">node</span><span class="special">::</span><span class="identifier">create</span><span class="special">(</span> |
| <span class="identifier">node</span><span class="special">::</span><span class="identifier">create</span><span class="special">(</span><span class="string">"a"</span><span class="special">),</span> |
| <span class="string">"b"</span><span class="special">,</span> |
| <span class="identifier">node</span><span class="special">::</span><span class="identifier">create</span><span class="special">(</span><span class="string">"c"</span><span class="special">)),</span> |
| <span class="identifier">root</span><span class="special">,</span> |
| <span class="identifier">node</span><span class="special">::</span><span class="identifier">create</span><span class="special">(</span><span class="string">"e"</span><span class="special">));</span> |
| <span class="special">}</span> |
| |
| <span class="identifier">node</span><span class="special">::</span><span class="identifier">ptr_t</span> <span class="identifier">create_right_tree_from</span><span class="special">(</span><span class="keyword">const</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span><span class="special">&</span> <span class="identifier">root</span><span class="special">){</span> |
| <span class="comment">/* -------- |
| root |
| / \ |
| a d |
| / \ |
| c e |
| -------- */</span> |
| <span class="keyword">return</span> <span class="identifier">node</span><span class="special">::</span><span class="identifier">create</span><span class="special">(</span> |
| <span class="identifier">node</span><span class="special">::</span><span class="identifier">create</span><span class="special">(</span><span class="string">"a"</span><span class="special">),</span> |
| <span class="identifier">root</span><span class="special">,</span> |
| <span class="identifier">node</span><span class="special">::</span><span class="identifier">create</span><span class="special">(</span> |
| <span class="identifier">node</span><span class="special">::</span><span class="identifier">create</span><span class="special">(</span><span class="string">"c"</span><span class="special">),</span> |
| <span class="string">"d"</span><span class="special">,</span> |
| <span class="identifier">node</span><span class="special">::</span><span class="identifier">create</span><span class="special">(</span><span class="string">"e"</span><span class="special">)));</span> |
| <span class="special">}</span> |
| |
| <span class="comment">// recursively walk the tree, delivering values in order</span> |
| <span class="keyword">void</span> <span class="identifier">traverse</span><span class="special">(</span><span class="identifier">node</span><span class="special">::</span><span class="identifier">ptr_t</span> <span class="identifier">n</span><span class="special">,</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">coroutines</span><span class="special">::</span><span class="identifier">asymmetric_coroutine</span><span class="special"><</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span><span class="special">>::</span><span class="identifier">push_type</span><span class="special">&</span> <span class="identifier">out</span><span class="special">){</span> |
| <span class="keyword">if</span><span class="special">(</span><span class="identifier">n</span><span class="special">-></span><span class="identifier">left</span><span class="special">)</span> <span class="identifier">traverse</span><span class="special">(</span><span class="identifier">n</span><span class="special">-></span><span class="identifier">left</span><span class="special">,</span><span class="identifier">out</span><span class="special">);</span> |
| <span class="identifier">out</span><span class="special">(</span><span class="identifier">n</span><span class="special">-></span><span class="identifier">value</span><span class="special">);</span> |
| <span class="keyword">if</span><span class="special">(</span><span class="identifier">n</span><span class="special">-></span><span class="identifier">right</span><span class="special">)</span> <span class="identifier">traverse</span><span class="special">(</span><span class="identifier">n</span><span class="special">-></span><span class="identifier">right</span><span class="special">,</span><span class="identifier">out</span><span class="special">);</span> |
| <span class="special">}</span> |
| |
| <span class="comment">// evaluation</span> |
| <span class="special">{</span> |
| <span class="identifier">node</span><span class="special">::</span><span class="identifier">ptr_t</span> <span class="identifier">left_d</span><span class="special">(</span><span class="identifier">create_left_tree_from</span><span class="special">(</span><span class="string">"d"</span><span class="special">));</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">coroutines</span><span class="special">::</span><span class="identifier">asymmetric_coroutine</span><span class="special"><</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span><span class="special">>::</span><span class="identifier">pull_type</span> <span class="identifier">left_d_reader</span><span class="special">(</span> |
| <span class="special">[&](</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">coroutines</span><span class="special">::</span><span class="identifier">asymmetric_coroutine</span><span class="special"><</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span><span class="special">>::</span><span class="identifier">push_type</span> <span class="special">&</span> <span class="identifier">out</span><span class="special">){</span> |
| <span class="identifier">traverse</span><span class="special">(</span><span class="identifier">left_d</span><span class="special">,</span><span class="identifier">out</span><span class="special">);</span> |
| <span class="special">});</span> |
| |
| <span class="identifier">node</span><span class="special">::</span><span class="identifier">ptr_t</span> <span class="identifier">right_b</span><span class="special">(</span><span class="identifier">create_right_tree_from</span><span class="special">(</span><span class="string">"b"</span><span class="special">));</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">coroutines</span><span class="special">::</span><span class="identifier">asymmetric_coroutine</span><span class="special"><</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span><span class="special">>::</span><span class="identifier">pull_type</span> <span class="identifier">right_b_reader</span><span class="special">(</span> |
| <span class="special">[&](</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">coroutines</span><span class="special">::</span><span class="identifier">asymmetric_coroutine</span><span class="special"><</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span><span class="special">>::</span><span class="identifier">push_type</span> <span class="special">&</span> <span class="identifier">out</span><span class="special">){</span> |
| <span class="identifier">traverse</span><span class="special">(</span><span class="identifier">right_b</span><span class="special">,</span><span class="identifier">out</span><span class="special">);</span> |
| <span class="special">});</span> |
| |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">cout</span> <span class="special"><<</span> <span class="string">"left tree from d == right tree from b? "</span> |
| <span class="special"><<</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">boolalpha</span> |
| <span class="special"><<</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">equal</span><span class="special">(</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">begin</span><span class="special">(</span><span class="identifier">left_d_reader</span><span class="special">),</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">end</span><span class="special">(</span><span class="identifier">left_d_reader</span><span class="special">),</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">begin</span><span class="special">(</span><span class="identifier">right_b_reader</span><span class="special">))</span> |
| <span class="special"><<</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">endl</span><span class="special">;</span> |
| <span class="special">}</span> |
| <span class="special">{</span> |
| <span class="identifier">node</span><span class="special">::</span><span class="identifier">ptr_t</span> <span class="identifier">left_d</span><span class="special">(</span><span class="identifier">create_left_tree_from</span><span class="special">(</span><span class="string">"d"</span><span class="special">));</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">coroutines</span><span class="special">::</span><span class="identifier">asymmetric_coroutine</span><span class="special"><</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span><span class="special">>::</span><span class="identifier">pull_type</span> <span class="identifier">left_d_reader</span><span class="special">(</span> |
| <span class="special">[&](</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">coroutines</span><span class="special">::</span><span class="identifier">asymmetric_coroutine</span><span class="special"><</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span><span class="special">>::</span><span class="identifier">push_type</span> <span class="special">&</span> <span class="identifier">out</span><span class="special">){</span> |
| <span class="identifier">traverse</span><span class="special">(</span><span class="identifier">left_d</span><span class="special">,</span><span class="identifier">out</span><span class="special">);</span> |
| <span class="special">});</span> |
| |
| <span class="identifier">node</span><span class="special">::</span><span class="identifier">ptr_t</span> <span class="identifier">right_x</span><span class="special">(</span><span class="identifier">create_right_tree_from</span><span class="special">(</span><span class="string">"x"</span><span class="special">));</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">coroutines</span><span class="special">::</span><span class="identifier">asymmetric_coroutine</span><span class="special"><</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span><span class="special">>::</span><span class="identifier">pull_type</span> <span class="identifier">right_x_reader</span><span class="special">(</span> |
| <span class="special">[&](</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">coroutines</span><span class="special">::</span><span class="identifier">asymmetric_coroutine</span><span class="special"><</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span><span class="special">>::</span><span class="identifier">push_type</span> <span class="special">&</span> <span class="identifier">out</span><span class="special">){</span> |
| <span class="identifier">traverse</span><span class="special">(</span><span class="identifier">right_x</span><span class="special">,</span><span class="identifier">out</span><span class="special">);</span> |
| <span class="special">});</span> |
| |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">cout</span> <span class="special"><<</span> <span class="string">"left tree from d == right tree from x? "</span> |
| <span class="special"><<</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">boolalpha</span> |
| <span class="special"><<</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">equal</span><span class="special">(</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">begin</span><span class="special">(</span><span class="identifier">left_d_reader</span><span class="special">),</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">end</span><span class="special">(</span><span class="identifier">left_d_reader</span><span class="special">),</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">begin</span><span class="special">(</span><span class="identifier">right_x_reader</span><span class="special">))</span> |
| <span class="special"><<</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">endl</span><span class="special">;</span> |
| <span class="special">}</span> |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">cout</span> <span class="special"><<</span> <span class="string">"Done"</span> <span class="special"><<</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">endl</span><span class="special">;</span> |
| |
| <span class="identifier">output</span><span class="special">:</span> |
| <span class="identifier">left</span> <span class="identifier">tree</span> <span class="identifier">from</span> <span class="identifier">d</span> <span class="special">==</span> <span class="identifier">right</span> <span class="identifier">tree</span> <span class="identifier">from</span> <span class="identifier">b</span><span class="special">?</span> <span class="keyword">true</span> |
| <span class="identifier">left</span> <span class="identifier">tree</span> <span class="identifier">from</span> <span class="identifier">d</span> <span class="special">==</span> <span class="identifier">right</span> <span class="identifier">tree</span> <span class="identifier">from</span> <span class="identifier">x</span><span class="special">?</span> <span class="keyword">false</span> |
| <span class="identifier">Done</span> |
| </pre> |
| <h4> |
| <a name="coroutine.motivation.h4"></a> |
| <span class="phrase"><a name="coroutine.motivation.merging_two_sorted_arrays"></a></span><a class="link" href="motivation.html#coroutine.motivation.merging_two_sorted_arrays">merging |
| two sorted arrays</a> |
| </h4> |
| <p> |
| This example demonstrates how symmetric coroutines merge two sorted arrays. |
| </p> |
| <pre class="programlisting"><span class="identifier">std</span><span class="special">::</span><span class="identifier">vector</span><span class="special"><</span><span class="keyword">int</span><span class="special">></span> <span class="identifier">merge</span><span class="special">(</span><span class="keyword">const</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">vector</span><span class="special"><</span><span class="keyword">int</span><span class="special">>&</span> <span class="identifier">a</span><span class="special">,</span><span class="keyword">const</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">vector</span><span class="special"><</span><span class="keyword">int</span><span class="special">>&</span> <span class="identifier">b</span><span class="special">){</span> |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">vector</span><span class="special"><</span><span class="keyword">int</span><span class="special">></span> <span class="identifier">c</span><span class="special">;</span> |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">size_t</span> <span class="identifier">idx_a</span><span class="special">=</span><span class="number">0</span><span class="special">,</span><span class="identifier">idx_b</span><span class="special">=</span><span class="number">0</span><span class="special">;</span> |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">coroutines</span><span class="special">::</span><span class="identifier">symmetric_coroutine</span><span class="special"><</span><span class="keyword">void</span><span class="special">>::</span><span class="identifier">call_type</span> <span class="special">*</span><span class="identifier">other_a</span><span class="special">=</span><span class="number">0</span><span class="special">,*</span><span class="identifier">other_b</span><span class="special">=</span><span class="number">0</span><span class="special">;</span> |
| |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">coroutines</span><span class="special">::</span><span class="identifier">symmetric_coroutine</span><span class="special"><</span><span class="keyword">void</span><span class="special">>::</span><span class="identifier">call_type</span> <span class="identifier">coro_a</span><span class="special">(</span> |
| <span class="special">[&](</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">coroutines</span><span class="special">::</span><span class="identifier">symmetric_coroutine</span><span class="special"><</span><span class="keyword">void</span><span class="special">>::</span><span class="identifier">yield_type</span><span class="special">&</span> <span class="identifier">yield</span><span class="special">){</span> |
| <span class="keyword">while</span><span class="special">(</span><span class="identifier">idx_a</span><span class="special"><</span><span class="identifier">a</span><span class="special">.</span><span class="identifier">size</span><span class="special">()){</span> |
| <span class="keyword">if</span><span class="special">(</span><span class="identifier">b</span><span class="special">[</span><span class="identifier">idx_b</span><span class="special">]<</span><span class="identifier">a</span><span class="special">[</span><span class="identifier">idx_a</span><span class="special">])</span> <span class="comment">// test if element in array b is less than in array a</span> |
| <span class="identifier">yield</span><span class="special">(*</span><span class="identifier">other_b</span><span class="special">);</span> <span class="comment">// yield to coroutine coro_b</span> |
| <span class="identifier">c</span><span class="special">.</span><span class="identifier">push_back</span><span class="special">(</span><span class="identifier">a</span><span class="special">[</span><span class="identifier">idx_a</span><span class="special">++]);</span> <span class="comment">// add element to final array</span> |
| <span class="special">}</span> |
| <span class="comment">// add remaining elements of array b</span> |
| <span class="keyword">while</span><span class="special">(</span><span class="identifier">idx_b</span><span class="special"><</span><span class="identifier">b</span><span class="special">.</span><span class="identifier">size</span><span class="special">())</span> |
| <span class="identifier">c</span><span class="special">.</span><span class="identifier">push_back</span><span class="special">(</span><span class="identifier">b</span><span class="special">[</span><span class="identifier">idx_b</span><span class="special">++]);</span> |
| <span class="special">});</span> |
| |
| <span class="identifier">boost</span><span class="special">::</span><span class="identifier">coroutines</span><span class="special">::</span><span class="identifier">symmetric_coroutine</span><span class="special"><</span><span class="keyword">void</span><span class="special">>::</span><span class="identifier">call_type</span> <span class="identifier">coro_b</span><span class="special">(</span> |
| <span class="special">[&](</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">coroutines</span><span class="special">::</span><span class="identifier">symmetric_coroutine</span><span class="special"><</span><span class="keyword">void</span><span class="special">>::</span><span class="identifier">yield_type</span><span class="special">&</span> <span class="identifier">yield</span><span class="special">){</span> |
| <span class="keyword">while</span><span class="special">(</span><span class="identifier">idx_b</span><span class="special"><</span><span class="identifier">b</span><span class="special">.</span><span class="identifier">size</span><span class="special">()){</span> |
| <span class="keyword">if</span><span class="special">(</span><span class="identifier">a</span><span class="special">[</span><span class="identifier">idx_a</span><span class="special">]<</span><span class="identifier">b</span><span class="special">[</span><span class="identifier">idx_b</span><span class="special">])</span> <span class="comment">// test if element in array a is less than in array b</span> |
| <span class="identifier">yield</span><span class="special">(*</span><span class="identifier">other_a</span><span class="special">);</span> <span class="comment">// yield to coroutine coro_a</span> |
| <span class="identifier">c</span><span class="special">.</span><span class="identifier">push_back</span><span class="special">(</span><span class="identifier">b</span><span class="special">[</span><span class="identifier">idx_b</span><span class="special">++]);</span> <span class="comment">// add element to final array</span> |
| <span class="special">}</span> |
| <span class="comment">// add remaining elements of array a</span> |
| <span class="keyword">while</span><span class="special">(</span><span class="identifier">idx_a</span><span class="special"><</span><span class="identifier">a</span><span class="special">.</span><span class="identifier">size</span><span class="special">())</span> |
| <span class="identifier">c</span><span class="special">.</span><span class="identifier">push_back</span><span class="special">(</span><span class="identifier">a</span><span class="special">[</span><span class="identifier">idx_a</span><span class="special">++]);</span> |
| <span class="special">});</span> |
| |
| |
| <span class="identifier">other_a</span><span class="special">=&</span><span class="identifier">coro_a</span><span class="special">;</span> |
| <span class="identifier">other_b</span><span class="special">=&</span><span class="identifier">coro_b</span><span class="special">;</span> |
| |
| <span class="identifier">coro_a</span><span class="special">();</span> <span class="comment">// enter coroutine-fn of coro_a</span> |
| |
| <span class="keyword">return</span> <span class="identifier">c</span><span class="special">;</span> |
| <span class="special">}</span> |
| </pre> |
| <h4> |
| <a name="coroutine.motivation.h5"></a> |
| <span class="phrase"><a name="coroutine.motivation.chaining_coroutines"></a></span><a class="link" href="motivation.html#coroutine.motivation.chaining_coroutines">chaining |
| coroutines</a> |
| </h4> |
| <p> |
| This code shows how coroutines could be chained. |
| </p> |
| <pre class="programlisting"><span class="keyword">typedef</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">coroutines</span><span class="special">::</span><span class="identifier">asymmetric_coroutine</span><span class="special"><</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span><span class="special">></span> <span class="identifier">coro_t</span><span class="special">;</span> |
| |
| <span class="comment">// deliver each line of input stream to sink as a separate string</span> |
| <span class="keyword">void</span> <span class="identifier">readlines</span><span class="special">(</span><span class="identifier">coro_t</span><span class="special">::</span><span class="identifier">push_type</span><span class="special">&</span> <span class="identifier">sink</span><span class="special">,</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">istream</span><span class="special">&</span> <span class="identifier">in</span><span class="special">){</span> |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="identifier">line</span><span class="special">;</span> |
| <span class="keyword">while</span><span class="special">(</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">getline</span><span class="special">(</span><span class="identifier">in</span><span class="special">,</span><span class="identifier">line</span><span class="special">))</span> |
| <span class="identifier">sink</span><span class="special">(</span><span class="identifier">line</span><span class="special">);</span> |
| <span class="special">}</span> |
| |
| <span class="keyword">void</span> <span class="identifier">tokenize</span><span class="special">(</span><span class="identifier">coro_t</span><span class="special">::</span><span class="identifier">push_type</span><span class="special">&</span> <span class="identifier">sink</span><span class="special">,</span> <span class="identifier">coro_t</span><span class="special">::</span><span class="identifier">pull_type</span><span class="special">&</span> <span class="identifier">source</span><span class="special">){</span> |
| <span class="comment">// This tokenizer doesn't happen to be stateful: you could reasonably</span> |
| <span class="comment">// implement it with a single call to push each new token downstream. But</span> |
| <span class="comment">// I've worked with stateful tokenizers, in which the meaning of input</span> |
| <span class="comment">// characters depends in part on their position within the input line.</span> |
| <span class="identifier">BOOST_FOREACH</span><span class="special">(</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="identifier">line</span><span class="special">,</span><span class="identifier">source</span><span class="special">){</span> |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span><span class="special">::</span><span class="identifier">size_type</span> <span class="identifier">pos</span><span class="special">=</span><span class="number">0</span><span class="special">;</span> |
| <span class="keyword">while</span><span class="special">(</span><span class="identifier">pos</span><span class="special"><</span><span class="identifier">line</span><span class="special">.</span><span class="identifier">length</span><span class="special">()){</span> |
| <span class="keyword">if</span><span class="special">(</span><span class="identifier">line</span><span class="special">[</span><span class="identifier">pos</span><span class="special">]==</span><span class="char">'"'</span><span class="special">){</span> |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="identifier">token</span><span class="special">;</span> |
| <span class="special">++</span><span class="identifier">pos</span><span class="special">;</span> <span class="comment">// skip open quote</span> |
| <span class="keyword">while</span><span class="special">(</span><span class="identifier">pos</span><span class="special"><</span><span class="identifier">line</span><span class="special">.</span><span class="identifier">length</span><span class="special">()&&</span><span class="identifier">line</span><span class="special">[</span><span class="identifier">pos</span><span class="special">]!=</span><span class="char">'"'</span><span class="special">)</span> |
| <span class="identifier">token</span><span class="special">+=</span><span class="identifier">line</span><span class="special">[</span><span class="identifier">pos</span><span class="special">++];</span> |
| <span class="special">++</span><span class="identifier">pos</span><span class="special">;</span> <span class="comment">// skip close quote</span> |
| <span class="identifier">sink</span><span class="special">(</span><span class="identifier">token</span><span class="special">);</span> <span class="comment">// pass token downstream</span> |
| <span class="special">}</span> <span class="keyword">else</span> <span class="keyword">if</span> <span class="special">(</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">isspace</span><span class="special">(</span><span class="identifier">line</span><span class="special">[</span><span class="identifier">pos</span><span class="special">])){</span> |
| <span class="special">++</span><span class="identifier">pos</span><span class="special">;</span> <span class="comment">// outside quotes, ignore whitespace</span> |
| <span class="special">}</span> <span class="keyword">else</span> <span class="keyword">if</span> <span class="special">(</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">isalpha</span><span class="special">(</span><span class="identifier">line</span><span class="special">[</span><span class="identifier">pos</span><span class="special">])){</span> |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="identifier">token</span><span class="special">;</span> |
| <span class="keyword">while</span> <span class="special">(</span><span class="identifier">pos</span> <span class="special"><</span> <span class="identifier">line</span><span class="special">.</span><span class="identifier">length</span><span class="special">()</span> <span class="special">&&</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">isalpha</span><span class="special">(</span><span class="identifier">line</span><span class="special">[</span><span class="identifier">pos</span><span class="special">]))</span> |
| <span class="identifier">token</span> <span class="special">+=</span> <span class="identifier">line</span><span class="special">[</span><span class="identifier">pos</span><span class="special">++];</span> |
| <span class="identifier">sink</span><span class="special">(</span><span class="identifier">token</span><span class="special">);</span> <span class="comment">// pass token downstream</span> |
| <span class="special">}</span> <span class="keyword">else</span> <span class="special">{</span> <span class="comment">// punctuation</span> |
| <span class="identifier">sink</span><span class="special">(</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span><span class="special">(</span><span class="number">1</span><span class="special">,</span><span class="identifier">line</span><span class="special">[</span><span class="identifier">pos</span><span class="special">++]));</span> |
| <span class="special">}</span> |
| <span class="special">}</span> |
| <span class="special">}</span> |
| <span class="special">}</span> |
| |
| <span class="keyword">void</span> <span class="identifier">only_words</span><span class="special">(</span><span class="identifier">coro_t</span><span class="special">::</span><span class="identifier">push_type</span><span class="special">&</span> <span class="identifier">sink</span><span class="special">,</span><span class="identifier">coro_t</span><span class="special">::</span><span class="identifier">pull_type</span><span class="special">&</span> <span class="identifier">source</span><span class="special">){</span> |
| <span class="identifier">BOOST_FOREACH</span><span class="special">(</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="identifier">token</span><span class="special">,</span><span class="identifier">source</span><span class="special">){</span> |
| <span class="keyword">if</span> <span class="special">(!</span><span class="identifier">token</span><span class="special">.</span><span class="identifier">empty</span><span class="special">()</span> <span class="special">&&</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">isalpha</span><span class="special">(</span><span class="identifier">token</span><span class="special">[</span><span class="number">0</span><span class="special">]))</span> |
| <span class="identifier">sink</span><span class="special">(</span><span class="identifier">token</span><span class="special">);</span> |
| <span class="special">}</span> |
| <span class="special">}</span> |
| |
| <span class="keyword">void</span> <span class="identifier">trace</span><span class="special">(</span><span class="identifier">coro_t</span><span class="special">::</span><span class="identifier">push_type</span><span class="special">&</span> <span class="identifier">sink</span><span class="special">,</span> <span class="identifier">coro_t</span><span class="special">::</span><span class="identifier">pull_type</span><span class="special">&</span> <span class="identifier">source</span><span class="special">){</span> |
| <span class="identifier">BOOST_FOREACH</span><span class="special">(</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="identifier">token</span><span class="special">,</span><span class="identifier">source</span><span class="special">){</span> |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">cout</span> <span class="special"><<</span> <span class="string">"trace: '"</span> <span class="special"><<</span> <span class="identifier">token</span> <span class="special"><<</span> <span class="string">"'\n"</span><span class="special">;</span> |
| <span class="identifier">sink</span><span class="special">(</span><span class="identifier">token</span><span class="special">);</span> |
| <span class="special">}</span> |
| <span class="special">}</span> |
| |
| <span class="keyword">struct</span> <span class="identifier">FinalEOL</span><span class="special">{</span> |
| <span class="special">~</span><span class="identifier">FinalEOL</span><span class="special">(){</span> |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">cout</span> <span class="special"><<</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">endl</span><span class="special">;</span> |
| <span class="special">}</span> |
| <span class="special">};</span> |
| |
| <span class="keyword">void</span> <span class="identifier">layout</span><span class="special">(</span><span class="identifier">coro_t</span><span class="special">::</span><span class="identifier">pull_type</span><span class="special">&</span> <span class="identifier">source</span><span class="special">,</span><span class="keyword">int</span> <span class="identifier">num</span><span class="special">,</span><span class="keyword">int</span> <span class="identifier">width</span><span class="special">){</span> |
| <span class="comment">// Finish the last line when we leave by whatever means</span> |
| <span class="identifier">FinalEOL</span> <span class="identifier">eol</span><span class="special">;</span> |
| |
| <span class="comment">// Pull values from upstream, lay them out 'num' to a line</span> |
| <span class="keyword">for</span> <span class="special">(;;){</span> |
| <span class="keyword">for</span> <span class="special">(</span><span class="keyword">int</span> <span class="identifier">i</span> <span class="special">=</span> <span class="number">0</span><span class="special">;</span> <span class="identifier">i</span> <span class="special"><</span> <span class="identifier">num</span><span class="special">;</span> <span class="special">++</span><span class="identifier">i</span><span class="special">){</span> |
| <span class="comment">// when we exhaust the input, stop</span> |
| <span class="keyword">if</span> <span class="special">(!</span><span class="identifier">source</span><span class="special">)</span> <span class="keyword">return</span><span class="special">;</span> |
| |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">cout</span> <span class="special"><<</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">setw</span><span class="special">(</span><span class="identifier">width</span><span class="special">)</span> <span class="special"><<</span> <span class="identifier">source</span><span class="special">.</span><span class="identifier">get</span><span class="special">();</span> |
| <span class="comment">// now that we've handled this item, advance to next</span> |
| <span class="identifier">source</span><span class="special">();</span> |
| <span class="special">}</span> |
| <span class="comment">// after 'num' items, line break</span> |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">cout</span> <span class="special"><<</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">endl</span><span class="special">;</span> |
| <span class="special">}</span> |
| <span class="special">}</span> |
| |
| <span class="comment">// For example purposes, instead of having a separate text file in the</span> |
| <span class="comment">// local filesystem, construct an istringstream to read.</span> |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="identifier">data</span><span class="special">(</span> |
| <span class="string">"This is the first line.\n"</span> |
| <span class="string">"This, the second.\n"</span> |
| <span class="string">"The third has \"a phrase\"!\n"</span> |
| <span class="special">);</span> |
| |
| <span class="special">{</span> |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">cout</span> <span class="special"><<</span> <span class="string">"\nfilter:\n"</span><span class="special">;</span> |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">istringstream</span> <span class="identifier">infile</span><span class="special">(</span><span class="identifier">data</span><span class="special">);</span> |
| <span class="identifier">coro_t</span><span class="special">::</span><span class="identifier">pull_type</span> <span class="identifier">reader</span><span class="special">(</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">bind</span><span class="special">(</span><span class="identifier">readlines</span><span class="special">,</span> <span class="identifier">_1</span><span class="special">,</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">ref</span><span class="special">(</span><span class="identifier">infile</span><span class="special">)));</span> |
| <span class="identifier">coro_t</span><span class="special">::</span><span class="identifier">pull_type</span> <span class="identifier">tokenizer</span><span class="special">(</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">bind</span><span class="special">(</span><span class="identifier">tokenize</span><span class="special">,</span> <span class="identifier">_1</span><span class="special">,</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">ref</span><span class="special">(</span><span class="identifier">reader</span><span class="special">)));</span> |
| <span class="identifier">coro_t</span><span class="special">::</span><span class="identifier">pull_type</span> <span class="identifier">filter</span><span class="special">(</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">bind</span><span class="special">(</span><span class="identifier">only_words</span><span class="special">,</span> <span class="identifier">_1</span><span class="special">,</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">ref</span><span class="special">(</span><span class="identifier">tokenizer</span><span class="special">)));</span> |
| <span class="identifier">coro_t</span><span class="special">::</span><span class="identifier">pull_type</span> <span class="identifier">tracer</span><span class="special">(</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">bind</span><span class="special">(</span><span class="identifier">trace</span><span class="special">,</span> <span class="identifier">_1</span><span class="special">,</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">ref</span><span class="special">(</span><span class="identifier">filter</span><span class="special">)));</span> |
| <span class="identifier">BOOST_FOREACH</span><span class="special">(</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="identifier">token</span><span class="special">,</span><span class="identifier">tracer</span><span class="special">){</span> |
| <span class="comment">// just iterate, we're already pulling through tracer</span> |
| <span class="special">}</span> |
| <span class="special">}</span> |
| |
| <span class="special">{</span> |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">cout</span> <span class="special"><<</span> <span class="string">"\nlayout() as coroutine::push_type:\n"</span><span class="special">;</span> |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">istringstream</span> <span class="identifier">infile</span><span class="special">(</span><span class="identifier">data</span><span class="special">);</span> |
| <span class="identifier">coro_t</span><span class="special">::</span><span class="identifier">pull_type</span> <span class="identifier">reader</span><span class="special">(</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">bind</span><span class="special">(</span><span class="identifier">readlines</span><span class="special">,</span> <span class="identifier">_1</span><span class="special">,</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">ref</span><span class="special">(</span><span class="identifier">infile</span><span class="special">)));</span> |
| <span class="identifier">coro_t</span><span class="special">::</span><span class="identifier">pull_type</span> <span class="identifier">tokenizer</span><span class="special">(</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">bind</span><span class="special">(</span><span class="identifier">tokenize</span><span class="special">,</span> <span class="identifier">_1</span><span class="special">,</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">ref</span><span class="special">(</span><span class="identifier">reader</span><span class="special">)));</span> |
| <span class="identifier">coro_t</span><span class="special">::</span><span class="identifier">pull_type</span> <span class="identifier">filter</span><span class="special">(</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">bind</span><span class="special">(</span><span class="identifier">only_words</span><span class="special">,</span> <span class="identifier">_1</span><span class="special">,</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">ref</span><span class="special">(</span><span class="identifier">tokenizer</span><span class="special">)));</span> |
| <span class="identifier">coro_t</span><span class="special">::</span><span class="identifier">push_type</span> <span class="identifier">writer</span><span class="special">(</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">bind</span><span class="special">(</span><span class="identifier">layout</span><span class="special">,</span> <span class="identifier">_1</span><span class="special">,</span> <span class="number">5</span><span class="special">,</span> <span class="number">15</span><span class="special">));</span> |
| <span class="identifier">BOOST_FOREACH</span><span class="special">(</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="identifier">token</span><span class="special">,</span><span class="identifier">filter</span><span class="special">){</span> |
| <span class="identifier">writer</span><span class="special">(</span><span class="identifier">token</span><span class="special">);</span> |
| <span class="special">}</span> |
| <span class="special">}</span> |
| |
| <span class="special">{</span> |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">cout</span> <span class="special"><<</span> <span class="string">"\nfiltering output:\n"</span><span class="special">;</span> |
| <span class="identifier">std</span><span class="special">::</span><span class="identifier">istringstream</span> <span class="identifier">infile</span><span class="special">(</span><span class="identifier">data</span><span class="special">);</span> |
| <span class="identifier">coro_t</span><span class="special">::</span><span class="identifier">pull_type</span> <span class="identifier">reader</span><span class="special">(</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">bind</span><span class="special">(</span><span class="identifier">readlines</span><span class="special">,</span><span class="identifier">_1</span><span class="special">,</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">ref</span><span class="special">(</span><span class="identifier">infile</span><span class="special">)));</span> |
| <span class="identifier">coro_t</span><span class="special">::</span><span class="identifier">pull_type</span> <span class="identifier">tokenizer</span><span class="special">(</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">bind</span><span class="special">(</span><span class="identifier">tokenize</span><span class="special">,</span><span class="identifier">_1</span><span class="special">,</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">ref</span><span class="special">(</span><span class="identifier">reader</span><span class="special">)));</span> |
| <span class="identifier">coro_t</span><span class="special">::</span><span class="identifier">push_type</span> <span class="identifier">writer</span><span class="special">(</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">bind</span><span class="special">(</span><span class="identifier">layout</span><span class="special">,</span><span class="identifier">_1</span><span class="special">,</span><span class="number">5</span><span class="special">,</span><span class="number">15</span><span class="special">));</span> |
| <span class="comment">// Because of the symmetry of the API, we can use any of these</span> |
| <span class="comment">// chaining functions in a push_type coroutine chain as well.</span> |
| <span class="identifier">coro_t</span><span class="special">::</span><span class="identifier">push_type</span> <span class="identifier">filter</span><span class="special">(</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">bind</span><span class="special">(</span><span class="identifier">only_words</span><span class="special">,</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">ref</span><span class="special">(</span><span class="identifier">writer</span><span class="special">),</span><span class="identifier">_1</span><span class="special">));</span> |
| <span class="identifier">BOOST_FOREACH</span><span class="special">(</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="identifier">token</span><span class="special">,</span><span class="identifier">tokenizer</span><span class="special">){</span> |
| <span class="identifier">filter</span><span class="special">(</span><span class="identifier">token</span><span class="special">);</span> |
| <span class="special">}</span> |
| <span class="special">}</span> |
| </pre> |
| <div class="footnotes"> |
| <br><hr style="width:100; text-align:left;margin-left: 0"> |
| <div id="ftn.coroutine.motivation.f0" class="footnote"><p><a href="#coroutine.motivation.f0" class="para"><sup class="para">[4] </sup></a> |
| Christopher Kohlhoff, <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3964.pdf" target="_top">N3964 |
| - Library Foundations for Asynchronous Operations, Revision 1</a> |
| </p></div> |
| </div> |
| </div> |
| <table xmlns:rev="http://www.cs.rpi.edu/~gregod/boost/tools/doc/revision" width="100%"><tr> |
| <td align="left"></td> |
| <td align="right"><div class="copyright-footer">Copyright © 2009 Oliver Kowalke<p> |
| Distributed under the Boost Software License, Version 1.0. (See accompanying |
| file LICENSE_1_0.txt or copy at <a href="http://www.boost.org/LICENSE_1_0.txt" target="_top">http://www.boost.org/LICENSE_1_0.txt</a>) |
| </p> |
| </div></td> |
| </tr></table> |
| <hr> |
| <div class="spirit-nav"> |
| <a accesskey="p" href="intro.html"><img src="../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../index.html"><img src="../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../index.html"><img src="../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="coroutine.html"><img src="../../../../../doc/src/images/next.png" alt="Next"></a> |
| </div> |
| </body> |
| </html> |