179 lines
15 KiB
HTML
179 lines
15 KiB
HTML
<!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>
|