b62ef86e28
corrections in cpp promotion added make_safe_literal separated integers from other types dropped example91 updated CMakeLists.txt and Jamfile.v2 accordingly
388 lines
18 KiB
XML
388 lines
18 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE section PUBLIC "-//Boost//DTD BoostBook XML V1.1//EN"
|
|
"http://www.boost.org/tools/boostbook/dtd/boostbook.dtd">
|
|
<section id="safe_numerics.eliminate_runtime_penalty">
|
|
<title>Eliminating Runtime Penalty</title>
|
|
|
|
<para>Up until now, we've mostly focused on detecting when incorrect results
|
|
are produced and handling these occurrences either by throwing an exception
|
|
or invoking some designated function. We've achieved our goal of detecting
|
|
and handling arithmetically incorrect behavior - but at cost of checking
|
|
many arithmetic operations at runtime. It is a fact that many C++
|
|
programmers will find this trade-off unacceptable. So the question arises as
|
|
to how we might minimize or eliminate this runtime penalty.</para>
|
|
|
|
<para>The first step is to determine what parts of a program might invoke
|
|
exceptions. The following program is similar to previous examples but uses a
|
|
special exception policy: <link
|
|
linkend="safe_numerics.exception_policies.loose_trap_policy"><code>loose_trap_policy</code></link>.</para>
|
|
|
|
<para><programlisting><xi:include href="../../example/example81.cpp"
|
|
parse="text" xmlns:xi="http://www.w3.org/2001/XInclude"/></programlisting>Now,
|
|
any expression which <emphasis><emphasis
|
|
role="bold">might</emphasis></emphasis> fail at runtime is flagged with a
|
|
compile time error. There is no longer any need for <code>try/catch</code>
|
|
blocks. Since this program does not compile, the <emphasis
|
|
role="bold">library absolutely <emphasis role="bold">guarantees that no
|
|
arithmetic expression</emphasis> will yield incorrect results</emphasis>.
|
|
Furthermore, it is <emphasis role="bold">absolutely guaranteed that no
|
|
exception will ever be thrown</emphasis>. This is our original goal.</para>
|
|
|
|
<para>Now all we need to do is make the program compile. There are a couple
|
|
of ways to achieve this.</para>
|
|
|
|
<section id="safe_numerics.eliminate_runtime_penalty.2">
|
|
<title>Using <link linkend="safe_numerics.safe_range">safe_range</link>
|
|
and <link linkend="safe_numerics.safe_literal">safe_literal</link></title>
|
|
|
|
<para>When trying to avoid arithmetic errors of the above type,
|
|
programmers will select data types which are wide enough to hold values
|
|
large enough to be certain that results won't overflow, but are not so
|
|
large as to make the program needlessly inefficient. In the example below,
|
|
we presume we know that the values we want to work with fall in the range
|
|
[-24,82]. So we "know" the program will always result in a correct result.
|
|
But since we trust no one, and since the program could change and the
|
|
expressions be replaced with other ones, we'll still use the <link
|
|
linkend="safe_numerics.exception_policies.loose_trap_policy"><code>loose_trap_policy</code></link>
|
|
exception policy to verify at compile time that what we "know" to be true
|
|
is in fact true.</para>
|
|
|
|
<programlisting><xi:include href="../../example/example83.cpp"
|
|
parse="text" xmlns:xi="http://www.w3.org/2001/XInclude"/></programlisting>
|
|
|
|
<para><itemizedlist>
|
|
<listitem>
|
|
<para><code><code>safe_signed_range</code></code> defines a type
|
|
which is limited to the indicated range. Out of range assignments
|
|
will be detected at compile time if possible (as in this case) or at
|
|
run time if necessary.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>A safe range could be defined with the same minimum and
|
|
maximum value effectively restricting the type to holding one
|
|
specific value. This is what <code>safe_signed_literal</code>
|
|
does.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Defining constants with <code>safe_signed_literal</code>
|
|
enables the library to correctly anticipate the correct range of the
|
|
results of arithmetic expressions at compile time.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>The usage of <code><link
|
|
linkend="safe_numerics.exception_policies.loose_trap_policy"><code>loose_trap_policy</code></link></code>
|
|
will mean that any assignment to z which could be outside its legal
|
|
range will result in a compile time error.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>All safe integer operations are implemented as constant
|
|
expressions. The usage of <code>constexpr</code> will guarantee that
|
|
<code>z</code> will be available at compile time for any subsequent
|
|
use.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>So if this program compiles, it's guaranteed to return a valid
|
|
result.</para>
|
|
</listitem>
|
|
</itemizedlist>The output uses a custom output manipulator,
|
|
<code>safe_format</code>, for safe types to display the underlying type
|
|
and its range as well as current value. This program produces the
|
|
following run time output.</para>
|
|
|
|
<screen>example 83:
|
|
x = <signed char>[10,10] = 10
|
|
y = <signed char>[67,67] = 67
|
|
x + y = <int>[77,77] = 77
|
|
z = <signed char>[-24,82] = 77</screen>
|
|
|
|
<para>Take note of the various variable types:<itemizedlist>
|
|
<listitem>
|
|
<para><code>x</code> and <code>y</code> are safe types with fixed
|
|
ranges which encompass one single value. They can hold only that
|
|
value which they have been assigned at compile time.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><code>The sum x + y can also be determined at compile
|
|
time.</code></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>The type of z is defined so that It can hold only values in
|
|
the closed range -24,82. We can assign the sum of x + y because it
|
|
is in the range that <code>z</code> is guaranteed to hold. If the
|
|
sum could not be be guaranteed to fall in the range of
|
|
<code>z</code>, we would get a compile time error due to the fact we
|
|
are using the <code>loose_trap_policy</code> exception
|
|
policy.</para>
|
|
</listitem>
|
|
</itemizedlist>All this information regarding the range and values of
|
|
variables has been determined at compile time. There is no runtime
|
|
overhead. The usage of safe types does not alter the calculations or
|
|
results in anyway. So <code>safe_t</code> and <code>const_safe_t</code>
|
|
could be redefined to <code>int</code> and <code>const int</code>
|
|
respectively and the program would operate identically - although it might
|
|
We could compile the program for another machine - as is common when
|
|
building embedded systems and know (assuming the target machine
|
|
architecture was the same as our native one) that no erroneous results
|
|
would ever be produced.</para>
|
|
</section>
|
|
|
|
<section id="safe_numerics.eliminate_runtime_penalty.1">
|
|
<title>Using Automatic Type Promotion</title>
|
|
|
|
<para>The C++ standard describes how binary operations on different
|
|
integer types are handled. Here is a simplified version of the
|
|
rules:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>promote any operand smaller than <code>int</code> to an
|
|
<code>int</code> or <code>unsigned int</code>.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>if the size of the signed operand is larger than the size of the
|
|
signed operand, the type of the result will be signed. Otherwise, the
|
|
type of the result will be unsigned.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Convert the type each operand to the type of the result,
|
|
expanding the size as necessary.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Perform the operation the two resultant operands.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>So the type of the result of some binary operation may be different
|
|
than the types of either or both of the original operands.</para>
|
|
|
|
<para>If the values are large, the result can exceed the size that the
|
|
resulting integer type can hold. This is what we call "overflow". The
|
|
C/C++ standard characterizes this as undefined behavior and leaves to
|
|
compiler implementors the decision as to how such a situation will be
|
|
handled. Usually, this means just truncating the result to fit into the
|
|
result type - which sometimes will make the result arithmetically
|
|
incorrect. However, depending on the compiler and compile time switch
|
|
settings, such cases may result in some sort of run time exception or
|
|
silently producing some arbitrary result.</para>
|
|
|
|
<para>The complete signature for a safe integer type is:</para>
|
|
|
|
<para><programlisting>template <
|
|
class T, // underlying integer type
|
|
class P = native, // type promotion policy class
|
|
class E = default_exception_policy // error handling policy class
|
|
>
|
|
safe;
|
|
</programlisting></para>
|
|
|
|
<para>The promotion rules for arithmetic operations are implemented in the
|
|
default <code><link
|
|
linkend="safe_numerics.promotion_policies.native">native</link></code>
|
|
type promotion policy are consistent with those of standard C++</para>
|
|
|
|
<para>Up until now, we've focused on detecting when an arithmetic error
|
|
occurs and invoking an exception or other kind of error handler.</para>
|
|
|
|
<para>But now we look at another option. Using the <link
|
|
linkend="safe_numerics.promotion_policies.automatic"><code>automatic</code></link>
|
|
type promotion policy, we can change the rules of C++ arithmetic for safe
|
|
types to something like the following:</para>
|
|
|
|
<para><itemizedlist>
|
|
<listitem>
|
|
<para>for any C++ numeric type, we know from <ulink
|
|
url="http://en.cppreference.com/w/cpp/types/numeric_limits"><code>std::numeric_limits</code></ulink>
|
|
what the maximum and minimum values that a variable can be - this
|
|
defines a closed interval.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>For any binary operation on these types, we can calculate the
|
|
interval of the result at compile time.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>From this interval we can select a new type which can be
|
|
guaranteed to hold the result and use this for the calculation. This
|
|
is more or less equivalent to the following code:</para>
|
|
|
|
<programlisting>int x, y;
|
|
int z = x + y // could overflow
|
|
// so replace with the following:
|
|
int x, y;
|
|
long z = (long)x + (long)y; // can never overflow</programlisting>
|
|
|
|
<para>One could do this by editing his code manually as above, but
|
|
such a task would be tedious, error prone, non-portable and leave
|
|
the resulting code hard to read and verify. Using the <link
|
|
linkend="safe_numerics.promotion_policies.automatic"><code>automatic</code></link>
|
|
type promotion policy will achieve the equivalent result without
|
|
these problems.</para>
|
|
</listitem>
|
|
</itemizedlist></para>
|
|
|
|
<para>When using the <link
|
|
linkend="safe_numerics.promotion_policies.automatic"><code>automatic</code></link>
|
|
type promotion policy, with a given a binary operation, we silently
|
|
promote the types of the operands to a wider result type so the result
|
|
cannot overflow. This is a fundamental departure from the C++ Standard
|
|
behavior.</para>
|
|
|
|
<para>If the interval of the result cannot be guaranteed to fit in the
|
|
largest type that the machine can handle (usually 64 bits these days), the
|
|
largest available integer type with the correct result sign is used. So
|
|
even with our "automatic" type promotion scheme, it's still possible to
|
|
overflow. So while our <link
|
|
linkend="safe_numerics.promotion_policies.automatic"><code>automatic</code></link>
|
|
type promotion policy might eliminate exceptions in our example above, it
|
|
wouldn't be guaranteed to eliminate them for all programs.</para>
|
|
|
|
<para>Using the <link
|
|
linkend="safe_numerics.exception_policies.loose_trap_policy"><code>loose_trap_policy</code></link>
|
|
exception policy will produce a compile time error anytime it's possible
|
|
for an error to occur.</para>
|
|
|
|
<para>This small example illustrates how to use automatic type promotion
|
|
to eliminate all runtime penalty.</para>
|
|
|
|
<para><programlisting><xi:include href="../../example/example82.cpp"
|
|
parse="text" xmlns:xi="http://www.w3.org/2001/XInclude"/></programlisting></para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>the <link
|
|
linkend="safe_numerics.promotion_policies.automatic"><code>automatic</code></link>
|
|
type promotion policy has rendered the result of the sum of two
|
|
<code>integers</code> as a <code>safe<long</code>> type.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>our program compiles without error - even when using the <link
|
|
linkend="safe_numerics.exception_policies.loose_trap_policy"><code>loose_trap_policy</code></link>
|
|
exception policy. This is because since a <code>long</code> can always
|
|
hold the result of the sum of two integers.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>We do not need to use the <code>try/catch</code> idiom to handle
|
|
arithmetic errors - we will have no exceptions.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>We only needed to change two lines of code to achieve our goal
|
|
of guaranteed program correctness with no runtime penalty.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>The above program produces the following output:</para>
|
|
|
|
<para><screen>example 82:
|
|
x = <int>[-2147483648,2147483647] = 2147483647
|
|
y = <int>[-2147483648,2147483647] = 2
|
|
x + y = <long>[-4294967296,4294967294] = 2147483649
|
|
</screen></para>
|
|
|
|
<para>Note that if any time in the future we were to change
|
|
safe<int> to safe<long long> the program could now overflow.
|
|
But since we're using <link
|
|
linkend="safe_numerics.exception_policies.loose_trap_policy"><code>loose_trap_policy</code></link>
|
|
the modified program would fail to compile. At this point we'd have to
|
|
alter our yet program again to eliminate run time penalty or set aside our
|
|
goal of zero run time overhead and change the exception policy to <link
|
|
linkend="safe_numerics.exception_policies.default_exception_policy"><code>default_exception_policy</code></link>
|
|
.</para>
|
|
|
|
<para>Note that once we use automatic type promotion, our programming
|
|
language isn't C/C++ anymore. So don't be tempted to so something like the
|
|
following:</para>
|
|
|
|
<programlisting>// DON'T DO THIS !
|
|
#if defined(NDEBUG)
|
|
using safe_t = boost::numeric::safe<
|
|
int,
|
|
boost::numeric::automatic, // note use of "automatic" policy!!!
|
|
boost::numeric::loose_trap_policy
|
|
>;
|
|
#else
|
|
using safe_t = boost::numeric::safe<int>;
|
|
#endif
|
|
</programlisting>
|
|
</section>
|
|
|
|
<section id="safe_numerics.eliminate_runtime_penalty.3">
|
|
<title>Mixing Approaches</title>
|
|
|
|
<para>For purposes of exposition, we've divided the discussion of how to
|
|
eliminate runtime penalties by the different approaches available. A
|
|
realistic program could likely include all techniques mentioned above.
|
|
Consider the following:</para>
|
|
|
|
<programlisting><xi:include href="../../example/example84.cpp"
|
|
parse="text" xmlns:xi="http://www.w3.org/2001/XInclude"/></programlisting>
|
|
|
|
<para><itemizedlist>
|
|
<listitem>
|
|
<para>As before, we define a type <code>safe_t</code> to reflect our
|
|
view of legal values for this program. This uses the <link
|
|
linkend="safe_numerics.promotion_policies.automatic"><code>automatic</code></link>
|
|
type promotion policy as well as the <link
|
|
linkend="safe_numerics.exception_policies.loose_trap_policy"><code>loose_trap_policy</code></link>
|
|
exception policy to enforce elimination of runtime penalties.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>The function <code>f</code> accepts only arguments of type
|
|
<code>safe_t</code> so there is no need to check the input values.
|
|
This performs the functionality of <emphasis><emphasis
|
|
role="bold">programming by contract</emphasis></emphasis> with no
|
|
runtime cost.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>In addition, we define <code>input_safe_t</code> to be used
|
|
when reading variables from the program console. Clearly, these can
|
|
only be checked at runtime so they use the throw_exception policy.
|
|
When variables are read from the console they are checked for legal
|
|
values. We need no ad hoc code to do this, as these types are
|
|
guaranteed to contain legal values and will throw an exception when
|
|
this guarantee is violated. In other words, we automatically get
|
|
checking of input variables with no additional programming.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>On calling of the function <code>f</code>, arguments of type
|
|
<code>input_safe_t</code> are converted to values of type
|
|
<code>safe_t</code> . In this particular example, it can be
|
|
determined at compile time that construction of an instance of a
|
|
<code>safe_t</code> from an <code>input_safe_t</code> can never
|
|
fail. Hence, no <code>try/catch</code> block is necessary. The usage
|
|
of the <link
|
|
linkend="safe_numerics.exception_policies.loose_trap_policy"><code>loose_trap_policy</code></link>
|
|
policy for <code>safe_t</code> types guarantees this to be true at
|
|
compile time.</para>
|
|
</listitem>
|
|
</itemizedlist>Here is the output from the program when values 12 and 32
|
|
are input from the console:</para>
|
|
|
|
<screen>example 84:
|
|
type in values in format x y:33 45
|
|
x<signed char>[-24,82] = 33
|
|
y<signed char>[-24,82] = 45
|
|
z = <short>[-48,164] = 78
|
|
(x + y) = <short>[-48,164] = 78
|
|
(x - y) = <signed char>[-106,106] = -12
|
|
<short>[-48,164] = 78
|
|
</screen>
|
|
</section>
|
|
</section>
|