188 lines
18 KiB
HTML
188 lines
18 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 std::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/std_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/plug_error_code2.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 std::error_code`</h1></div></div></div>
|
|
<p><a href="../motivation/plug_error_code2.html">See here for this guide, but for <code>boost::system::error_code</code></a>.</p>
|
|
|
|
<p>This section illustrates how you can hook into the <code>std::error_code</code> system from
|
|
the Standard Library 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 the C++ STL 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> – the input string is empty,</li>
|
|
<li><code>IllegalChar</code> – input contains characters that are not digits,</li>
|
|
<li><code>TooLong</code> – 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"><iostream></span><span class="cp">
|
|
</span><span class="cp">#include</span> <span class="cpf"><string> // for string printing</span><span class="cp">
|
|
</span><span class="cp">#include</span> <span class="cpf"><system_error> // bring in std::error_code et al</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">std</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"><></span> <span class="k">struct</span> <span class="n">is_error_code_enum</span><span class="o"><</span><span class="n">ConversionErrc</span><span class="o">></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="k">namespace</span> <span class="n">detail</span>
|
|
<span class="p">{</span>
|
|
<span class="c1">// Define a custom error code category derived from std::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">std</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">"ConversionError"</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"><</span><span class="n">ConversionErrc</span><span class="o">></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">"conversion successful"</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">"converting empty string"</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">"got non-digit char when converting to a number"</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">"the number would not fit into memory"</span><span class="p">;</span>
|
|
<span class="k">default</span><span class="o">:</span>
|
|
<span class="k">return</span> <span class="s">"unknown"</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">std</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"><</span><span class="n">ConversionErrc</span><span class="o">></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">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="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">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="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">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="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">std</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">// 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">&</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">std</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"><</span><span class="kt">int</span><span class="o">></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">std</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"><<</span> <span class="s">"ConversionErrc::IllegalChar is printed by std::error_code as "</span>
|
|
<span class="o"><<</span> <span class="n">ec</span> <span class="o"><<</span> <span class="s">" with explanatory message "</span> <span class="o"><<</span> <span class="n">ec</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="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"><<</span> <span class="s">"ec is equivalent to std::errc::invalid_argument = "</span>
|
|
<span class="o"><<</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"><<</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"><<</span> <span class="s">"ec is equivalent to std::errc::result_out_of_range = "</span>
|
|
<span class="o"><<</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"><<</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/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>std::error_code</code> can now work with errors from your
|
|
code, AND without being recompiled.</li>
|
|
<li><code>std::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>std::error_code</code> knows how to print itself, and will print your custom error
|
|
code without extra work from you. As usually you’d need to define a print routine
|
|
for any custom error code you’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>std::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<></code> or <code>outcome<></code> you can just start your
|
|
enumerations from 1. If you intend to also return <code>std::error_code</code> directly from
|
|
functions, you should probably define value 0 as success, so that you are able to
|
|
inform about function’s success by returning <code>MyEnum::Success</code>. This is because <code>error_code</code>’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’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 +0100</small></p>
|
|
<hr>
|
|
<div class="spirit-nav">
|
|
<a accesskey="p" href="../motivation/std_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/plug_error_code2.html"><img src="../images/next.png" alt="Next"></a></div></body>
|
|
</html>
|