123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
- <html><meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <title>Extending BOOST_OUTCOME_TRY - Boost.Outcome documentation</title>
- <link rel="stylesheet" href="../css/boost.css" type="text/css">
- <meta name="generator" content="Hugo 0.52 with Boostdoc theme">
- <meta name="viewport" content="width=device-width,initial-scale=1.0"/>
- <link rel="icon" href="../images/favicon.ico" type="image/ico"/>
- <body><div class="spirit-nav">
- <a accesskey="p" href="../recipes/asio-integration-1-70.html"><img src="../images/prev.png" alt="Prev"></a>
- <a accesskey="u" href="../recipes.html"><img src="../images/up.png" alt="Up"></a>
- <a accesskey="h" href="../index.html"><img src="../images/home.png" alt="Home"></a><a accesskey="n" href="../experimental.html"><img src="../images/next.png" alt="Next"></a></div><div id="content">
- <div class="titlepage"><div><div><h1 style="clear: both">Extending <code>BOOST_OUTCOME_TRY</code></h1></div></div></div>
-
- <p>Outcome’s <a href="../reference/macros/try.html" class="api-reference"><code>BOOST_OUTCOME_TRY(var, expr)</code></a>
- operation is fully extensible
- to accept as input any foreign types.
- It already recognises types matching the
- <a href="../reference/concepts/valueorerror.html" class="api-reference"><code>ValueOrError<T, E></code></a>
- concept, which is to say all types which have:</p>
- <ul>
- <li>A public <code>.has_value()</code> member function which returns a <code>bool</code>.</li>
- <li>In order of preference, a public <code>.assume_value()</code>/<code>.value()</code> member
- function.</li>
- <li>In order of preference, a public <code>.as_failure()</code>/<code>.assume_error()</code>/<code>.error()</code>
- member function.</li>
- </ul>
- <p>This should automatically handle inputs of <code>std::expected<T, E></code>, and many others,
- including intermixing Boost.Outcome and standalone Outcome within the same
- translation unit.</p>
- <p><code>BOOST_OUTCOME_TRY</code> has the following free function customisation points:</p>
- <dl>
- <dt><code>BOOST_OUTCOME_V2_NAMESPACE::</code><a href="../reference/functions/try_operation_has_value.html" class="api-reference"><code>try_operation_has_value(X)</code></a>
- <dd>Returns a `bool` which is true if the input to TRY has a value.
- <dt><code>BOOST_OUTCOME_V2_NAMESPACE::</code><a href="../reference/functions/try_operation_return_as.html" class="api-reference"><code>try_operation_return_as(X)</code></a>
- <dd>Returns a suitable <a href="../reference/types/failure_type.html" class="api-reference"><code>failure_type<EC, EP = void></code></a>
- which
- is returned immediately to cause stack unwind. Ought to preserve rvalue
- semantics (i.e. if passed an rvalue, move the error state into the failure
- type).
- <dt><code>BOOST_OUTCOME_V2_NAMESPACE::</code><a href="../reference/functions/try_operation_extract_value.html" class="api-reference"><code>try_operation_extract_value(X)</code></a>
- <dd>Extracts a value type from the input for the `TRY` to set its variable.
- Ought to preserve rvalue semantics (i.e. if passed an rvalue, move the value).
- </dl>
- <p>New overloads of these to support additional input types must be injected into
- the <code>BOOST_OUTCOME_V2_NAMESPACE</code> namespace before the compiler parses the relevant
- <code>BOOST_OUTCOME_TRY</code> in order to be found. This is called ‘early binding’ in the two
- phase name lookup model in C++. This was chosen over ‘late binding’, where an
- <code>BOOST_OUTCOME_TRY</code> in a templated piece of code could look up overloads introduced after
- parsing the template containing the <code>BOOST_OUTCOME_TRY</code>, because it has much lower
- impact on build times, as binding is done once at the point of parse, instead
- of on every occasion at the point of instantiation. If you are careful to ensure
- that you inject the overloads which you need early in the parse of the
- translation unit, all will be well.</p>
- <p>Let us work through an applied example.</p>
- <hr />
- <h2 id="a-very-foreign-pseudo-expected-type">A very foreign pseudo-Expected type</h2>
- <p>This is a paraphrase of a poorly written pseudo-Expected type which I once
- encountered in the production codebase of a large multinational. Lots
- of the code was already using it, and it was weird enough that it couldn’t
- be swapped out for something better easily.</p>
- <div class="code-snippet"><div class="highlight"><pre class="chroma"><code class="language-c++" data-lang="c++"><span class="k">enum</span> <span class="n">Errc</span>
- <span class="p">{</span>
- <span class="n">kBadValue</span>
- <span class="p">};</span>
- <span class="k">template</span> <span class="o"><</span><span class="k">class</span><span class="err"> </span><span class="nc">T</span><span class="p">,</span> <span class="k">class</span><span class="err"> </span><span class="nc">E</span> <span class="o">=</span> <span class="n">Errc</span><span class="o">></span> <span class="k">struct</span> <span class="n">ForeignExpected</span>
- <span class="p">{</span>
- <span class="n">T</span> <span class="n">Value</span><span class="p">;</span>
- <span class="n">E</span> <span class="n">Error</span><span class="p">;</span>
- <span class="kt">int</span> <span class="n">IsErrored</span><span class="p">;</span>
- <span class="n">ForeignExpected</span><span class="p">(</span><span class="n">T</span> <span class="n">v</span><span class="p">)</span>
- <span class="o">:</span> <span class="n">Value</span><span class="p">(</span><span class="n">v</span><span class="p">)</span>
- <span class="p">,</span> <span class="n">Error</span><span class="p">()</span>
- <span class="p">,</span> <span class="n">IsErrored</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
- <span class="p">{</span>
- <span class="p">}</span>
- <span class="n">ForeignExpected</span><span class="p">(</span><span class="n">E</span> <span class="n">e</span><span class="p">)</span>
- <span class="o">:</span> <span class="n">Value</span><span class="p">()</span>
- <span class="p">,</span> <span class="n">Error</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
- <span class="p">,</span> <span class="n">IsErrored</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
- <span class="p">{</span>
- <span class="p">}</span>
- <span class="p">};</span>
- </code></pre></div><a href="https://github.com/boostorg/outcome/tree/master/doc/src/snippets/foreign_try.cpp#L37" class="code-snippet-url" target="_blank">View this code on Github</a></div>
- <p>What we would like is for new code to be written using Outcome, but be able
- to transparently call old code, like this:</p>
- <div class="code-snippet"><div class="highlight"><pre class="chroma"><code class="language-c++" data-lang="c++"><span class="n">ForeignExpected</span><span class="o"><</span><span class="kt">int</span><span class="o">></span> <span class="n">old_code</span><span class="p">(</span><span class="kt">int</span> <span class="n">a</span><span class="p">)</span> <span class="c1">// old code
- </span><span class="c1"></span><span class="p">{</span>
- <span class="k">if</span><span class="p">(</span><span class="mi">0</span> <span class="o">==</span> <span class="n">a</span><span class="p">)</span>
- <span class="k">return</span> <span class="n">kBadValue</span><span class="p">;</span>
- <span class="k">return</span> <span class="n">a</span><span class="p">;</span>
- <span class="p">}</span>
- <span class="n">outcome</span><span class="o">::</span><span class="n">result</span><span class="o"><</span><span class="kt">int</span><span class="o">></span> <span class="n">new_code</span><span class="p">(</span><span class="kt">int</span> <span class="n">a</span><span class="p">)</span> <span class="c1">// new code
- </span><span class="c1"></span><span class="p">{</span>
- <span class="n">BOOST_OUTCOME_TRY</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">old_code</span><span class="p">(</span><span class="n">a</span><span class="p">));</span>
- <span class="k">return</span> <span class="n">x</span><span class="p">;</span>
- <span class="p">}</span>
- </code></pre></div><a href="https://github.com/boostorg/outcome/tree/master/doc/src/snippets/foreign_try.cpp#L88" class="code-snippet-url" target="_blank">View this code on Github</a></div>
- <p>Telling Outcome about this weird foreign Expected is straightforward:</p>
- <div class="code-snippet"><div class="highlight"><pre class="chroma"><code class="language-c++" data-lang="c++"><span class="n">BOOST_OUTCOME_V2_NAMESPACE_BEGIN</span>
- <span class="k">template</span> <span class="o"><</span><span class="k">class</span><span class="err"> </span><span class="nc">T</span><span class="p">,</span> <span class="k">class</span><span class="err"> </span><span class="nc">E</span><span class="o">></span> <span class="c1">//
- </span><span class="c1"></span><span class="kr">inline</span> <span class="kt">bool</span> <span class="n">try_operation_has_value</span><span class="p">(</span><span class="k">const</span> <span class="n">ForeignExpected</span><span class="o"><</span><span class="n">T</span><span class="p">,</span> <span class="n">E</span><span class="o">></span> <span class="o">&</span><span class="n">v</span><span class="p">)</span>
- <span class="p">{</span>
- <span class="k">return</span> <span class="mi">0</span> <span class="o">==</span> <span class="n">v</span><span class="p">.</span><span class="n">IsErrored</span><span class="p">;</span>
- <span class="p">}</span>
- <span class="k">template</span> <span class="o"><</span><span class="k">class</span><span class="err"> </span><span class="nc">T</span><span class="p">,</span> <span class="k">class</span><span class="err"> </span><span class="nc">E</span><span class="o">></span> <span class="c1">//
- </span><span class="c1"></span><span class="kr">inline</span> <span class="k">auto</span> <span class="n">try_operation_return_as</span><span class="p">(</span><span class="k">const</span> <span class="n">ForeignExpected</span><span class="o"><</span><span class="n">T</span><span class="p">,</span> <span class="n">E</span><span class="o">></span> <span class="o">&</span><span class="n">v</span><span class="p">)</span>
- <span class="p">{</span>
- <span class="k">switch</span><span class="p">(</span><span class="n">v</span><span class="p">.</span><span class="n">Error</span><span class="p">)</span>
- <span class="p">{</span>
- <span class="k">case</span> <span class="nl">kBadValue</span><span class="p">:</span>
- <span class="k">return</span> <span class="n">failure</span><span class="p">(</span><span class="n">make_error_code</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">errc</span><span class="o">::</span><span class="n">argument_out_of_domain</span><span class="p">));</span>
- <span class="p">}</span>
- <span class="n">abort</span><span class="p">();</span>
- <span class="p">}</span>
- <span class="k">template</span> <span class="o"><</span><span class="k">class</span><span class="err"> </span><span class="nc">T</span><span class="p">,</span> <span class="k">class</span><span class="err"> </span><span class="nc">E</span><span class="o">></span> <span class="c1">//
- </span><span class="c1"></span><span class="kr">inline</span> <span class="k">auto</span> <span class="n">try_operation_extract_value</span><span class="p">(</span><span class="k">const</span> <span class="n">ForeignExpected</span><span class="o"><</span><span class="n">T</span><span class="p">,</span> <span class="n">E</span><span class="o">></span> <span class="o">&</span><span class="n">v</span><span class="p">)</span>
- <span class="p">{</span>
- <span class="k">return</span> <span class="n">v</span><span class="p">.</span><span class="n">Value</span><span class="p">;</span>
- <span class="p">}</span>
- <span class="n">BOOST_OUTCOME_V2_NAMESPACE_END</span>
- </code></pre></div><a href="https://github.com/boostorg/outcome/tree/master/doc/src/snippets/foreign_try.cpp#L63" class="code-snippet-url" target="_blank">View this code on Github</a></div>
- <p>And now <code>BOOST_OUTCOME_TRY</code> works exactly as expected:</p>
- <div class="code-snippet"><div class="highlight"><pre class="chroma"><code class="language-c++" data-lang="c++"> <span class="k">auto</span> <span class="n">printresult</span> <span class="o">=</span> <span class="p">[](</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">desc</span><span class="p">,</span> <span class="k">auto</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span>
- <span class="k">if</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
- <span class="p">{</span>
- <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="n">desc</span> <span class="o"><<</span> <span class="s">" returns successful "</span> <span class="o"><<</span> <span class="n">x</span><span class="p">.</span><span class="n">value</span><span class="p">()</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
- <span class="p">}</span>
- <span class="k">else</span>
- <span class="p">{</span>
- <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="n">desc</span> <span class="o"><<</span> <span class="s">" returns failure "</span> <span class="o"><<</span> <span class="n">x</span><span class="p">.</span><span class="n">error</span><span class="p">().</span><span class="n">message</span><span class="p">()</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
- <span class="p">}</span>
- <span class="p">};</span>
- <span class="n">printresult</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">new_code(5)"</span><span class="p">,</span> <span class="n">new_code</span><span class="p">(</span><span class="mi">5</span><span class="p">));</span>
- <span class="n">printresult</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">new_code(0)"</span><span class="p">,</span> <span class="n">new_code</span><span class="p">(</span><span class="mi">0</span><span class="p">));</span>
- </code></pre></div><a href="https://github.com/boostorg/outcome/tree/master/doc/src/snippets/foreign_try.cpp#L105" class="code-snippet-url" target="_blank">View this code on Github</a></div>
- <p>… which outputs:</p>
- <pre><code>new_code(5) returns successful 5
- new_code(0) returns failure argument out of domain
- </code></pre>
- </div><p><small>Last revised: June 22, 2019 at 21:22:42 +0100</small></p>
- <hr>
- <div class="spirit-nav">
- <a accesskey="p" href="../recipes/asio-integration-1-70.html"><img src="../images/prev.png" alt="Prev"></a>
- <a accesskey="u" href="../recipes.html"><img src="../images/up.png" alt="Up"></a>
- <a accesskey="h" href="../index.html"><img src="../images/home.png" alt="Home"></a><a accesskey="n" href="../experimental.html"><img src="../images/next.png" alt="Next"></a></div></body>
- </html>
|