plug_error_code.html 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
  2. <html><meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  3. <title>Plugging a library into std::error_code - Boost.Outcome documentation</title>
  4. <link rel="stylesheet" href="../css/boost.css" type="text/css">
  5. <meta name="generator" content="Hugo 0.52 with Boostdoc theme">
  6. <meta name="viewport" content="width=device-width,initial-scale=1.0"/>
  7. <link rel="icon" href="../images/favicon.ico" type="image/ico"/>
  8. <body><div class="spirit-nav">
  9. <a accesskey="p" href="../motivation/std_error_code.html"><img src="../images/prev.png" alt="Prev"></a>
  10. <a accesskey="u" href="../motivation.html"><img src="../images/up.png" alt="Up"></a>
  11. <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">
  12. <div class="titlepage"><div><div><h1 style="clear: both">Plugging a library into std::error_code`</h1></div></div></div>
  13. <p><a href="../motivation/plug_error_code2.html">See here for this guide, but for <code>boost::system::error_code</code></a>.</p>
  14. <p>This section illustrates how you can hook into the <code>std::error_code</code> system from
  15. the Standard Library in order to work with your own set of error codes. As is usually
  16. the case in C++, doing this is straightforward but requires typing boilerplate
  17. to tell the C++ STL about your custom error type. This is not part of Outcome library,
  18. but we still provide this short guide here, because how to do this is not well documented [1].</p>
  19. <p>Suppose you want to report all reasons for failure in converting a <code>std::string</code> to a non-negative <code>int</code>.
  20. The list is:</p>
  21. <ul>
  22. <li><code>EmptyString</code> &ndash; the input string is empty,</li>
  23. <li><code>IllegalChar</code> &ndash; input contains characters that are not digits,</li>
  24. <li><code>TooLong</code> &ndash; input represents a number, but this number would not fit into a variable of type <code>int</code>.</li>
  25. </ul>
  26. <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;iostream&gt;</span><span class="cp">
  27. </span><span class="cp">#include</span> <span class="cpf">&lt;string&gt; // for string printing</span><span class="cp">
  28. </span><span class="cp">#include</span> <span class="cpf">&lt;system_error&gt; // bring in std::error_code et al</span><span class="cp">
  29. </span><span class="cp"></span>
  30. <span class="c1">// This is the custom error code enum
  31. </span><span class="c1"></span><span class="k">enum</span> <span class="k">class</span><span class="err"> </span><span class="nc">ConversionErrc</span>
  32. <span class="p">{</span>
  33. <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
  34. </span><span class="c1"></span> <span class="n">EmptyString</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span>
  35. <span class="n">IllegalChar</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span>
  36. <span class="n">TooLong</span> <span class="o">=</span> <span class="mi">3</span><span class="p">,</span>
  37. <span class="p">};</span>
  38. <span class="k">namespace</span> <span class="n">std</span>
  39. <span class="p">{</span>
  40. <span class="c1">// Tell the C++ 11 STL metaprogramming that enum ConversionErrc
  41. </span><span class="c1"></span> <span class="c1">// is registered with the standard error code system
  42. </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">true_type</span>
  43. <span class="p">{</span>
  44. <span class="p">};</span>
  45. <span class="p">}</span>
  46. <span class="k">namespace</span> <span class="n">detail</span>
  47. <span class="p">{</span>
  48. <span class="c1">// Define a custom error code category derived from std::error_category
  49. </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>
  50. <span class="p">{</span>
  51. <span class="k">public</span><span class="o">:</span>
  52. <span class="c1">// Return a short descriptive name for the category
  53. </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>
  54. <span class="c1">// Return what each enum means in text
  55. </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>
  56. <span class="p">{</span>
  57. <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>
  58. <span class="p">{</span>
  59. <span class="k">case</span> <span class="n">ConversionErrc</span><span class="o">::</span><span class="nl">Success</span><span class="p">:</span>
  60. <span class="k">return</span> <span class="s">&#34;conversion successful&#34;</span><span class="p">;</span>
  61. <span class="k">case</span> <span class="n">ConversionErrc</span><span class="o">::</span><span class="nl">EmptyString</span><span class="p">:</span>
  62. <span class="k">return</span> <span class="s">&#34;converting empty string&#34;</span><span class="p">;</span>
  63. <span class="k">case</span> <span class="n">ConversionErrc</span><span class="o">::</span><span class="nl">IllegalChar</span><span class="p">:</span>
  64. <span class="k">return</span> <span class="s">&#34;got non-digit char when converting to a number&#34;</span><span class="p">;</span>
  65. <span class="k">case</span> <span class="n">ConversionErrc</span><span class="o">::</span><span class="nl">TooLong</span><span class="p">:</span>
  66. <span class="k">return</span> <span class="s">&#34;the number would not fit into memory&#34;</span><span class="p">;</span>
  67. <span class="k">default</span><span class="o">:</span>
  68. <span class="k">return</span> <span class="s">&#34;unknown&#34;</span><span class="p">;</span>
  69. <span class="p">}</span>
  70. <span class="p">}</span>
  71. <span class="c1">// OPTIONAL: Allow generic error conditions to be compared to me
  72. </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>
  73. <span class="p">{</span>
  74. <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>
  75. <span class="p">{</span>
  76. <span class="k">case</span> <span class="n">ConversionErrc</span><span class="o">::</span><span class="nl">EmptyString</span><span class="p">:</span>
  77. <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>
  78. <span class="k">case</span> <span class="n">ConversionErrc</span><span class="o">::</span><span class="nl">IllegalChar</span><span class="p">:</span>
  79. <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>
  80. <span class="k">case</span> <span class="n">ConversionErrc</span><span class="o">::</span><span class="nl">TooLong</span><span class="p">:</span>
  81. <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>
  82. <span class="k">default</span><span class="o">:</span>
  83. <span class="c1">// I have no mapping for this code
  84. </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>
  85. <span class="p">}</span>
  86. <span class="p">}</span>
  87. <span class="p">};</span>
  88. <span class="p">}</span>
  89. <span class="c1">// Define the linkage for this function to be used by external code.
  90. </span><span class="c1">// This would be the usual __declspec(dllexport) or __declspec(dllimport)
  91. </span><span class="c1">// if we were in a Windows DLL etc. But for this example use a global
  92. </span><span class="c1">// instance but with inline linkage so multiple definitions do not collide.
  93. </span><span class="c1"></span><span class="cp">#define THIS_MODULE_API_DECL extern inline
  94. </span><span class="cp"></span>
  95. <span class="c1">// Declare a global function returning a static instance of the custom category
  96. </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>
  97. <span class="p">{</span>
  98. <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>
  99. <span class="k">return</span> <span class="n">c</span><span class="p">;</span>
  100. <span class="p">}</span>
  101. <span class="c1">// Overload the global make_error_code() free function with our
  102. </span><span class="c1">// custom enum. It will be found via ADL by the compiler if needed.
  103. </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>
  104. <span class="p">{</span>
  105. <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>
  106. <span class="p">}</span>
  107. <span class="kt">int</span> <span class="n">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
  108. <span class="p">{</span>
  109. <span class="c1">// Note that we can now supply ConversionErrc directly to error_code
  110. </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>
  111. <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 std::error_code as &#34;</span>
  112. <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>
  113. <span class="c1">// We can compare ConversionErrc containing error codes to generic conditions
  114. </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 std::errc::invalid_argument = &#34;</span>
  115. <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>
  116. <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 std::errc::result_out_of_range = &#34;</span>
  117. <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>
  118. <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
  119. <span class="p">}</span>
  120. </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>
  121. <p>This might look like a lot of extra boilerplate over simply using your custom
  122. error code enum directly, but look at the advantages:</p>
  123. <ol>
  124. <li>Any code which can speak <code>std::error_code</code> can now work with errors from your
  125. code, AND without being recompiled.</li>
  126. <li><code>std::system_error</code> can now wrap your custom error codes seamlessly, allowing
  127. your custom error code to be converted into a C++ exception <em>and back out again</em>
  128. without losing information.</li>
  129. <li><code>std::error_code</code> knows how to print itself, and will print your custom error
  130. code without extra work from you. As usually you&rsquo;d need to define a print routine
  131. for any custom error code you&rsquo;d write anyway, there is actually very little extra
  132. boilerplate here.</li>
  133. <li>If you implement the <code>default_error_condition()</code> override, you can allow code
  134. exclusively written to understand <code>std::errc</code> alone to examine your custom error
  135. code domain for equivalence to the standard error conditions, AND without being
  136. recompiled.</li>
  137. </ol>
  138. <div class="notices note" style="background: url('../images/note.png') top left no-repeat padding-box padding-box;">
  139. <div class="notices heading">note</div>
  140. <div class="notices message"><p>This documentation recommends that when you define your custom <code>enum</code> for representing
  141. <code>error_code</code>s, you should always make sure that value 0 never represents an actual error:
  142. it should either represent a success or should not be provided at all. If you only
  143. intend to use your <code>enum</code> inside <code>result&lt;&gt;</code> or <code>outcome&lt;&gt;</code> you can just start your
  144. enumerations from 1. If you intend to also return <code>std::error_code</code> directly from
  145. functions, you should probably define value 0 as success, so that you are able to
  146. inform about function&rsquo;s success by returning <code>MyEnum::Success</code>. This is because <code>error_code</code>&rsquo;s
  147. contextual conversion to <code>bool</code> (which some people use to check if there was an error or not)
  148. only checks for the numeric value of the error code (without looking at error domain (category)).</p>
  149. </div>
  150. </div>
  151. <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>
  152. <ul>
  153. <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>
  154. <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>
  155. <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>
  156. <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>
  157. <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>
  158. </ul>
  159. </div><p><small>Last revised: June 21, 2019 at 22:46:55 &#43;0100</small></p>
  160. <hr>
  161. <div class="spirit-nav">
  162. <a accesskey="p" href="../motivation/std_error_code.html"><img src="../images/prev.png" alt="Prev"></a>
  163. <a accesskey="u" href="../motivation.html"><img src="../images/up.png" alt="Up"></a>
  164. <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>
  165. </html>