132 lines
10 KiB
HTML
132 lines
10 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>Narrow contracts - 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_code2.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="../tutorial.html"><img src="../images/next.png" alt="Next"></a></div><div id="content">
|
|
<div class="titlepage"><div><div><h1 style="clear: both">Narrow contracts</h1></div></div></div>
|
|
<p>A program’s thread of execution can enter a “disappointing” state for two reasons:</p>
|
|
|
|
<ul>
|
|
<li>due to disappointing situation in the environment (operating system, external input),
|
|
or</li>
|
|
<li>due to a bug in the program.</li>
|
|
</ul>
|
|
|
|
<p>The key to handling these disappointments correctly is to identify to which
|
|
category they belong, and use the tools adequate for a given category. In this
|
|
tutorial when we say “error” or “failure” we only refer to the first category.
|
|
A bug is not an error.</p>
|
|
|
|
<p>A bug is when a program is something else than what it is supposed to be. The
|
|
correct action in that case is to change the program so that it is exactly what
|
|
it is supposed to be. Unfortunately, sometimes the symptoms of a bug are only
|
|
detected when the system is running and at this point no code changes are possible.</p>
|
|
|
|
<p>In contrast, a failure is when a correct function in a correct program reflects
|
|
some disappointing behavior in the environment. The correct action in that case
|
|
is for the program to take a control path different than usual, which will likely
|
|
cancel some operations and will likely result in different communication with the
|
|
outside world.</p>
|
|
|
|
<p>Symptoms of bugs can sometimes be detected during compilation or static program
|
|
analysis or at run-time when observing certain values of objects that are declared
|
|
never to be valid at certain points. One classical example is passing a null pointer
|
|
to functions that expect a pointer to a valid object:</p>
|
|
<div class="highlight"><pre class="chroma"><code class="language-c++" data-lang="c++"><span class="kt">int</span> <span class="nf">f</span><span class="p">(</span><span class="kt">int</span> <span class="o">*</span> <span class="n">pi</span><span class="p">)</span> <span class="c1">// expects: pi != nullptr
|
|
</span><span class="c1"></span><span class="p">{</span>
|
|
<span class="k">return</span> <span class="o">*</span><span class="n">pi</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
</code></pre></div>
|
|
<p>Passing a null pointer where it is not expected is so common a bug that tools
|
|
are very good at finding them. For instance, static analyzers will usually detect
|
|
it without even executing your code. Similarly, tools like undefined behavior
|
|
sanitizers will compile a code as the one above so that a safety check is performed
|
|
to check if the pointer is null, and an error message will be logged and program
|
|
optionally terminated.</p>
|
|
|
|
<p>More, compilers can perform optimizations based on undefined behavior caused by
|
|
dereferencing a null pointer. In the following code:</p>
|
|
<div class="highlight"><pre class="chroma"><code class="language-c++" data-lang="c++"><span class="n">pair</span><span class="o"><</span><span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="o">></span> <span class="n">g</span><span class="p">(</span><span class="kt">int</span> <span class="o">*</span> <span class="n">pi</span><span class="p">)</span> <span class="c1">// expects: pi != nullptr
|
|
</span><span class="c1"></span><span class="p">{</span>
|
|
<span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="o">*</span><span class="n">pi</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
|
|
<span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="p">(</span><span class="n">pi</span> <span class="o">==</span> <span class="k">nullptr</span><span class="p">)</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
|
|
<span class="k">return</span> <span class="p">{</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">};</span>
|
|
<span class="p">}</span>
|
|
</code></pre></div>
|
|
<p>The compiler can see that if <code>pi</code> is null, the program would have undefined
|
|
behavior. Since undefined behavior is required by the C++ standard to never
|
|
be the programmer’s intention, the compiler
|
|
assumes that apparently this function is never called with <code>pi == nullptr</code>. If so,
|
|
<code>j</code> is always <code>0</code> and the code can be transformed to a faster one:</p>
|
|
<div class="highlight"><pre class="chroma"><code class="language-c++" data-lang="c++"><span class="n">pair</span><span class="o"><</span><span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="o">></span> <span class="n">g</span><span class="p">(</span><span class="kt">int</span> <span class="o">*</span> <span class="n">pi</span><span class="p">)</span> <span class="c1">// expects: pi != nullptr
|
|
</span><span class="c1"></span><span class="p">{</span>
|
|
<span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="o">*</span><span class="n">pi</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
|
|
<span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
|
|
<span class="k">return</span> <span class="p">{</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">};</span>
|
|
<span class="p">}</span>
|
|
</code></pre></div>
|
|
<p>Functions like the one above that declare that certain values of input parameters
|
|
must not be passed to them are said to have a <em>narrow contract</em>.</p>
|
|
|
|
<p>Compilers give you non-standard tools to tell them about narrow contracts, so
|
|
that they can detect it and make use of it the same way as they are detecting
|
|
invalid null pointers. For instance, if a function in your library takes an <code>int</code>
|
|
and declares that the value of this <code>int</code> must never be negative. You can use
|
|
<code>__builtin_trap()</code> available in GCC and clang:</p>
|
|
<div class="highlight"><pre class="chroma"><code class="language-c++" data-lang="c++"><span class="kt">void</span> <span class="nf">h</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span> <span class="c1">// expects: i >= 0
|
|
</span><span class="c1"></span><span class="p">{</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o"><</span> <span class="mi">0</span><span class="p">)</span> <span class="n">__builtin_trap</span><span class="p">();</span>
|
|
|
|
<span class="c1">// normal program logic follows ...
|
|
</span><span class="c1"></span><span class="p">}</span>
|
|
</code></pre></div>
|
|
<p>This instruction when hit, causes the program to exit abnormally, which means:</p>
|
|
|
|
<ul>
|
|
<li>a debugger can be launched,</li>
|
|
<li>static analyzer can warn you if it can detect a program flow that reaches this
|
|
point,</li>
|
|
<li>UB-sanitizer can log error message when it hits it.</li>
|
|
</ul>
|
|
|
|
<p>Another tool you could use is <code>__builtin_unreachable()</code>, also available in GCC
|
|
and clang:</p>
|
|
<div class="highlight"><pre class="chroma"><code class="language-c++" data-lang="c++"><span class="kt">void</span> <span class="nf">h</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span> <span class="c1">// expects: i >= 0
|
|
</span><span class="c1"></span><span class="p">{</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o"><</span> <span class="mi">0</span><span class="p">)</span> <span class="n">__builtin_unreachable</span><span class="p">();</span>
|
|
|
|
<span class="c1">// normal program logic follows ...
|
|
</span><span class="c1"></span><span class="p">}</span>
|
|
</code></pre></div>
|
|
<p>This gives a hint to the tools: the programmer guarantees that the program flow
|
|
will never reach to the point of executing it. In other words, it is undefined
|
|
behavior if control reaches this point. Compiler and other tools can take this
|
|
for granted. This way they can deduce that expression <code>i < 0</code> will never be true,
|
|
and they can further use this assumption to issue warnings or to optimize the code.
|
|
UB-sanitizers can use it to inject a log message and terminate if this point is
|
|
nonetheless reached.</p>
|
|
|
|
<p>Allowing for some input values to be invalid works similarly to cyclic redundancy
|
|
checks. It allows for the possibility to observe the symptoms of the bugs (not
|
|
the bugs themselves), and if the symptom is revealed the hunt for the bug can start.
|
|
This is not only tools that can now easily detect symptoms of bugs, but also
|
|
humans during the code review. A reviewer can now say, “hey, function <code>h()</code> is
|
|
expecting a non-negative value, but this <code>i</code> is actually <code>-1</code>; maybe you wanted
|
|
to pass <code>j</code> instead?“.</p>
|
|
|
|
|
|
</div><p><small>Last revised: April 26, 2019 at 17:43:41 +0200</small></p>
|
|
<hr>
|
|
<div class="spirit-nav">
|
|
<a accesskey="p" href="../motivation/plug_error_code2.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="../tutorial.html"><img src="../images/next.png" alt="Next"></a></div></body>
|
|
</html>
|