outcome/doc/html/motivation/plug_error_code2.html
2019-12-01 13:32:32 +00:00

191 lines
19 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>Plugging a library into boost::system::error_code - 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="../motivation/plug_error_code.html"><img src="../images/prev.png" alt="Prev"></a>
<a accesskey="u" href="../motivation.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="../motivation/narrow_contract.html"><img src="../images/next.png" alt="Next"></a></div><div id="content">
<div class="titlepage"><div><div><h1 style="clear: both">Plugging a library into boost::system::error_code</h1></div></div></div>
<p><a href="../motivation/plug_error_code.html">See here for this guide, but for <code>std::error_code</code></a>.</p>
<p>This section illustrates how you can hook into the <code>boost::system::error_code</code> system from
the Boost in order to work with your own set of error codes. As is usually
the case in C++, doing this is straightforward but requires typing boilerplate
to tell Boost.System about your custom error type. This is not part of Outcome library,
but we still provide this short guide here, because how to do this is not well documented [1].</p>
<p>Suppose you want to report all reasons for failure in converting a <code>std::string</code> to a non-negative <code>int</code>.
The list is:</p>
<ul>
<li><code>EmptyString</code> &ndash; the input string is empty,</li>
<li><code>IllegalChar</code> &ndash; input contains characters that are not digits,</li>
<li><code>TooLong</code> &ndash; input represents a number, but this number would not fit into a variable of type <code>int</code>.</li>
</ul>
<div class="code-snippet"><div class="highlight"><pre class="chroma"><code class="language-c++" data-lang="c++"><span class="cp">#include</span> <span class="cpf">&lt;boost/system/error_code.hpp&gt; // bring in boost::system::error_code et al</span><span class="cp">
</span><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
</span><span class="cp">#include</span> <span class="cpf">&lt;string&gt; // for string printing</span><span class="cp">
</span><span class="cp"></span>
<span class="c1">// This is the custom error code enum
</span><span class="c1"></span><span class="k">enum</span> <span class="k">class</span><span class="err"> </span><span class="nc">ConversionErrc</span>
<span class="p">{</span>
<span class="n">Success</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="c1">// 0 should not represent an error
</span><span class="c1"></span> <span class="n">EmptyString</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span>
<span class="n">IllegalChar</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span>
<span class="n">TooLong</span> <span class="o">=</span> <span class="mi">3</span><span class="p">,</span>
<span class="p">};</span>
<span class="k">namespace</span> <span class="n">boost</span>
<span class="p">{</span>
<span class="k">namespace</span> <span class="n">system</span>
<span class="p">{</span>
<span class="c1">// Tell the C++ 11 STL metaprogramming that enum ConversionErrc
</span><span class="c1"></span> <span class="c1">// is registered with the standard error code system
</span><span class="c1"></span> <span class="k">template</span> <span class="o">&lt;&gt;</span> <span class="k">struct</span> <span class="n">is_error_code_enum</span><span class="o">&lt;</span><span class="n">ConversionErrc</span><span class="o">&gt;</span> <span class="o">:</span> <span class="n">std</span><span class="o">::</span><span class="n">true_type</span>
<span class="p">{</span>
<span class="p">};</span>
<span class="p">}</span> <span class="c1">// namespace system
</span><span class="c1"></span><span class="p">}</span> <span class="c1">// namespace boost
</span><span class="c1"></span>
<span class="k">namespace</span> <span class="n">detail</span>
<span class="p">{</span>
<span class="c1">// Define a custom error code category derived from boost::system::error_category
</span><span class="c1"></span> <span class="k">class</span><span class="err"> </span><span class="nc">ConversionErrc_category</span> <span class="o">:</span> <span class="k">public</span> <span class="n">boost</span><span class="o">::</span><span class="n">system</span><span class="o">::</span><span class="n">error_category</span>
<span class="p">{</span>
<span class="k">public</span><span class="o">:</span>
<span class="c1">// Return a short descriptive name for the category
</span><span class="c1"></span> <span class="k">virtual</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">name</span><span class="p">()</span> <span class="k">const</span> <span class="k">noexcept</span> <span class="k">override</span> <span class="k">final</span> <span class="p">{</span> <span class="k">return</span> <span class="s">&#34;ConversionError&#34;</span><span class="p">;</span> <span class="p">}</span>
<span class="c1">// Return what each enum means in text
</span><span class="c1"></span> <span class="k">virtual</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">message</span><span class="p">(</span><span class="kt">int</span> <span class="n">c</span><span class="p">)</span> <span class="k">const</span> <span class="k">override</span> <span class="k">final</span>
<span class="p">{</span>
<span class="k">switch</span><span class="p">(</span><span class="k">static_cast</span><span class="o">&lt;</span><span class="n">ConversionErrc</span><span class="o">&gt;</span><span class="p">(</span><span class="n">c</span><span class="p">))</span>
<span class="p">{</span>
<span class="k">case</span> <span class="n">ConversionErrc</span><span class="o">::</span><span class="nl">Success</span><span class="p">:</span>
<span class="k">return</span> <span class="s">&#34;conversion successful&#34;</span><span class="p">;</span>
<span class="k">case</span> <span class="n">ConversionErrc</span><span class="o">::</span><span class="nl">EmptyString</span><span class="p">:</span>
<span class="k">return</span> <span class="s">&#34;converting empty string&#34;</span><span class="p">;</span>
<span class="k">case</span> <span class="n">ConversionErrc</span><span class="o">::</span><span class="nl">IllegalChar</span><span class="p">:</span>
<span class="k">return</span> <span class="s">&#34;got non-digit char when converting to a number&#34;</span><span class="p">;</span>
<span class="k">case</span> <span class="n">ConversionErrc</span><span class="o">::</span><span class="nl">TooLong</span><span class="p">:</span>
<span class="k">return</span> <span class="s">&#34;the number would not fit into memory&#34;</span><span class="p">;</span>
<span class="k">default</span><span class="o">:</span>
<span class="k">return</span> <span class="s">&#34;unknown&#34;</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// OPTIONAL: Allow generic error conditions to be compared to me
</span><span class="c1"></span> <span class="k">virtual</span> <span class="n">boost</span><span class="o">::</span><span class="n">system</span><span class="o">::</span><span class="n">error_condition</span> <span class="n">default_error_condition</span><span class="p">(</span><span class="kt">int</span> <span class="n">c</span><span class="p">)</span> <span class="k">const</span> <span class="k">noexcept</span> <span class="k">override</span> <span class="k">final</span>
<span class="p">{</span>
<span class="k">switch</span><span class="p">(</span><span class="k">static_cast</span><span class="o">&lt;</span><span class="n">ConversionErrc</span><span class="o">&gt;</span><span class="p">(</span><span class="n">c</span><span class="p">))</span>
<span class="p">{</span>
<span class="k">case</span> <span class="n">ConversionErrc</span><span class="o">::</span><span class="nl">EmptyString</span><span class="p">:</span>
<span class="k">return</span> <span class="n">make_error_condition</span><span class="p">(</span><span class="n">boost</span><span class="o">::</span><span class="n">system</span><span class="o">::</span><span class="n">errc</span><span class="o">::</span><span class="n">invalid_argument</span><span class="p">);</span>
<span class="k">case</span> <span class="n">ConversionErrc</span><span class="o">::</span><span class="nl">IllegalChar</span><span class="p">:</span>
<span class="k">return</span> <span class="n">make_error_condition</span><span class="p">(</span><span class="n">boost</span><span class="o">::</span><span class="n">system</span><span class="o">::</span><span class="n">errc</span><span class="o">::</span><span class="n">invalid_argument</span><span class="p">);</span>
<span class="k">case</span> <span class="n">ConversionErrc</span><span class="o">::</span><span class="nl">TooLong</span><span class="p">:</span>
<span class="k">return</span> <span class="n">make_error_condition</span><span class="p">(</span><span class="n">boost</span><span class="o">::</span><span class="n">system</span><span class="o">::</span><span class="n">errc</span><span class="o">::</span><span class="n">result_out_of_range</span><span class="p">);</span>
<span class="k">default</span><span class="o">:</span>
<span class="c1">// I have no mapping for this code
</span><span class="c1"></span> <span class="k">return</span> <span class="n">boost</span><span class="o">::</span><span class="n">system</span><span class="o">::</span><span class="n">error_condition</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="o">*</span><span class="k">this</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">};</span>
<span class="p">}</span> <span class="c1">// namespace detail
</span><span class="c1"></span>
<span class="c1">// Define the linkage for this function to be used by external code.
</span><span class="c1">// This would be the usual __declspec(dllexport) or __declspec(dllimport)
</span><span class="c1">// if we were in a Windows DLL etc. But for this example use a global
</span><span class="c1">// instance but with inline linkage so multiple definitions do not collide.
</span><span class="c1"></span><span class="cp">#define THIS_MODULE_API_DECL extern inline
</span><span class="cp"></span>
<span class="c1">// Declare a global function returning a static instance of the custom category
</span><span class="c1"></span><span class="n">THIS_MODULE_API_DECL</span> <span class="k">const</span> <span class="n">detail</span><span class="o">::</span><span class="n">ConversionErrc_category</span> <span class="o">&amp;</span><span class="n">ConversionErrc_category</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">static</span> <span class="n">detail</span><span class="o">::</span><span class="n">ConversionErrc_category</span> <span class="n">c</span><span class="p">;</span>
<span class="k">return</span> <span class="n">c</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Overload the global make_error_code() free function with our
</span><span class="c1">// custom enum. It will be found via ADL by the compiler if needed.
</span><span class="c1"></span><span class="kr">inline</span> <span class="n">boost</span><span class="o">::</span><span class="n">system</span><span class="o">::</span><span class="n">error_code</span> <span class="n">make_error_code</span><span class="p">(</span><span class="n">ConversionErrc</span> <span class="n">e</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="p">{</span><span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">(</span><span class="n">e</span><span class="p">),</span> <span class="n">ConversionErrc_category</span><span class="p">()};</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="n">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// Note that we can now supply ConversionErrc directly to error_code
</span><span class="c1"></span> <span class="n">boost</span><span class="o">::</span><span class="n">system</span><span class="o">::</span><span class="n">error_code</span> <span class="n">ec</span> <span class="o">=</span> <span class="n">ConversionErrc</span><span class="o">::</span><span class="n">IllegalChar</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;ConversionErrc::IllegalChar is printed by boost::system::error_code as &#34;</span>
<span class="o">&lt;&lt;</span> <span class="n">ec</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; with explanatory message &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">ec</span><span class="p">.</span><span class="n">message</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="c1">// We can compare ConversionErrc containing error codes to generic conditions
</span><span class="c1"></span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;ec is equivalent to boost::system::errc::invalid_argument = &#34;</span>
<span class="o">&lt;&lt;</span> <span class="p">(</span><span class="n">ec</span> <span class="o">==</span> <span class="n">std</span><span class="o">::</span><span class="n">errc</span><span class="o">::</span><span class="n">invalid_argument</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;ec is equivalent to boost::system::errc::result_out_of_range = &#34;</span>
<span class="o">&lt;&lt;</span> <span class="p">(</span><span class="n">ec</span> <span class="o">==</span> <span class="n">std</span><span class="o">::</span><span class="n">errc</span><span class="o">::</span><span class="n">result_out_of_range</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div><a href="https://github.com/boostorg/outcome/tree/master/doc/src/snippets/boost-only/error_code_registration.cpp#L32" class="code-snippet-url" target="_blank">View this code on Github</a></div>
<p>This might look like a lot of extra boilerplate over simply using your custom
error code enum directly, but look at the advantages:</p>
<ol>
<li>Any code which can speak <code>boost::system::error_code</code> can now work with errors from your
code, AND without being recompiled.</li>
<li><code>boost::system::system_error</code> can now wrap your custom error codes seamlessly, allowing
your custom error code to be converted into a C++ exception <em>and back out again</em>
without losing information.</li>
<li><code>boost::system::error_code</code> knows how to print itself, and will print your custom error
code without extra work from you. As usually you&rsquo;d need to define a print routine
for any custom error code you&rsquo;d write anyway, there is actually very little extra
boilerplate here.</li>
<li>If you implement the <code>default_error_condition()</code> override, you can allow code
exclusively written to understand <code>boost::system::errc</code> alone to examine your custom error
code domain for equivalence to the standard error conditions, AND without being
recompiled.</li>
</ol>
<div class="notices note" style="background: url('../images/note.png') top left no-repeat padding-box padding-box;">
<div class="notices heading">note</div>
<div class="notices message"><p>This documentation recommends that when you define your custom <code>enum</code> for representing
<code>error_code</code>s, you should always make sure that value 0 never represents an actual error:
it should either represent a success or should not be provided at all. If you only
intend to use your <code>enum</code> inside <code>result&lt;&gt;</code> or <code>outcome&lt;&gt;</code> you can just start your
enumerations from 1. If you intend to also return <code>boost::system::error_code</code> directly from
functions, you should probably define value 0 as success, so that you are able to
inform about function&rsquo;s success by returning <code>MyEnum::Success</code>. This is because <code>error_code</code>&rsquo;s
contextual conversion to <code>bool</code> (which some people use to check if there was an error or not)
only checks for the numeric value of the error code (without looking at error domain (category)).</p>
</div>
</div>
<p>[1]: The only documentation I&rsquo;m aware of is the quite old guide by Chris Kohlhoff, founder of ASIO and the Networking TS:</p>
<ul>
<li><a href="http://blog.think-async.com/2010/04/system-error-support-in-c0x-part-1.html">http://blog.think-async.com/2010/04/system-error-support-in-c0x-part-1.html</a></li>
<li><a href="http://blog.think-async.com/2010/04/system-error-support-in-c0x-part-2.html">http://blog.think-async.com/2010/04/system-error-support-in-c0x-part-2.html</a></li>
<li><a href="http://blog.think-async.com/2010/04/system-error-support-in-c0x-part-3.html">http://blog.think-async.com/2010/04/system-error-support-in-c0x-part-3.html</a></li>
<li><a href="http://blog.think-async.com/2010/04/system-error-support-in-c0x-part-4.html">http://blog.think-async.com/2010/04/system-error-support-in-c0x-part-4.html</a></li>
<li><a href="http://blog.think-async.com/2010/04/system-error-support-in-c0x-part-5.html">http://blog.think-async.com/2010/04/system-error-support-in-c0x-part-5.html</a></li>
</ul>
</div><p><small>Last revised: June 21, 2019 at 22:46:55 &#43;0100</small></p>
<hr>
<div class="spirit-nav">
<a accesskey="p" href="../motivation/plug_error_code.html"><img src="../images/prev.png" alt="Prev"></a>
<a accesskey="u" href="../motivation.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="../motivation/narrow_contract.html"><img src="../images/next.png" alt="Next"></a></div></body>
</html>