diff --git a/CMake/CMakeLists.txt b/CMake/CMakeLists.txt index bcf6d90..ee218c3 100644 --- a/CMake/CMakeLists.txt +++ b/CMake/CMakeLists.txt @@ -197,7 +197,7 @@ foreach(file_path ${example_list}) add_test(NAME ${base_name} COMMAND ${base_name}) endforeach(file_path) -set_target_properties(example81 example84 PROPERTIES +set_target_properties(example81 example9 PROPERTIES EXCLUDE_FROM_ALL TRUE EXCLUDE_FROM_DEFAULT_BUILD TRUE ) diff --git a/doc/boostbook/HTML.manifest b/doc/boostbook/HTML.manifest index 6743129..5bcb3fa 100644 --- a/doc/boostbook/HTML.manifest +++ b/doc/boostbook/HTML.manifest @@ -4,10 +4,14 @@ ../html/tutorial/1.html ../html/tutorial/2.html ../html/tutorial/4.html +../html/tutorial/10.html ../html/tutorial/5.html ../html/tutorial/6.html ../html/tutorial/7.html -../html/tutorial/8.html +../html/eliminate_runtime_penalty.html +../html/eliminate_runtime_penalty/1.html +../html/eliminate_runtime_penalty/2.html +../html/eliminate_runtime_penalty/3.html ../html/notes.html ../html/concepts.html ../html/numeric.html @@ -17,15 +21,17 @@ ../html/exception_policy.html ../html/types.html ../html/safe.html -../html/safe_unsigned_range.html +../html/safe_range.html +../html/safe_literal.html ../html/promotion_policies.html ../html/exception_policies.html ../html/exception_safety.html ../html/library_implementation.html +../html/exception_type.html ../html/checked_result.html ../html/checked_integer_arithmetic.html -../html/interval_arithmetic.html -../html/index.html +../html/interval.html +../html/performance_tests.html ../html/rationale.html ../html/pending_issues.html ../html/change_log.html diff --git a/doc/boostbook/checked.xml b/doc/boostbook/checked.xml index ff67525..aa15705 100644 --- a/doc/boostbook/checked.xml +++ b/doc/boostbook/checked.xml @@ -1,7 +1,7 @@ -
+
Checked Integer Arithmetic
diff --git a/doc/boostbook/eliminate_runtime_penalty.xml b/doc/boostbook/eliminate_runtime_penalty.xml index b8c181d..721753e 100644 --- a/doc/boostbook/eliminate_runtime_penalty.xml +++ b/doc/boostbook/eliminate_runtime_penalty.xml @@ -1,32 +1,35 @@ -
+
Eliminating Runtime Penalty - Up until now, we've focused on detecting when this happens and - handling either by throwing an exception or invoking some designated - function. We've achieved our goal of enforcing arithmetically correct - behavior - but at what cost. For many C++ program any any runtime penalty is - unacceptable. Whether or not one agrees with this, its a fact that many C++ - programmers feel this way. So the question arises as to how we alter our - program to eliminate any runtime penalty. + Up until now, we've focused on detecting when incorrect results are + produced and handling these occurances either by throwing an exception or + invoking some designated function. We've achieved our goal of enforcing + arithmetically correct behavior - but at what cost. For many C++ programers + any runtime penalty is unacceptable. Whether or not one agrees with this + tradeoff, its a fact that many C++ programmers feel this way. So the + question arises as to how we alter our program to minimize or eliminate any + runtime penalty. - The first step is to determine what parts might invoke exceptions. The - following program is similar to previous examples but uses a special - exception policy trap_exception. + 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: trap_exception. Now, - any expression which might fail at runtime is flagged with a compile time - error. There is no longer any need for try/catch blocks. Since - this program does not compile, the library absolutely - guarantees that no arithmetic expression - will yield incorrect results . This is our original goal. Now all - we need to do is make the program work. There are a couple of ways to do - this. + any expression which MIGHT fail at runtime is flagged with a + compile time error. There is no longer any need for try/catch + blocks. Since this program does not compile, the library absolutely guarantees that no + arithmetic expression will yield incorrect results . + This is our original goal. Now all we need to do is make the program work. + There are a couple of ways to do this. -
+
Using Automatic Type Promotion The C++ standard describes how binary operations on different @@ -53,7 +56,7 @@ integer type. If the values are large, they will exceed the size that the resulting integer type can hold. This is what we call "overflow". Standard C/C++ does just truncates the result to fit into the result type - which - sometimes will make the result arithmetically incorrect. + sometimes will make the result arithmetically incorrect. The complete signature for a safe integer type is: @@ -91,29 +94,49 @@ safe; - From this interval we can determine a new safe type which can - be guaranteed to hold the result. This turns out to be a safe_range - type. + 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: + + int x, y; +int z = x + y // could overflow + +int x, y; +long z = (long)x + (long)y; // can never overflow + + One could do this by editing his code manually, but such a + task would be tedious, error prone, and leave the resulting code + hard to read and verify. Using the "automatic" + type promotion policy will achieve the equivalent result + without these problems Since the result type is guaranteed to hold the result, there - is no need to check for errors - they can't happen !!! + is no need to check for errors - they can't happen !!! The usage of + "trap_exception" + exception policy enforces this guarentee + + + + Since there can be no errors, there is no need for try/catuch + blocks. The only runtime error checking we need to do is when safe values are initialized or assigned using smaller types. These are - infrequent occurrences. And many times, one can make small - adjustments in selecting the types in order to eliminate all runtime - penalties. This is what has been done in the above example. + infrequent occurrences which generally have little or no impact on + program running time. And many times, one can make small adjustments + in selecting the types in order to eliminate all runtime + penalties. - In short, given a binary operation, we promote the constituent types - to a larger result type which can't overflow. This is a fundamental - departure from the C++ Standard behavior. + In short, given a binary operation, we promote the types of the + operands to a larger so that the result type cannot overflow. This is a + fundamental departure from the C++ Standard behavior. If the interval of the result cannot be contained in the largest type that the machine can handle (usually 64 bits these days), the largest @@ -121,10 +144,13 @@ safe; our "automatic" type promotion scheme, it's still possible to overflow. In this case, and only this case, is runtime error checking code generated. Depending on the application, it should be rare to generate error checking - code, and even more rare to actually invoke it. + code, and even more rare to actually invoke it. Any such instances are + detected by the "trap_exception" exception + policy . - This small example illustrates how to use type promotion and how it - works. + This small example illustrates how to use automatic type promotion + to eliminate all runtime penalty. @@ -132,10 +158,10 @@ safe; The above program produces the following output: example 82: -x<int>[-2147483648,2147483647] = 2147483647 -y<int>[-2147483648,2147483647] = 2 -z(x + y)<long>[-4294967296,4294967294] = 2147483649 -(x - y)<long>[-4294967295,4294967295] = 2147483645 +x = <int>[-2147483648,2147483647] = 2147483647 +y = <int>[-2147483648,2147483647] = 2 +z = (x + y) = <long>[-4294967296,4294967294] = 2147483649 +(x - y) = <long>[-4294967295,4294967295] = 2147483645 <long>[-4294967296,4294967294] = 2147483649 @@ -144,7 +170,8 @@ z(x + y)<long>[-4294967296,4294967294] = 2147483649 that: automatic type promotion policy has rendered the result of the - some of two + some of two integers as a long + type. @@ -164,18 +191,20 @@ z(x + y)<long>[-4294967296,4294967294] = 2147483649
-
+
Using <link linkend="safe_numerics.safe_range">safe_range</link> - instead of relying on automatic type promotion, you can just create - your own types in such a way that you known they won't overflow. Here we - presume we happen to know that the values we want to work with fall in the - closed range of -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 replaced with other ones, we'll still you the - trap_exception to verify at compile time that what we "know" - to be true is in fact true. + Instead of relying on automatic type promotion, we can just create + our own types in such a way that we known they won't overflow. In the + example below, we presume we happen to know that the values we want to + work with fall in the closed range of -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 replaced with other + ones, we'll still use the "trap_exception" exception + policy to verify at compile time that what we "know" to be + true is in fact true. @@ -183,30 +212,41 @@ z(x + y)<long>[-4294967296,4294967294] = 2147483649 which produces the following output. example 83: -x<signed char>[-24,82] = 1 -y<signed char>[-24,82] = 2 -z(x + y)<int>[-2147483648,2147483647] = 3 -(x - y)<int>[-2147483648,2147483647] = -1 -<int>[-2147483648,2147483647] = 3 - +x = <signed char>[-24,82] = 2 +y = <signed char>[-24,82] = 2 +z = (x + y) = <int>[-48,164] = 4 +(x - y) = <int>[-106,106] = 0 +<int>[-48,164] = 4 - Note that we use a special object - safe_literal to - initialize safe types. safe_literal is a special safe type which wraps a - constant defined at compile time. Without this, the compiler will emit - code to check the value initializing variables x and y and our - trap_exception compile time error would be invoked. It cannot be assigned - to, or changed. Subject to this restriction, it can participate in safe - arithmetic operations. + + + In this example, standard C++ type promotion rules are used. + These promote operands to int before invoking the addition operation. + So addition operation itself won't overflow. The result of addition is + another unnamed safe type guaranteed to be able to hold the some of + any pair of safe types. In this example the result is a safe type + based on the C++ built-in type of short. + - Variables x and y are stored as 8 bit signed integers with range - specified as -24 to 82. The result of x + y could be any value in the - range -48 to 164. Since this result can't be stored in an 8 bit signed - integer, a 16 bit signed integer is allocated. The result x - y could - range from -106 to 106 so will fit in an 8 bit signed integer is - allocated. Binary operations with safe numeric using automatic type - promotion will produce other safe numeric types with template parameters - appropriate to hold the result. The resultant safe types may have smaller - or larger ranges than the parameters of the binary operation. + + So when we try to assign the result to z we could get an error. + This is because our custom safe_t cannot be guaranteed to + hold the some of all possible pairs of safe_t instances. + We fix the by using an "auto" for the sum. + + + + We now have a problem when we try to initialize our + safe_t variable with an initial literal value. This + operation could overflow at runtime. To our disappointment, our + attempt to fix the problem by using constexpr fails. The + fix for this is to use safe_literal to initialize safe + types. safe_literal is a special safe type which wraps a constant + defined at compile time. It cannot be assigned to, or changed. Subject + to this restriction, it can participate in safe arithmetic + operations. + + We've used simple expressions in this illustration. But since binary operations on safe types result in other safe types, expressions can be @@ -216,12 +256,65 @@ z(x + y)<int>[-2147483648,2147483647] = 3 regardless of how elaborate the expression might be.
-
- Miscellaneous +
+ Mixing Approaches For purposes of exposition, we've divided the discussion of how to eliminate runtime penalties by the different approaches available. A - realistic program would likely include all techniques mentioned - above. + realistic program would likely include all techniques mentioned above. + Consider the following: + + + + + + As before we define a safe_t to reflect our view of legal + values for this program. This uses automatic type policy as well as + trapping exception policy to enforce elimination of runtime + penalties. + + + + In addition we define input_safe_t 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 and assigned to + safe_t variables x and y, they are checked for legal values. We need + at hoc code to do this, as these types are guaranteed to contain + legal values and will throw an exception when this guarentee is + violated. In other words, we automatically get checking of input + variables with no additional programming. + + + + On calling of the function f, the variables of type + input_safe_t are converted to variables of safe_t. This conversion + invokes any needed checking. In this case there is none necessary. + If checker were necessary, the usage of the trap_exception policy + for safe_t types would cause a compile time error. + + + + The function f accepts only safe_t types so we have no need to + check the input types. This performs the functionality of + programming by contract with no runtime cost. + Due to our usage of trap_exception as a policy in safe_t, any + violation in our "contract" will result in a runtime error. + + Here is the output from the program when values 12 and 32 + are input from the console: + + example 83: +12 32 +x<signed char>[-24,82] = 12 +y<signed char>[-24,82] = 32 +zz = <short>[-48,164] = 44 +(x + y) = <short>[-48,164] = 44 +(x - y) = <signed char>[-106,106] = -20 +<short>[-48,164] = 44
diff --git a/doc/boostbook/exception_type.xml b/doc/boostbook/exception_type.xml index c595e61..4d2bf9e 100644 --- a/doc/boostbook/exception_type.xml +++ b/doc/boostbook/exception_type.xml @@ -1,7 +1,7 @@ -
+
exception_type
diff --git a/doc/boostbook/ignore_exception.xml b/doc/boostbook/ignore_exception.xml index a1302be..e9c2dc9 100644 --- a/doc/boostbook/ignore_exception.xml +++ b/doc/boostbook/ignore_exception.xml @@ -1,8 +1,8 @@ -
- ignore_exception +
+ ignore_exception
Description diff --git a/doc/boostbook/interval.xml b/doc/boostbook/interval.xml index 5c4de2c..4e86f1c 100644 --- a/doc/boostbook/interval.xml +++ b/doc/boostbook/interval.xml @@ -1,7 +1,7 @@ -
+
interval<typename R>
@@ -21,14 +21,9 @@
Notation - [The next two sections, associated types and valid expressions, - present expressions involving types that model the concept being defined. - This section defines the meaning of the variables and identifiers used in - those expressions.] - - + @@ -48,7 +43,7 @@ - i,t, v + i, j An interval @@ -68,7 +63,7 @@ C - checked_result<R> + checked_result<interval<R>> @@ -81,6 +76,28 @@
+
+ Associated Types + + + + + + + + + + checked_result + + holds either the result of an operation or information as + to why it failed + + + + +
+
Valid Expressions @@ -88,11 +105,11 @@ - + - + @@ -129,16 +146,24 @@ copy constructor + + i.includes(j) + + bool + + return true if interval i includes interval j + + i.includes(t) bool - return true if interval i includes interval t + return true if interval i includes value t - add<R>(t, v) + add<R>(i, j) C @@ -146,7 +171,7 @@ - subtract<R>(t, v) + subtract<R>(i, j) C @@ -154,7 +179,7 @@ - multiply<R>(t, v) + multiply<R>(i, j) C @@ -162,7 +187,7 @@ - divide_nz<R>(t, v) + divide_nz<R>(i, j) C @@ -171,7 +196,7 @@ - divide<R>(t, v) + divide<R>(i, j) C @@ -180,7 +205,7 @@ - modulus_nz<R>(t, v) + modulus_nz<R>(i, j) C @@ -189,7 +214,7 @@ - modulus<R>(t, v) + modulus<R>(i, j) C @@ -198,21 +223,21 @@ - left_shift<R>(t, v) + left_shift<R>(i, j) C - calculate modulus of one interval by another and return - the result + calculate the range that would result from shifting one + interval by another - right_shift<R>(t, v) + right_shift<R>(i, j) C - calculate modulus of one interval by another and return - the result + calculate the range that would result from shifting one + interval by another @@ -233,22 +258,6 @@ in u - - t == u - - bool - - true if limits are equal - - - - t != u - - bool - - true if limits are not equal - - t <= u @@ -268,12 +277,19 @@ - t < u + t == u - boost::logic::tribool + bool - calculate modulus of one interval by another and return - the result + true if limits are equal + + + + t != u + + bool + + true if limits are not equal @@ -297,28 +313,21 @@
Example of use - [A code fragment involving the type.] + #include <iostream> +#include <cstdint> +#include <cassert> +#include <boost/numeric/interval.hpp> - #include <vector> - -vector<int> V; -V.insert(V.begin(), 3); -assert(V.size() == 1 && V.capacity() >= 1 && V[0] == 3); - -
- -
- Notes - - [Footnotes (if any) that are referred to by other parts of the - page.] -
- -
- See Also - - [links to any other related concepts or types] - - +int main(){ + std::cout << "test1" << std::endl; + interval<std::int16_t> x = {-64, 63}; + std::cout << "x = " << x << std::endl; + interval<std::int16_t> y(-128, 126); + std::cout << "y = " << y << std::endl; + assert(static_cast<interval<std::int16_t>>(add<std::int16_t>(x,x)) == y); + std::cout << "x + x =" << add<std::int16_t>(x, x) << std::endl; + std::cout << "x - x = " << subtract<std::int16_t>(x, x) << std::endl; + return 0; +}
diff --git a/doc/boostbook/no_exception_support.xml b/doc/boostbook/no_exception_support.xml index cd5c5a5..0fab016 100644 --- a/doc/boostbook/no_exception_support.xml +++ b/doc/boostbook/no_exception_support.xml @@ -1,7 +1,7 @@ -
+
no_exception_support<O, U = O, R =O, D = O>
diff --git a/doc/boostbook/promotion_policy_concept.xml b/doc/boostbook/promotion_policy_concept.xml index d1bab56..9e4781a 100644 --- a/doc/boostbook/promotion_policy_concept.xml +++ b/doc/boostbook/promotion_policy_concept.xml @@ -51,27 +51,6 @@ auto z = x + ythe type of z will be an
-
- Associated Types - - - - - - - - - - EP - - A type that full fills the requirements of an ExceptionPollicy - - - - -
-
Valid Expressions @@ -92,50 +71,53 @@ auto z = x + ythe type of z will be an - PP::addition_result<T, U, PP, - EP>::type + PP::addition_result<T, + U>::type unspecified Numeric type - PP::subtraction_result<T, U, PP, - EP>::type + PP::subtraction_result<T, + U>::type unspecified Numeric type - PP::multiplication_result<T, U, PP, - EP>::type + PP::multiplication_result<T, + U>::type unspecified Numeric type - PP::division_result<T, U, PP, - EP>::type + PP::division_result<T, + U>::type unspecified Numeric type - PP::modulus_result<T, U, PP, - EP>::type + PP::modulus_result<T, U>::type unspecified Numeric type - PP::left_shift_result<T, U, PP, - EP>::type + PP::left_shift_result<T>::type unspecified Numeric type - PP::right_shift_result<T, U, PP, - EP>::type + PP::right_shift_result<T>::type + + unspecified Numeric type + + + + PP::bitwise_result<T>::type unspecified Numeric type diff --git a/doc/boostbook/safe_numeric_concept.xml b/doc/boostbook/safe_numeric_concept.xml index 18faa5b..7707c0a 100644 --- a/doc/boostbook/safe_numeric_concept.xml +++ b/doc/boostbook/safe_numeric_concept.xml @@ -52,7 +52,7 @@ - S + S, S1, S2 A type fulfilling SafeNumeric type requirements @@ -114,12 +114,12 @@ - s op u + s op t unspecified S invoke safe C++ operator op and return another - SafeNumeric type + SafeNumeric type. @@ -128,7 +128,7 @@ unspecified S invoke safe C++ operator op and return another - SafeNumeric type + SafeNumeric type. @@ -137,16 +137,7 @@ unspecified S invoke safe C++ operator op and return another - SafeNumeric type - - - - s1 assign_op s2 - - unspecified S - - invoke safe C++ operator op and return another - SafeNumeric type + SafeNumeric type. @@ -155,7 +146,7 @@ unspecified S invoke safe C++ operator op and return another - SafeNumeric type + SafeNumeric type. @@ -164,7 +155,17 @@ unspecified S invoke safe C++ operator op and return another - SafeNumeric type + SafeNumeric type. + + + + s assign_op t + + S1 + + convert t to type S1 and assign it to s1. If the + value t cannot be represented as an instance of type S1, it is + an error. @@ -172,8 +173,9 @@ unspecified S - construct a instance of S from a value - t + construct a instance of S from a value of type T. f + the value t cannot be represented as an instance of type S1, it + is an error. @@ -182,13 +184,14 @@ S construct a uninitialized instance of - S + S. is_safe<S> - std::true_type or std::false_type + std::true_type or + std::false_type type trait to query whether any type T fulfills the requirements for a SafeNumeric type. @@ -207,12 +210,22 @@ - The key fact documented in the above table is that the result of any - binary operation where one or both of the operands is a SafeNumeric type - is also a SafeNumeric type. Also note that all the expressions in the - above table are constexpr expressions. So that if they are - invoked with constant values, the result will be available a compile - time. + + + Result of any binary operation where one or both of the operands + is a SafeNumeric type is also a SafeNumeric type. + + + + All the expressions in the above table are + constexpr expressions + + + + Binary expressions which are not assignments require that + promotion and exception policies be identical. + +
@@ -245,10 +258,12 @@
Models - boost::numeric::safe<T> + safe<T> - boost::numeric::safe_signed_range<-11, 11> + safe_signed_range<-11, 11> - boost::numeric::safe_unsigned_range<0, 11> + safe_unsigned_range<0, 11> + + safe_literal<4>
diff --git a/doc/boostbook/safe_numerics.xml b/doc/boostbook/safe_numerics.xml index 3b7ad3d..94df15b 100644 --- a/doc/boostbook/safe_numerics.xml +++ b/doc/boostbook/safe_numerics.xml @@ -105,7 +105,7 @@ xmlns:xi="http://www.w3.org/2001/XInclude"/>
-
+
Types + +
Promotion Policies @@ -133,14 +136,14 @@ + + - -
@@ -178,7 +181,7 @@ xmlns:xi="http://www.w3.org/2001/XInclude"/>
-
+
Performance Tests Our goal creating facilities which make it possible write programs @@ -323,17 +326,24 @@ strtoi etc. DO check for valid input and throw the exception std::out_of_range if the string cannot be converted to the specified integer type. In other words, - strtoi already contains the exact functionality that + strtoi already contains some of the functionality that safe<int> provides. Although care was taking to make the library portable, it's likely that at least some parts of the implementation - particularly - checked arithmetic - depend upon twos complement + checked arithmetic - depend upon two's complement representation of integers. Hence the library is probably not currently portable to other architectures. + + + Currently the library permits a safe<int> value to be + unitialized. This supports the goal of "drop-in" replacement of C++/C + built-in types with safe counter parts. On the other hand, this breaks + the "always valid" guarantee. +
diff --git a/doc/boostbook/throw_exception.xml b/doc/boostbook/throw_exception.xml index 9fb006c..0c6b64b 100644 --- a/doc/boostbook/throw_exception.xml +++ b/doc/boostbook/throw_exception.xml @@ -1,10 +1,10 @@ -
+
throw_exception -
+
Description This exception policy throws a an exception derived from diff --git a/doc/boostbook/trap_exception.xml b/doc/boostbook/trap_exception.xml index 20449b8..65e45e4 100644 --- a/doc/boostbook/trap_exception.xml +++ b/doc/boostbook/trap_exception.xml @@ -1,15 +1,17 @@ -
+
trap_exception
Description - This exception policy will trap at compile time any operation could - result in a runtime exception. It can be used in an evironment which can - tolerate neither arithmetic errors nor runtime overhead. + This exception policy will trap at compile time any operation + COULD result in a + runtime exception. It can be used in an environment which can tolerate + neither arithmetic errors nor runtime overhead. Usage of this policy will + almost always require altering one's program to avoid exceptions.
@@ -30,7 +32,7 @@
Example of use - The following + The following #include "../../include/safe_integer.hpp" #include "../../include/automatic.hpp" diff --git a/doc/boostbook/tutorial.xml b/doc/boostbook/tutorial.xml index 8c2e10b..c9280b5 100644 --- a/doc/boostbook/tutorial.xml +++ b/doc/boostbook/tutorial.xml @@ -5,7 +5,7 @@ Tutorial and Motivating Examples
- Arithmetic Operations Can Yield Incorrect Results. + Arithmetic Expressions Can Yield Incorrect Results. When some operation results in a result which exceeds the capacity of a data variable to hold it, the result is undefined. This is called @@ -29,7 +29,7 @@
- Undetected Overflow + Arithmetic Operations can Overflow Silently A variation of the above is when a value is incremented/decremented beyond it's domain. This is a common problem with for loops. @@ -76,6 +76,30 @@ xmlns:xi="http://www.w3.org/2001/XInclude"/>
+
+ Mixing Data Types Can Create Subtle Errors + + C++ contains signed and unsigned integer types. In spite of their + names, they function differently which often produces surprising results + for some operands. Program errors from this behavior can be exceedingly + difficult to find. This has lead to recommendations of various ad hoc + "rules" to avoid these problems. It's not always easy to apply these + "rules" to existing code without creating even more bugs. Here is a + typical example of this problem: + + Here + is the output of the above program:example 10: mixing types produces surprising results +Not using safe numerics +10000 +4294957296 +Using safe numerics +10000 +detected error:converted negative value to unsigned +This solution is simple, Just replace instances of the + intwith safe<int>. +
+
Array Index Value Can Exceed Array Limits diff --git a/doc/html/bibliography.html b/doc/html/bibliography.html index d0ba880..104588e 100644 --- a/doc/html/bibliography.html +++ b/doc/html/bibliography.html @@ -20,7 +20,7 @@

Bibliography

-

Lawerence Crowl. +

Lawrence Crowl. C++ Binary Fixed-Point Arithmetic @@ -31,7 +31,7 @@ . January 15, 2012. Crowl

-

Lawerence Crowl. Thorsten Ottosen. +

Lawrence Crowl. Thorsten Ottosen. Proposal to add Contract Programming to C++ @@ -42,7 +42,7 @@ . February 25, 2006. Crowl & Ottosen

-

Will Dietz. Peng Li. John Regehr. Vikram Adve. +

Will Dietz. Peng Li. John Regehr. Vikram Adve. Understanding Integer Overflow in C/C++ . @@ -52,7 +52,7 @@ . June 2012.

-

J. Daniel Garcia. +

J. Daniel Garcia. C++ language support for contract programming @@ -63,7 +63,7 @@ . December 23, 2014. Garcia

-

Omer Katz. +

Omer Katz. SafeInt code proposal @@ -74,7 +74,7 @@ . Katz

-

David LeBlanc. +

David LeBlanc. Integer Handling with the C++ SafeInt Class . @@ -82,14 +82,14 @@ . January 7, 2004. LeBlanc

-

David LeBlanc. +

David LeBlanc. SafeInt . CodePlex . Dec 3, 2014. LeBlanc

-

Jacques-Louis Lions. +

Jacques-Louis Lions. Ariane 501 Inquiry Board report . @@ -97,7 +97,7 @@ . July 19, 1996. Lions

-

Daniel Plakosh. +

Daniel Plakosh. Safe Integer Operations . @@ -112,7 +112,7 @@ . 2nd Edition. Addison-Wesley Professional. April 12, 2013. 978-0321822130. Seacord

-

Robert C. Seacord. +

Robert C. Seacord. INT30-C. Ensure that operations on unsigned integers do not wrap . @@ -121,7 +121,7 @@ . August 17, 2014. INT30-C

-

Robert C. Seacord. +

Robert C. Seacord. INT32-C. Ensure that operations on signed integers do not result in overflow @@ -131,7 +131,7 @@ . August 17, 2014. INT32-C

-

Forum Posts. +

Forum Posts. C++ Binary Fixed-Point Arithmetic diff --git a/doc/html/exception_policies.html b/doc/html/exception_policies.html index 9ec7884..cb8db31 100644 --- a/doc/html/exception_policies.html +++ b/doc/html/exception_policies.html @@ -21,36 +21,36 @@

Exception Policies

-throw_exception

+throw_exception
-Description
+Description

This exception policy throws a an exception derived from std::exception any time some operation would result in an incorrect result. This is the default exception handling policy.

-Model of
+Model of

ExceptionPolicy

-Header
+Header

#include <boost/safe_numerics/exception_policies.hpp>

-Example of use
+Example of use

This example is somewhat contrived as throw_exception is the default for safe types. Hence it usually is not necessarily to request it explicitly.

@@ -68,10 +68,51 @@ int main(){

-ignore_exception

+trap_exception
-Description
+Description
+

This exception policy will trap at compile time any operation + COULD result in a + runtime exception. It can be used in an environment which can tolerate + neither arithmetic errors nor runtime overhead. Usage of this policy will + almost always require altering one's program to avoid exceptions.

+ +
+
+Model of
+

ExceptionPolicy

+
+ +
+
+Example of use
+

The following

+
#include "../../include/safe_integer.hpp"
+#include "../../include/automatic.hpp"
+#include "../../include/exception_policies.hpp"
+
+int main(){
+    using namespace boost::numeric;
+    safe<char, automatic, trap_exception> x, y;
+    y = x * x; // compile error here !!!
+    auto z = x * x; // compile OK
+    return 0;
+}
+
+ +
+

+ignore_exception

+
+
+Description

This exception policy can be used to just ignore conditions which generate incorrect arithmetic results and continue processing. Programs using this policy along with the native promotion policy @@ -79,28 +120,28 @@ int main(){

-Model of
+Model of

ExceptionPolicy

-Header
+Header

#include <boost/safe_numerics/exception_policy.hpp>

-Example of use
+Example of use
safe<int, native, ignore_exception> st(4);

-no_exception_support<O, U = O, R =O, D = O>

+no_exception_support<O, U = O, R =O, D = O>
-Description
+Description

This exception policy can be used in an environment where one cannot or does not want to use exceptions.

Parameters are pointers to static functions which are invoked for @@ -111,7 +152,7 @@ int main(){

-Template Parameters
+Template Parameters

Function objects to be invoked are specified for each error condition are specified via template parameters.

@@ -130,7 +171,7 @@ int main(){ - + @@ -157,19 +198,19 @@ int main(){
-Model of
+Model of

ExceptionPolicy

-Header
+Header

#include <boost/safe_numerics/exception_policy.hpp>

-Example of use
+Example of use

[A code fragment involving the type.]

void overflow(const char * msg);
 void underflow(const char * msg);
@@ -186,45 +227,6 @@ using ep = ignore_exception<
 safe<int, native, ep> st(4);
-
-

-trap_exception

-
-
-Description
-

This exception policy will trap at compile time any operation could - result in a runtime exception. It can be used in an evironment which can - tolerate neither arithmetic errors nor runtime overhead.

-
-
-
-Model of
-

ExceptionPolicy

-
- -
-
-Example of use
-

The following

-
#include "../../include/safe_integer.hpp"
-#include "../../include/automatic.hpp"
-#include "../../include/exception_policies.hpp"
-
-int main(){
-    using namespace boost::numeric;
-    safe<char, automatic, trap_exception> x, y;
-    y = x * x; // compile error here !!!
-    auto z = x * x; // compile OK
-    return 0;
-}
-
-
O  void (*O)(const char *)

Function to call on overflow error

diff --git a/doc/html/exception_policy.html b/doc/html/exception_policy.html index 7d2a09f..b1ff1ec 100644 --- a/doc/html/exception_policy.html +++ b/doc/html/exception_policy.html @@ -21,15 +21,15 @@

ExceptionPolicy<EP>

-Description

+Description

The exception policy specifies what is to occur when a safe operation cannot return a valid arithmetic result. A type is an ExceptionPolicy if it has functions for handling exceptional events that @@ -37,7 +37,7 @@

-Notation

+Notation
@@ -59,7 +59,7 @@

-Valid Expressions

+Valid Expressions

Any operations which result in integers which cannot be represented as some Numeric type will throw an exception.

@@ -92,14 +92,14 @@

-Header

+Header

#include <safe_numerics/include/concepts/exception_policy.hpp>

-Models

+Models

The library header <safe_numerics/include/exception_policies.hpp> contains a number of pre-made exception policies:

    diff --git a/doc/html/index.html b/doc/html/index.html index 6553e4c..5816cfd 100644 --- a/doc/html/index.html +++ b/doc/html/index.html @@ -22,7 +22,7 @@
@@ -33,20 +33,26 @@
Problem
Solution
-
Summary
-
Features
+
Implementation
+
Additional Features
Requirements
Scope
Tutorial and Motivating Examples
-
Arithmetic Operations Can Yield Incorrect Results.
-
Undetected Overflow
+
Arithmetic Expressions Can Yield Incorrect Results.
+
Arithmetic Operations can Overflow Silently
Implicit Conversions Change Data Values
+
Mixing Data Types Can Create Subtle Errors
Array Index Value Can Exceed Array Limits
Checking of Input Values Can Be Easily Overlooked
Programming by Contract is Too Slow
-
Eliminate Runtime Cost
+
+
Eliminating Runtime Penalty
+
+
Using Automatic Type Promotion
+
Using safe_range
+
Mixing Approaches
Notes
Type Requirements
@@ -61,19 +67,21 @@
safe<T, PP = boost::numeric::native, EP = boost::numeric::throw_exception>
-
safe_signed_range<MIN, MAX, PP, EP> and +
safe_signed_range<MIN, MAX, PP, EP> and safe_unsigned_range<MIN, MAX, PP, EP>
+
safe_literal<Value>, safe_unsigned_literal<Value>
Promotion Policies
Exception Policies
Exception Safety
Library Implementation
-
Checked_Result<R>
+
exception_type
+
checked_result<typename R>
Checked Integer Arithmetic
-
Interval Arithmetic
+
interval<typename R>
-
Performance Tests
+
Performance Tests
Rationale and FAQ
Pending Issues
Change Log
diff --git a/doc/html/integer.html b/doc/html/integer.html index b4263de..01769ed 100644 --- a/doc/html/integer.html +++ b/doc/html/integer.html @@ -21,15 +21,15 @@

Integer<T>

-Description

+Description

A type is fulls the requirements of an Integer if it has the properties of a integer.

More specifically, a type T is Integer if there exists @@ -46,13 +46,13 @@

-Refinement of

+Refinement of

Numeric

-Valid Expressions

-

In addition to the expressions defined defined in Numeric the following expressions +Valid Expressions

+

In addition to the expressions defined in Numeric the following expressions must be valid.

@@ -72,13 +72,13 @@

-Header

+Header

#include <safe_numerics/include/concepts/numeric.hpp>

-Models

+Models

int, safe<int>, safe_unsigned_range<0, 11>, etc.

diff --git a/doc/html/introduction.html b/doc/html/introduction.html index 17aad2a..cb4f08f 100644 --- a/doc/html/introduction.html +++ b/doc/html/introduction.html @@ -23,23 +23,23 @@

All data types, type requirements, function and meta function names - are found in the namespase boost::numeric . In order to make this document - more readable, we have omitted this namespace qualifier.

+ are found in the name space boost::numeric . In order to make this document + more readable, we have omitted this name space qualifier.

- + clutter.

[Note] Note

Library code in this document resides in the namespace - boost::numeric. This namespace has generally been +

Library code in this document resides in the name space + boost::numeric. This name space has generally been eliminated from text, code and examples in order to avoid - cludder.

@@ -61,7 +61,7 @@ behavior does not result in incorrect or unexpected operation of the program. There are no language facilities which do this. They have to be explicitly addressed in the program code. There are a number of ways to do - this. See[INT32-C] seems + this. See[INT32-C] seems to recommend the following approach.

int f(int x, int y){
   if (((y > 0) && (x > (INT_MAX - y))) 
@@ -89,7 +89,7 @@
 

This library implements special versions of int, unsigned, etc. which behave exactly like the original ones EXCEPT that the results of these operations are guaranteed to be either arithmetically correct or - invoke an error. Using this library, the above would be rendered + invoke an error. Using this library, the above example would be rendered as:

#include <boost/safe_numeric/safe_integer.hpp>
 
@@ -97,69 +97,89 @@ safe<int> f(safe<int> x, safe<int> y){
   return x + y; // throw exception if correct result cannot be returned
 }
 
-

The addition expression is checked at runtime or (if possible) or +

The addition expression is checked at runtime or (if possible) at compile time to trap any possible errors resulting from incorrect arithmetic behavior. This will permit one to write arithmetic expressions that cannot produce an erroneous result. Instead, one and only one of the following is guaranteed to occur.

    -
  • the expression will emit a compilation error.

  • -
  • the expression will invoke a runtime exception.

  • the expression will yield the correct mathematical result

  • +
  • the expression will emit a compilation error.

  • +
  • the expression will invoke a runtime exception.

-

In addition to eliminating undefined behavior from - primitive integer types, we define new data types - safe_signed_range<MIN, MAX> and - safe_unsigned_range<MIN, MAX> which will throw an - exception if an attempt is made to store a result which is outside the - closed range [MIN, MAX].

+

In other words, the library absolutely guarantees that no + arithmetic expression will yield incorrect results.

-Summary

-

Using techniques of C++ including overloading, template - metaprogramming, and others, this library implements special versions of - int, unsigned, etc. named safe<int>, +Implementation

+

All facilities modern C++ are employed to minimize runtime overhead + required to make this guarantee. In many cases there is no runtime + overhead at all. In other cases, small changes in the program are required + to eliminate the runtime overhead. The library implements special versions + of int, unsigned, etc. named safe<int>, safe<unsigned int> etc. These behave exactly like the - original ones EXCEPT that expressions involving these types are checked to - guarantee that any possible arithmetic errors are trapped at compile time - (if possible) or at runtime. Since these types are meant to be "drop-in" - replacements, they function in all other ways the same as the built-in - types they are meant to replace. So things which are legal - such as - assigning an signed to unsigned value are not trapped at compile time - as - they are legal C/C++ code - but rather checked at runtime to trap the case - where this (legal) operation would lead to an arithmetically incorrect - result.

+ original ones EXCEPT that expressions using these types fulfill the above + guarantee. These types are meant to be "drop-in" replacements for the + built-in types they are meant to replace. So things which are legal - such + as assigning an signed to unsigned value are not trapped at compile time - + as they are legal C/C++ code - but rather checked at runtime to trap the + case where this (legal) operation would lead to an arithmetically + incorrect result.

Note that the library addresses arithmetical errors generated by straightforward C/C++ expressions. Some of these arithmetic errors are defined as conforming to C/C++ standard while others are not. So characterizing this library as addressing undefined behavior of C/C++ - numeric expressions is misleading

+ numeric expressions is misleading.

-Features

+Additional Features

Operation of safe types is determined by template parameters which specify a pair of policy classes which specify the behavior for type promotion and error handling. In addition to the usage serving as a drop-in replacement for - standard integer types, The library can:

+ standard integer types, Users of the library can:

    -
  • Use custom type promotion so that expressions return types - guarenteed to hold the expression's results.

  • -
  • Ability to use custom integer sizes to implement cross - platform code testing

  • -
  • Trap some arithmetic errors at compile time

  • +
  • +

    Select or define an exception policy class to specify handling + of exceptions.

    +
      +
    • throw exception or runtime, trap at compile time.

    • +
    • trap at compiler time all operations which might fail at + runtime.

    • +
    • specify custom functions which should be called at + runtime

    • +
    +
  • +
  • +

    Select or define a promotion policy class to alter the C++ + type promotion rules. This can be used to

    +
      +
    • use C++ native type promotion rules so that, except + throwing/trapping of exceptions, programs will operate + identically when using/not using safe types.

    • +
    • replace C++ native promotion rules with ones which are + arithmetically equivalent but minimize the need for runtime + checking of arithmetic results.

    • +
    • replace C++ native promotion rules with ones which + emulate other machine architectures. This is designed to + permit the testing of C++ code destined to be run on another + machine on one's development platform. Such a situation often + occurs while developing code for embedded systems.

    • +
    +
  • Enforce of other program requirements using ranged integer - types.

  • -
  • Among others.

  • + types. The library includes types safe_..._range<Min, Max> and + safe_literal(...). These types can be used to improve program + correctness and performance.

Requirements

-

This library is composed entirely of C++ Headers. I requires a +

This library is composed entirely of C++ Headers. It requires a compiler compatible with the C++14 standard.

The following Boost Libraries must be installed in order to use this library

@@ -168,7 +188,11 @@ safe<int> f(safe<int> x, safe<int> y){
  • integer

  • config

  • concept checking

  • +
  • tribool

  • +
  • enable_if

  • +

    In order to run the test suite, the following the Boost + preprocessor library is also required.

    diff --git a/doc/html/notes.html b/doc/html/notes.html index 0f2723a..0dbc8e2 100644 --- a/doc/html/notes.html +++ b/doc/html/notes.html @@ -6,7 +6,7 @@ - + @@ -15,7 +15,7 @@

    Safe Numerics

    -PrevUpHomeNext +PrevUpHomeNext

    @@ -42,7 +42,7 @@ approximately 4400. It exploits the Boost Preprocessor Library to generate exhaustive tests.

    All concepts, types and functions documented are declared in the - namespace boost::numeric.

    + name space boost::numeric.

    @@ -52,7 +52,7 @@

    -PrevUpHomeNext +PrevUpHomeNext
    diff --git a/doc/html/numeric.html b/doc/html/numeric.html index 13bc08a..54cc296 100644 --- a/doc/html/numeric.html +++ b/doc/html/numeric.html @@ -21,16 +21,16 @@

    Numeric<T>

    -Description

    +Description

    A type is Numeric if it has the properties of a number.

    More specifically, a type T is Numeric if there exists specialization of std::numeric_limits<T>. See the @@ -45,7 +45,7 @@

    -Notation

    +Notation
    @@ -73,7 +73,7 @@

    -Associated Types

    +Associated Types
    @@ -90,13 +90,13 @@

    -Valid Expressions

    +Valid Expressions

    In addition to the expressions defined in Assignable the following expressions must be valid. Any operations which result in integers which cannot be represented as some Numeric type will throw an exception.

    -

    Table 1. General

    +

    Table 1. General

    @@ -130,7 +130,7 @@


    -

    Table 2. Unary Operators

    +

    Table 2. Unary Operators

    @@ -182,7 +182,7 @@

    -

    Table 3. Binary Operators

    +

    Table 3. Binary Operators

    @@ -339,13 +339,13 @@

    -Header

    +Header

    #include <safe_numerics/include/concepts/numeric.hpp>

    -Models

    +Models

    int, safe_signed_integer<int>, safe_signed_range<int>, etc.

    The definition of this concept

    diff --git a/doc/html/pending_issues.html b/doc/html/pending_issues.html index c92e4b2..476bb76 100644 --- a/doc/html/pending_issues.html +++ b/doc/html/pending_issues.html @@ -30,13 +30,17 @@ strtoi etc. DO check for valid input and throw the exception std::out_of_range if the string cannot be converted to the specified integer type. In other words, - strtoi already contains the exact functionality that + strtoi already contains some of the functionality that safe<int> provides.

  • Although care was taking to make the library portable, it's likely that at least some parts of the implementation - particularly - checked arithmetic - depend upon twos complement + checked arithmetic - depend upon two's complement representation of integers. Hence the library is probably not currently portable to other architectures.

  • +
  • Currently the library permits a safe<int> value to be + unitialized. This supports the goal of "drop-in" replacement of C++/C + built-in types with safe counter parts. On the other hand, this breaks + the "always valid" guarantee.

  • diff --git a/doc/html/promotion_policies.html b/doc/html/promotion_policies.html index 36e7d1e..26ff82e 100644 --- a/doc/html/promotion_policies.html +++ b/doc/html/promotion_policies.html @@ -6,7 +6,7 @@ - + @@ -15,7 +15,7 @@

    Safe Numerics

    -PrevUpHomeNext +PrevUpHomeNext

    @@ -30,7 +30,7 @@ native

    -Description
    +Description

    This type contains the functions to return a safe type corresponding to the C++ type resulting for a given arithmetic operation.

    Usage of this policy with safe types will produce the exact same @@ -41,7 +41,7 @@

    -Model of
    +Model of

    PromotionPolicy

    As an example of how this works consider C++ rules from section 5 of the standard - "usual arithmetic conversions".

    @@ -60,18 +60,18 @@ void int f(safe_int x, safe_int y){
    -Header
    +Header

    #include <boost/safe_numerics/include/native.hpp>

    -Example of use
    +Example of use

    The following example illustrates the native type being passed as a template parameter for the type safe<int>. This example is slightly contrived in that safe<int> - has native as it's default promotion parameter so explictiy + has native as it's default promotion parameter so explicitly using native is not necessary.

    #include <cassert>
     #include <boost/safe_numerics/safe_integer.hpp> 
    @@ -105,7 +105,7 @@ int main(){
     
     
    -Notes
    +Notes

    See Chapter 5, Expressions, C++ Standard

    @@ -114,24 +114,24 @@ int main(){ automatic
    -Description
    +Description

    This type contains the functions to return a type with sufficient capacity to hold the result of a given arithmetic operation.

    -Model of
    +Model of

    PromotionPolicy

    -Header
    +Header

    #include <boost/safe_numerics/automatic.hpp>

    -Example of use
    +Example of use

    The following example illustrates the automatic type being passed as a template parameter for the type safe<int>.

    @@ -157,22 +157,22 @@ int main(){ cpp<int C, int S, int I, int L, int LL>
    -Description
    +Description

    This policy is used to promote safe types in arithmetic expressions - according the the standard rules in the C++ standard. But rather than - using the native C++ standard types supported by the compiler, it uses - types whose length in number of bits is specified by the template + according the standard rules in the C++ standard. But rather than using + the native C++ standard types supported by the compiler, it uses types + whose length in number of bits is specified by the template parameters.

    This policy is useful for running test program which use C++ - portable integer types but which are destined to run on an an architecture + portable integer types but which are destined to run on an architecture which is different than the one on which the test program is being built and run. This can happen when developing code for embedded systems. Algorithms developed or borrowed from one architecture but destined for - another can be tested on ib the desk top.

    + another can be tested on the desk top.

    -Template Parameters
    +Template Parameters
    @@ -215,18 +215,18 @@ int main(){
    -Model of
    +Model of

    PromotionPolicy

    -Header
    +Header

    #include <boost/safe_numerics/cpp.hpp>

    -Example of Use
    +Example of Use

    Consider the following problem. One is developing software which uses a very small microprocessor and a very limited C compiler. The chip is so small, you can't print anything from the code, log, debug or @@ -235,8 +235,8 @@ int main(){ make changes and try again. This is a crude method which is usually the one used. But it can be quite time consuming.

    Consider an alternative. Build and compile your code in testable - modules. For each module write a test which excercises all the code and - makes it work. Finally download your code into the chip and - voila - + modules. For each module write a test which exercises all the code and + makes it work. Finally download your code into the chip and - voilà - working product. This sounds great, but there's one problem. Our target processor - in this case a PIC162550 from Microchip Technology is only an 8 bit CPU. The compiler we use defines INT as 8 bits. This (and a few @@ -303,8 +303,8 @@ uint16 get_stopping_distance(LEMPARAMETER velocity){ ...

    Note the usage of the compile time trap policy in order to detect at compile time any possible error conditions. As I write this, this is still - being refined. Hopefully this will be available by the time you read this. -

    + being refined. Hopefully this will be available by the time you read + this.

    @@ -316,7 +316,7 @@ uint16 get_stopping_distance(LEMPARAMETER velocity){

    -PrevUpHomeNext +PrevUpHomeNext
    diff --git a/doc/html/promotion_policy.html b/doc/html/promotion_policy.html index fd3941e..96af4a5 100644 --- a/doc/html/promotion_policy.html +++ b/doc/html/promotion_policy.html @@ -21,16 +21,15 @@

    PromotionPolicy<PP>

    -Description

    +Description

    In C++, arithmetic operations result in types which may or may not be the same as the constituent types. A promotion policy determines the type of the result of an arithmetic operation. For example, in the @@ -45,7 +44,7 @@ auto z = x + y

    -Notation

    +Notation
    @@ -70,22 +69,7 @@ auto z = x + y

    -Associated Types

    -
    ---- - - - - -
    EPA type that full fills the requirements of an ExceptionPollicy -
    - -
    -

    -Valid Expressions

    +Valid Expressions

    Any operations which result in integers which cannot be represented as some Numeric type will throw an exception.

    @@ -99,38 +83,39 @@ auto z = x + y - + - + - + - + - + - + - + + + + + @@ -138,14 +123,14 @@ auto z = x + y

    -Header

    +Header

    #include <safe_numerics/include/concepts/promotion_policy.hpp>

    -Models

    +Models

    The library contains a number of pre-made promotion policies:

    • @@ -182,11 +167,11 @@ safe_unsigned_range<2, 4> b; auto c = a + b; // c will be of type safe_unsigned_range<3, 8> and cannot overflow

      Type sz will be a SafeNumeric type which is guaranteed to hold he result of x + y. In this case that will - be a long int (or perhaps a long ong) depending upon the compiler and + be a long int (or perhaps a long long) depending upon the compiler and machine architecture. In this case, there will be no need for any special checking on the result and there can be no overflow.

      -

      Type of c will be a signed caracter as that type can be - guarenteed to hold the sum so no overflow checking is done.

      +

      Type of c will be a signed character as that type can be + guaranteed to hold the sum so no overflow checking is done.

      This policy is documented in Promotion Policies - automatic

    • diff --git a/doc/html/rationale.html b/doc/html/rationale.html index 6f09d73..27023eb 100644 --- a/doc/html/rationale.html +++ b/doc/html/rationale.html @@ -6,7 +6,7 @@ - + @@ -15,34 +15,34 @@
    PP::addition_result<T, U, PP, - EP>::typePP::addition_result<T, + U>::type unspecified Numeric type
    PP::subtraction_result<T, U, PP, - EP>::typePP::subtraction_result<T, + U>::type unspecified Numeric type
    PP::multiplication_result<T, U, PP, - EP>::typePP::multiplication_result<T, + U>::type unspecified Numeric type
    PP::division_result<T, U, PP, - EP>::typePP::division_result<T, + U>::type unspecified Numeric type
    PP::modulus_result<T, U, PP, - EP>::typePP::modulus_result<T, U>::type unspecified Numeric type
    PP::left_shift_result<T, U, PP, - EP>::typePP::left_shift_result<T>::type unspecified Numeric type
    PP::right_shift_result<T, U, PP, - EP>::typePP::right_shift_result<T>::typeunspecified Numeric type
    PP::bitwise_result<T>::type unspecified Numeric type

    Safe Numerics

    -PrevUpHomeNext +PrevUpHomeNext

    Rationale and FAQ

    -
    -
    1. Is this really necessary? If I'm writing the program with the +
    +
    1. Is this really necessary? If I'm writing the program with the requisite care and competence, problems noted in the introduction will never arise. Should they arise, they should be fixed "at the - source" and not with a "bandaid" to cover up bad practice. + source" and not with a "band aid" to cover up bad practice.
    -
    2. Why is Boost.Convert not used? +
    2. Why is Boost.Convert not used?
    -
    3. Why is the library named "safe ..." rather than something like +
    3. Why is the library named "safe ..." rather than something like "checked ..." ?
    -
    4. Given that the library is called "numerics" why is floating +
    4. Given that the library is called "numerics" why is floating point arithmetic not addressed?
    -
    5. Isn't putting a defensive check just before any potential +
    5. Isn't putting a defensive check just before any potential undefined behavior is often considered a bad practice?
    -
    6. It looks like the implementation presumes two's complement +
    6. It looks like the implementation presumes two's complement arithmetic at the hardware level. So this library is not portable - correct? What about other hardware architectures?
    -
    7. Why do you specialize numeric_limits for "safe" types? Do you +
    7. Why do you specialize numeric_limits for "safe" types? Do you need it?
    @@ -52,12 +52,12 @@ -

    1.

    +

    1.

    Is this really necessary? If I'm writing the program with the requisite care and competence, problems noted in the introduction will never arise. Should they arise, they should be fixed "at the - source" and not with a "bandaid" to cover up bad practice.

    + source" and not with a "band aid" to cover up bad practice.

    @@ -68,7 +68,7 @@ -

    2.

    +

    2.

    Why is Boost.Convert not used?

    @@ -79,7 +79,7 @@ -

    3.

    +

    3.

    Why is the library named "safe ..." rather than something like "checked ..." ?

    @@ -94,7 +94,7 @@ -

    4.

    +

    4.

    Given that the library is called "numerics" why is floating point arithmetic not addressed?

    @@ -111,7 +111,7 @@ -

    5.

    +

    5.

    Isn't putting a defensive check just before any potential undefined behavior is often considered a bad practice?

    @@ -126,7 +126,7 @@ -

    6.

    +

    6.

    It looks like the implementation presumes two's complement arithmetic at the hardware level. So this library is not portable - @@ -136,11 +136,11 @@

    As far as is known as of this writing, the library does not presume that the underlying hardware is two's compliment. However, - this has yet to be verified in a rigorus way.

    + this has yet to be verified in a rigorous way.

    -

    7.

    +

    7.

    Why do you specialize numeric_limits for "safe" types? Do you need it?

    @@ -164,7 +164,7 @@
    -PrevUpHomeNext +PrevUpHomeNext
    diff --git a/doc/html/safe.html b/doc/html/safe.html index 4b137a6..42cc23c 100644 --- a/doc/html/safe.html +++ b/doc/html/safe.html @@ -7,7 +7,7 @@ - + @@ -15,32 +15,32 @@

    Safe Numerics

    -PrevUpHomeNext +PrevUpHomeNext

    A safe<T, PP , EP> can be used anywhere a type T can be used. Any expression which uses this type is guaranteed to return an arithmetically correct value or trap in some way.

    -Notation

    +Notation
    @@ -59,7 +59,7 @@

    -Associated Types

    +Associated Types
    @@ -68,7 +68,7 @@ - @@ -81,7 +81,7 @@

    -Template Parameters

    +Template Parameters
    PPA type which specifes the result type of an expression +A type which specifies the result type of an expression using safe types.
    @@ -117,13 +117,13 @@

    -Model of

    +Model of

    Integer

    SafeNumeric

    -Valid Expressions

    +Valid Expressions

    Implements all expressions and only those expressions defined by the SafeNumeric type requirements. This, the result type of such an expression will be another @@ -132,13 +132,13 @@

    -Header

    +Header

    #include <boost/safe_numerics/safe_integer.hpp>

    -Examples of use

    +Examples of use

    The most common usage would be safe<T> which uses the default promotion and exception policies. This type is meant to be a "drop-in" replacement of the intrinsic integer types. That is, expressions involving @@ -149,7 +149,7 @@

    There are two aspects of the operation of this type which can be customized with a policy. The first is the result type of an arithmetic operation. C++ defines the rules which define this result type in terms of - the constintuent types of the operation. Here we refer to these rules a + the constituent types of the operation. Here we refer to these rules a "type promotion" rules. These rules will sometimes result in a type which cannot hold the actual arithmetic result of the operation. This is the main motivation for making this library in the first place. One way to @@ -157,7 +157,7 @@ the C++ ones.

    -Drop-in replacement for standard integer type.
    +Drop-in replacement for standard integer type.

    The following program will throw an exception and emit a error message at runtime if any of several events result in an incorrect arithmetic type. Behavior of this program could vary according to the @@ -182,7 +182,7 @@ void f(){

    -Guarantee correct behavior at compile time.
    +Guarantee correct behavior at compile time.

    In some instance catching an error at run time is not sufficient. We want to be sure that the program can never fail. To achieve this, use the trap_exception exception policy rather than the default throw @@ -206,16 +206,16 @@ void f(){

    -Adjust type promotion rules.
    +Adjust type promotion rules.

    Another way to avoid arithmetic errors like overflow is to promote types to larger sizes before doing the arithmetic. This can be justified - by the observ

    + by the observe

    Stepping back, we can see that many of the cases of invalid arithmetic are wouldn't exist if results types were larger. So we can avoid these problems by replacing the C++ type promotion rules for expressions with our own rules. This can be done by specifying a non-default type promotion policy automatic. The policy stores the - result of an expression in the smallest size that can accomodate the + result of an expression in the smallest size that can accommodate the largest value that an expression can yield. All the work is done at compile time - checking for exceptions necessary (input is of course an exception). The following example illustrates this.

    @@ -243,7 +243,7 @@ void f(){

    -PrevUpHomeNext +PrevUpHomeNext
    diff --git a/doc/html/safe_numeric_concept.html b/doc/html/safe_numeric_concept.html index 0ee29d6..8b889af 100644 --- a/doc/html/safe_numeric_concept.html +++ b/doc/html/safe_numeric_concept.html @@ -21,18 +21,18 @@

    SafeNumeric<T>

    -Description

    +Description

    This holds an arithmetic value which can be used as a replacement for built-in C++ arithmetic values. These types differ from their built-in counter parts in that the are guaranteed not to produce invalid arithmetic @@ -40,12 +40,12 @@

    -Refinement of

    +Refinement of

    Numeric

    -Notation

    +Notation
    @@ -66,7 +66,7 @@ - + @@ -94,7 +94,7 @@

    -Valid Expressions

    +Valid Expressions
    objects of types T, U
    SS, S1, S2 A type fulfilling SafeNumeric type requirements
    @@ -108,56 +108,61 @@ - + + SafeNumeric type.

    + SafeNumeric type.

    - - - - - + SafeNumeric type.

    + SafeNumeric type.

    + SafeNumeric type.

    + + + + + - + + S.

    - + @@ -170,23 +175,25 @@
    s op us op t unspecified S

    invoke safe C++ operator op and return another - SafeNumeric type

    t op s unspecified S

    invoke safe C++ operator op and return another - SafeNumeric type

    s1 op s2 unspecified S

    invoke safe C++ operator op and return another - SafeNumeric type

    s1 assign_op s2unspecified S

    invoke safe C++ operator op and return another - SafeNumeric type

    prefix_op S unspecified S

    invoke safe C++ operator op and return another - SafeNumeric type

    S postfix_op unspecified S

    invoke safe C++ operator op and return another - SafeNumeric type

    s assign_op tS1

    convert t to type S1 and assign it to s1. If the + value t cannot be represented as an instance of type S1, it is + an error.

    S(t) unspecified S

    construct a instance of S from a value - t

    construct a instance of S from a value of type T. f + the value t cannot be represented as an instance of type S1, it + is an error.

    S S

    construct a uninitialized instance of - S

    is_safe<S>std::true_type or std::false_type +std::true_type or + std::false_type +

    type trait to query whether any type T fulfills the requirements for a SafeNumeric type.

    -

    The key fact documented in the above table is that the result of any - binary operation where one or both of the operands is a SafeNumeric type - is also a SafeNumeric type. Also note that all the expressions in the - above table are constexpr expressions. So that if they are - invoked with constant values, the result will be available a compile - time.

    +
      +
    • Result of any binary operation where one or both of the operands + is a SafeNumeric type is also a SafeNumeric type.

    • +
    • All the expressions in the above table are + constexpr expressions

    • +
    • Binary expressions which are not assignments require that + promotion and exception policies be identical.

    • +

    -Complexity Guarantees

    +Complexity Guarantees

    There are no explicit complexity guarantees here. However, it would be very surprising if any implementation were to be more complex that O(0);

    -Invariants

    +Invariants

    The fundamental requirement of a SafeNumeric type is that implements all C++ operations permitted on it's base type in a way the prevents the return of an incorrect arithmetic result. Various implementations of this @@ -196,16 +203,17 @@

    -Header

    +Header

    #include <safe_numerics/include/concepts/safe_numeric.hpp>

    -Models

    -

    boost::numeric::safe<T>

    -

    boost::numeric::safe_signed_range<-11, 11>

    -

    boost::numeric::safe_unsigned_range<0, 11>

    +Models
    +

    safe<T>

    +

    safe_signed_range<-11, 11>

    +

    safe_unsigned_range<0, 11>

    +

    safe_literal<4>

    diff --git a/doc/html/tutorial.html b/doc/html/tutorial.html index f63a2ab..f2f60a7 100644 --- a/doc/html/tutorial.html +++ b/doc/html/tutorial.html @@ -7,7 +7,7 @@ - +
    @@ -21,13 +21,13 @@

    Tutorial and Motivating Examples

    diff --git a/doc/html/tutorial/1.html b/doc/html/tutorial/1.html index 4a98f47..e91ae49 100644 --- a/doc/html/tutorial/1.html +++ b/doc/html/tutorial/1.html @@ -1,13 +1,13 @@ -Arithmetic Operations Can Yield Incorrect Results. +Arithmetic Expressions Can Yield Incorrect Results. - +
    @@ -19,7 +19,7 @@

    -Arithmetic Operations Can Yield Incorrect Results.

    +Arithmetic Expressions Can Yield Incorrect Results.

    When some operation results in a result which exceeds the capacity of a data variable to hold it, the result is undefined. This is called "overflow". Since word size can differ between machines, code which @@ -34,6 +34,7 @@

    #include <cassert>
     #include <exception>
     #include <iostream>
    +#include <cstdint>
     
     #include "../include/safe_integer.hpp"
     
    @@ -43,27 +44,27 @@ int main(int argc, const char * argv[]){
         std::cout << "Not using safe numerics" << std::endl;
         // problem: arithmetic operations can yield incorrect results.
         try{
    -        char x = 127;
    -        char y = 2;
    -        char z;
    +        std::int8_t x = 127;
    +        std::int8_t y = 2;
    +        std::int8_t z;
             // this produces an invalid result !
             z = x + y;
             // but assert fails to detect it since C++ implicitly
             // converts variables to int before evaluating he expression!
    -        assert(z != x + y);
    +        // assert(z == x + y);
             std::cout << static_cast<int>(z) << " != " << x + y << std::endl;
             std::cout << "error NOT detected!" << std::endl;
         }
         catch(std::exception){
             std::cout << "error detected!" << std::endl;
         }
    -    // solution: replace char with safe<char>
    +    // solution: replace std::int8_t with safe<std::int8_t>
         std::cout << "Using safe numerics" << std::endl;
         try{
             using namespace boost::numeric;
    -        safe<char> x = 127;
    -        safe<char> y = 2;
    -        safe<char> z;
    +        safe<std::int8_t> x = 127;
    +        safe<std::int8_t> y = 2;
    +        safe<std::int8_t> z;
             // rather than producing and invalid result an exception is thrown
             z = x + y;
         }
    diff --git a/doc/html/tutorial/2.html b/doc/html/tutorial/2.html
    index e427ae9..a465059 100644
    --- a/doc/html/tutorial/2.html
    +++ b/doc/html/tutorial/2.html
    @@ -1,12 +1,12 @@
     
     
     
    -Undetected Overflow
    +Arithmetic Operations can Overflow Silently
     
     
     
     
    -
    +
     
     
     
    @@ -19,7 +19,7 @@
     
     

    -Undetected Overflow

    +Arithmetic Operations can Overflow Silently

    A variation of the above is when a value is incremented/decremented beyond it's domain. This is a common problem with for loops.

    #include <cassert>
    diff --git a/doc/html/tutorial/4.html b/doc/html/tutorial/4.html
    index 3724a92..a2b50ea 100644
    --- a/doc/html/tutorial/4.html
    +++ b/doc/html/tutorial/4.html
    @@ -6,8 +6,8 @@
     
     
     
    -
    -
    +
    +
     
     
     
    @@ -15,7 +15,7 @@

    Safe Numerics

    -PrevUpHomeNext +PrevUpHomeNext

    @@ -87,7 +87,7 @@ int main(int argc, const char * argv[]){
    -PrevUpHomeNext +PrevUpHomeNext
    diff --git a/doc/html/tutorial/5.html b/doc/html/tutorial/5.html index e6f9366..2450ad2 100644 --- a/doc/html/tutorial/5.html +++ b/doc/html/tutorial/5.html @@ -6,7 +6,7 @@ - + @@ -15,7 +15,7 @@

    Safe Numerics

    -PrevUpHomeNext +PrevUpHomeNext

    @@ -68,9 +68,9 @@ int main(int argc, const char * argv[]){ like standard arrays, vectors do array index checking in some function calls and not in others so this may not be the best example. However it does illustrate the usage of safe_range<T> for - assigning legal range to variables. This will guarentee that under no + assigning legal range to variables. This will guarantee that under no circumstances will the variable contain a value outside of the specified - range.

    + range.

    @@ -80,7 +80,7 @@ int main(int argc, const char * argv[]){

    -PrevUpHomeNext +PrevUpHomeNext
    diff --git a/doc/html/tutorial/7.html b/doc/html/tutorial/7.html index 63d5924..a3fe909 100644 --- a/doc/html/tutorial/7.html +++ b/doc/html/tutorial/7.html @@ -7,7 +7,7 @@ - + @@ -15,25 +15,25 @@

    Safe Numerics

    -PrevUpHomeNext +PrevUpHomeNext

    Programming by Contract is Too Slow

    Programming by Contract is a highly regarded technique. There has been much written about it has been proposed as an addition to the C++ - language [Garcia][Crowl & + language [Garcia][Crowl & Ottosen]. It (mostly) depends upon runtime checking of parameter and object values upon entry to and exit from every function. This can slow the program down considerably which in turn undermines the main motivation for using C++ in the first place! One popular scheme for addressing this issue is to enable parameter checking only during - debugging and testing which defeats the guarentee of correctness which we + debugging and testing which defeats the guarantee of correctness which we are seeking here! Programming by Contract will never be accepted by programmers as long as it is associated with significant additional runtime cost.

    The Safe Numerics Library has facilities which, in many cases, can - check guarentee parameter requirements with little or no runtime overhead. + check guarantee parameter requirements with little or no runtime overhead. Consider the following example:

    #include <cassert>
     #include <stdexcept>
    @@ -136,7 +136,7 @@ int main(int argc, const char * argv[]){
     
             // since safe types can be converted to their underlying unsafe types
             // we can still call an unsafe function with safe types
    -        convert(hours, minutes); // zero (almost) runtime overhead
    +        convert(hours, minutes); // zero (depending on compiler) runtime overhead
     
             // since unsafe types can be implicitly converted to corresponding
             // safe types we can just pass the unsafe types.  checkin will occur
    @@ -151,13 +151,13 @@ int main(int argc, const char * argv[]){
         return 0;
     }
     
    -

    In the example above the function convert incurrs significant - runtime cost every time the function is called. By using "safe" types, - this cost is moved to moment when the parameters are constructed. - Depending on how the program is constructed, this may totally eliminate - extraneous computions for parameter requirement type checking. In this - scenario, there is no reason to suppress the checking for release mode and - our program can be guarenteed to be always arithmetically correct.

    +

    In the example above the function convert incurs significant runtime + cost every time the function is called. By using "safe" types, this cost + is moved to moment when the parameters are constructed. Depending on how + the program is constructed, this may totally eliminate extraneous + computations for parameter requirement type checking. In this scenario, + there is no reason to suppress the checking for release mode and our + program can be guaranteed to be always arithmetically correct.

    @@ -167,7 +167,7 @@ int main(int argc, const char * argv[]){

    -PrevUpHomeNext +PrevUpHomeNext
    diff --git a/doc/html/tutorial/8.html b/doc/html/tutorial/8.html index 5fad77b..a36d768 100644 --- a/doc/html/tutorial/8.html +++ b/doc/html/tutorial/8.html @@ -1,13 +1,13 @@ -Eliminate Runtime Cost +Eliminating Runtime Penalty - + - + @@ -15,165 +15,64 @@

    Safe Numerics

    -PrevUpHomeNext +PrevUpHomeNext
    -

    -Eliminate Runtime Cost

    -

    Our system works by checking arithmetic operations whenever they - could result in an erroneous result. The C++ standard describes how binary - operations on different integer types are handled. Here is a simplified - version of the rules:

    -
      -
    • promote any operand smaller than int to an int or unsigned - int.

    • -
    • if the signed operand is larger than the signed one, the result - will be signed, otherwise the result will be unsigned.

    • -
    • expand the smaller operand to the size of the larger one

    • -
    -

    So the result of the sum of two integer types will result in another - integer type. If the values are large, they will exceed the size that the - resulting integer type can hold. This is what we call "overflow". Standard - C/C++ just truncates the result to fit into the result type - which makes - the result arithmetically incorrect. This behavior is consistent with the - default "native" type - promotion policy. Up until now, we've focused on detecting when - this happens and invoking an interrupt or other kind of error handler. -

    -

    But now we look at another option. Using the "automatic" type - promotion policy, we can change the rules of C++ arithmetic for - safe types to something like the following:

    -
      -
    • for any C++ numeric types, we know from - std::numeric::limits what the maximum and minimum - values that a variable can be - this defines a closed - interval.

    • -
    • For any binary operation on these types, we can calculate the - interval of the result at compile time.

    • -
    • From this interval we can determine a new safe type which can - be guarenteed to hold the result.

    • -
    • Since the result type is guarenteed to hold the result, there - is no need to check for errors - they can't happen !!!

    • -
    • The only error checking we need to do is when safe values are - initialized or assigned, but this we would have to do in any case. - So we've eliminated arithmetically incorrect results while incurring - zero runtime overhead for error checking.

    • -
    -

    In short, given a binary operation, we promote the constituent types - to a larger result type which can't overflow. This is a fundamental - deparature from the C++ Standard behavior.

    -

    If the interval of the result cannot be contained 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. In - this case, and only this case, is runtime error checking code generated. - Depending on the application, it should be rare to generate error checking - code, and even more rare to actually invoke it.

    -

    This small example illustrates how to use type promotion and how it - works.

    -
    #include <cassert>
    -#include <stdexcept>
    -#include <ostream>
    -#include <iostream>
    -#include <cxxabi.h>
    -#include <typeinfo>
    +

    +Eliminating Runtime Penalty

    + +

    Up until now, we've focused on detecting when incorrect results are + produced and handling these occurances either by throwing an exception or + invoking some designated function. We've achieved our goal of enforcing + arithmetically correct behavior - but at what cost. For many C++ programers + any runtime penalty is unacceptable. Whether or not one agrees with this + tradeoff, its a fact that many C++ programmers feel this way. So the + question arises as to how we alter our program to minimize or eliminate any + runtime penalty.

    +

    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: trap_exception.

    +
    #include <iostream>
     
    -#include "../include/safe_range.hpp"
    -#include "../include/automatic.hpp"
    +#include "../include/safe_integer.hpp"
    +#include "../include/exception.hpp" // include exception policies
    +#include "safe_format.hpp" // prints out range and value of any type
     
    -// create an output manipulator which prints variable type and limits
    -// as well as value
    -template<typename T>
    -struct formatted_impl {
    -    const T & m_t;
    -    formatted_impl(const T & t) :
    -        m_t(t)
    -    {}
    -    template <class charT, class Traits>
    -    friend std::basic_ostream<charT,Traits> &
    -    operator<<(
    -        std::basic_ostream<charT,Traits> & os,
    -        const formatted_impl<T> & f
    -    ){
    -        int status;
    -        return os
    -            << "<"
    -            << abi::__cxa_demangle(
    -                typeid(boost::numeric::base_value(m_t)).name(),0,0,&status
    -            )
    -            << ">["
    -            << std::numeric_limits<T>::min() << ","
    -            << std::numeric_limits<T>::max() << "] = "
    -            << f.m_t;
    -    }
    -};
    +using safe_t = boost::numeric::safe<
    +    int,
    +    boost::numeric::native,
    +    boost::numeric::trap_exception  // note use of "trap_exception" policy!
    +>;
     
    -template<typename T>
    -auto formatted(const T & t){
    -    return formatted_impl<T>(t);
    +safe_t f(const safe_t & x, const safe_t & y){
    +    // each statement below will fail to compile !
    +    safe_t z = x + y;
    +    std::cout << "(x + y)" << safe_format(x + y) << std::endl;
    +    std::cout << "(x - y)" << safe_format(x - y) << std::endl;
    +    return z;
     }
     
    -// create a type for holding small integers which implement automatic
    -// type promotion to larger types to guarentee correct results with
    -// zero runtime overhead !
    -template <
    -    std::intmax_t Min,
    -    std::intmax_t Max
    ->
    -using safe_t = boost::numeric::safe_signed_range<
    -    Min,
    -    Max,
    -    boost::numeric::automatic,
    -    boost::numeric::throw_exception
    ->;
    -using small_integer_t = safe_t<-24, 82>;
    -
     int main(int argc, const char * argv[]){
    -    // problem: checking of externally produced value can be overlooked
    -    std::cout << "example 8: ";
    -    std::cout << "eliminate runtime overhead" << std::endl;
    -
    -    try{
    -        const small_integer_t x(1);
    -        std::cout << "x" << formatted(x) << std::endl;
    -        small_integer_t y = 2;
    -        std::cout << "y" << formatted(y) << std::endl;
    -        auto z = x + y;  // zero runtime overhead !
    -        std::cout << "(x + y)" << formatted(z) << std::endl;
    -        std::cout << "(x - y)" << formatted(x - y) << std::endl;
    -    }
    -    catch(std::exception e){
    -        // none of the above should trap. Mark failure if they do
    -        std::cout << e.what() << std::endl;
    -        return false;
    -    }
    -
    +    std::cout << "example 81:\n";
    +    safe_t x(INT_MAX);  // will fail to compile
    +    safe_t y(2);        // will fail to compile
    +    std::cout << "x" << safe_format(x) << std::endl;
    +    std::cout << "y" << safe_format(y) << std::endl;
    +    std::cout << "z" << safe_format(f(x, y)) << std::endl;
         return 0;
     }
     
    -

    The - above program produces the following output:

    -
    example 8: eliminate runtime overhead
    -x<signed char>[-24,82] = 1
    -y<signed char>[-24,82] = 2
    -(x + y)<short>[-48,164] = 3
    -(x - y)<signed char>[-106,106] = -1
    -

    Variables x and y - are stored as 8 bit signed integers with range specied as -24 to 82. The - result of x + y could be any value in the range -48 to 164. Since this - result can't be stored in an 8 bit signed integer, a 16 bit signed integer - is allocated. The result x - y could range from -106 to 106 so will fit in - an 8 bit signed integer is allocated. Binary operations with safe numeric - using automatic type promotion will produce other safe numeric types with - template parameters appropriate to hold the result. The resultant safe - types may have smaller or larger ranges than the parameters of the binary - operation.

    -

    We've used simple expressions in this illustration. But since binary - operations on safe types result in other safe types, expressions can be - made arbitrarily elaborate - just as they can be with intrinsic integer - types. That is, safe integer types are drop in replacements for intrinsic - integer types. We are guarenteed never to produce an incorrect result - regardless of how elaborate the expression might be.

    +

    Now, + any expression which MIGHT fail at runtime is flagged with a + compile time error. There is no longer any need for try/catch + blocks. Since this program does not compile, the library absolutely guarantees that no + arithmetic expression will yield incorrect results . + This is our original goal. Now all we need to do is make the program work. + There are a couple of ways to do this.

    @@ -183,7 +82,7 @@ y<signed char>[-24,82] = 2

    -PrevUpHomeNext +PrevUpHomeNext
    diff --git a/doc/html/types.html b/doc/html/types.html index 2604b19..a38653e 100644 --- a/doc/html/types.html +++ b/doc/html/types.html @@ -23,8 +23,9 @@ diff --git a/examples/example10.cpp b/examples/example10.cpp index e5b85b9..82be003 100644 --- a/examples/example10.cpp +++ b/examples/example10.cpp @@ -1,30 +1,44 @@ -#include -#include -#include #include +#include #include "../include/safe_integer.hpp" #include "../include/cpp.hpp" -#include -#include - using namespace std; +using namespace boost::numeric; + +void f(const unsigned int & x, const int8_t & y){ + cout << x * y << endl; +} +void safe_f( + const safe & x, + const safe & y +){ + cout << x * y << endl; +} int main(){ - // problem: checking of externally produced value can be overlooked - std::cout << "example 8: "; - std::cout << "eliminate runtime overhead" << std::endl; - - int8_t x = 100; - int y = x * x; - cout << y << endl; - - uint32_t z1 = 100; - int8_t z2 = -100; - auto y2 = z1 * z2; - cout << y2 << endl; - + cout << "example 10: "; + cout << "mixing types produces surprising results" << endl; + try { + std::cout << "Not using safe numerics" << std::endl; + // problem: arithmetic operations can yield incorrect results. + f(100, 100); // works as expected + f(100, -100); // wrong result - unnoticed + } + catch(std::exception){ + // never arrive here + std::cout << "error detected!" << std::endl; + } + try { + // solution: use safe types + std::cout << "Using safe numerics" << std::endl; + safe_f(100, 100); // works as expected + safe_f(100, -100); // throw error + } + catch(const exception & e){ + cout << "detected error:" << e.what() << endl;; + } return 0; } diff --git a/examples/example82.cpp b/examples/example82.cpp index f5393f4..e41817d 100644 --- a/examples/example82.cpp +++ b/examples/example82.cpp @@ -12,18 +12,18 @@ using safe_t = boost::numeric::safe< auto f(const safe_t & x, const safe_t & y){ // note use of "auto" auto z = x + y; // note change to "auto" - std::cout << "(x + y)" << safe_format(x + y) << std::endl; - std::cout << "(x - y)" << safe_format(x - y) << std::endl; + std::cout << "(x + y) = " << safe_format(x + y) << std::endl; + std::cout << "(x - y) = " << safe_format(x - y) << std::endl; return z; } int main(int argc, const char * argv[]){ std::cout << "example 82:\n"; safe_t x(INT_MAX); - safe_t y(2); - std::cout << "x" << safe_format(x) << std::endl; - std::cout << "y" << safe_format(y) << std::endl; - std::cout << "z" << safe_format(f(x, y)) << std::endl; + safe_t y = 2; + std::cout << "x = " << safe_format(x) << std::endl; + std::cout << "y = " << safe_format(y) << std::endl; + std::cout << "z = " << safe_format(f(x, y)) << std::endl; return 0; } diff --git a/examples/example83.cpp b/examples/example83.cpp index b07db1b..3b23c72 100644 --- a/examples/example83.cpp +++ b/examples/example83.cpp @@ -12,28 +12,30 @@ using namespace boost::numeric; // for safe_literal // create a type for holding small integers. We "know" that C++ type // promotion rules will work such that operations on this type // will never overflow. If change the program to break this, the -// trap_exception will prevent compilation +// usage of the trap_exception promotion policy will prevent compilation. using safe_t = safe_signed_range< -24, 82, - native, // we don't need automatic in this case - trap_exception + native, // C++ type promotion rules work OK for this example + trap_exception // catch problems at compile time >; -int f(const safe_t & x, const safe_t & y){ - int z = x + y; // we know that this cannot fail - // std::int8_t z = x + y; // but this COULD fail. So we get a compile error - std::cout << "(x + y)" << safe_format(x + y) << std::endl; - std::cout << "(x - y)" << safe_format(x - y) << std::endl; +auto f(const safe_t & x, const safe_t & y){ + //safe_t z = x + y; // depending on values of x & y COULD fail + auto z = x + y; // due to C++ type promotion rules, + // we know that this cannot fail + std::cout << "(x + y) = " << safe_format(x + y) << std::endl; + std::cout << "(x - y) = " << safe_format(x - y) << std::endl; return z; } int main(int argc, const char * argv[]){ std::cout << "example 83:\n"; - safe_t x(safe_literal<1>{}); // note special type for initialization needed - safe_t y(safe_literal<2>{}); // to avoid runtime penalty - std::cout << "x" << safe_format(x) << std::endl; - std::cout << "y" << safe_format(y) << std::endl; - std::cout << "z" << safe_format(f(x, y)) << std::endl; + // constexpr const safe_t z = 3; // fails to compile + const safe_t x(safe_literal<2>{}); + const safe_t y = safe_literal<2>(); // to avoid runtime penalty + std::cout << "x = " << safe_format(x) << std::endl; + std::cout << "y = " << safe_format(y) << std::endl; + std::cout << "z = " << safe_format(f(x, y)) << std::endl; return 0; } diff --git a/examples/example84.cpp b/examples/example84.cpp index 8242122..fc5d9a1 100644 --- a/examples/example84.cpp +++ b/examples/example84.cpp @@ -28,8 +28,9 @@ using input_safe_t = safe_signed_range< // function arguments can never be outside of limits auto f(const safe_t & x, const safe_t & y){ auto z = x + y; // we know that this cannot fail - std::cout << "(x + y)" << safe_format(x + y) << std::endl; - std::cout << "(x - y)" << safe_format(x - y) << std::endl; + std::cout << "z = " << safe_format(z) << std::endl; + std::cout << "(x + y) = " << safe_format(x + y) << std::endl; + std::cout << "(x - y) = " << safe_format(x - y) << std::endl; return z; } diff --git a/include/automatic.hpp b/include/automatic.hpp index 952807a..3d8ef2e 100644 --- a/include/automatic.hpp +++ b/include/automatic.hpp @@ -22,10 +22,10 @@ #include // true_type, false_type, is_same #include +#include "utility.hpp" #include "safe_common.hpp" #include "checked_result.hpp" #include "interval.hpp" -#include "safe_range.hpp" namespace boost { namespace numeric { @@ -98,209 +98,173 @@ struct automatic { std::uintmax_t >::type >::type >::type; - template - struct defer_signed_lazily { - using type = boost::numeric::safe_signed_range; + template + struct defer_stored_signed_lazily { + using type = signed_stored_type; }; - template - struct defer_unsigned_lazily { - using type = boost::numeric::safe_unsigned_range; + template + struct defer_stored_unsigned_lazily { + using type = unsigned_stored_type; }; - template - using safe_range = + template + using result_type = typename boost::mpl::if_< std::is_signed, - defer_signed_lazily, - defer_unsigned_lazily + defer_stored_signed_lazily, + defer_stored_unsigned_lazily >::type; /////////////////////////////////////////////////////////////////////// - template + template struct addition_result { - typedef typename base_type::type base_type_t; - typedef typename base_type::type base_type_u; - constexpr static const interval t = { + using result_base_type = calculate_max_t; + using t_base_type = typename base_type::type; + using u_base_type = typename base_type::type; + + // filter out case were overflow cannot occur + // note: subtle trickery. Suppose t is safe_range. Then + // std::numeric_limits::min() will be safe_range t_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - constexpr static const interval u = { + constexpr static const interval u_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - typedef calculate_max_t max_t; + // when we add the temporary intervals above, we'll get a new interval + // with the correct range for the sum ! + constexpr static const checked_result> r_interval + = add(t_interval, u_interval); - constexpr static const checked_result> r - = add(t, u); - - constexpr static const interval< max_t> default_interval{}; - - constexpr static const interval result_interval = - r.no_exception() ? - static_cast>(r) + constexpr static const interval result_interval = + r_interval.no_exception() ? + static_cast>(r_interval) : - default_interval + interval{} ; - typedef typename safe_range< - max_t, + using type = typename result_type< + result_base_type, result_interval.l, - result_interval.u, - P, - E - >::type type; + result_interval.u + >::type; }; /////////////////////////////////////////////////////////////////////// - template + template struct subtraction_result { - typedef typename base_type::type base_type_t; - typedef typename base_type::type base_type_u; + using result_base_type = calculate_max_t; + using t_base_type = typename base_type::type; + using u_base_type = typename base_type::type; - constexpr static const interval t = { + constexpr static const interval t_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - constexpr static const interval u = { + constexpr static const interval u_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - typedef calculate_max_t max_t; + constexpr static const checked_result> r_interval + = subtract(t_interval, u_interval); - constexpr static const checked_result> r - = subtract(t, u); - - constexpr static const interval< max_t> default_interval{}; - - constexpr static const interval result_interval = - r.no_exception() ? - static_cast>(r) + constexpr static const interval result_interval = + r_interval.no_exception() ? + static_cast>(r_interval) : - default_interval + interval{} ; - typedef typename safe_range< - max_t, + using type = typename result_type< + result_base_type, result_interval.l, - result_interval.u, - P, - E - >::type type; + result_interval.u + >::type; }; /////////////////////////////////////////////////////////////////////// - template + template struct multiplication_result { - typedef typename base_type::type base_type_t; - typedef typename base_type::type base_type_u; + using result_base_type = calculate_max_t; + using t_base_type = typename base_type::type; + using u_base_type = typename base_type::type; - constexpr static const interval t = { + constexpr static const interval t_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - constexpr static const interval u = { + constexpr static const interval u_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - typedef calculate_max_t max_t; - // typedef print_type p_max_t; + constexpr static const checked_result> r_interval + = multiply(t_interval, u_interval); - constexpr static const checked_result> r - {multiply(t, u)}; - - constexpr static const interval default_interval{}; - - constexpr static const interval result_interval = - r.no_exception() ? - static_cast>(r) + constexpr static const interval result_interval = + r_interval.no_exception() ? + static_cast>(r_interval) : - default_interval + interval{} ; - typedef typename safe_range< - max_t, + using type = typename result_type< + result_base_type, result_interval.l, - result_interval.u, - P, - E - >::type type; + result_interval.u + >::type; }; /////////////////////////////////////////////////////////////////////// - template - constexpr static int bits(){ - // figure number of bits in quotient - return std::min( - std::numeric_limits::digits - + 1 // one guard bit to cover u == -1 & t = numeric_limits::min() - + 1 // one sign bit - , - std::numeric_limits::digits - + 1 // one sign bit - ); - } - - template + template struct division_result { - typedef typename base_type::type base_type_t; - static_assert( - std::is_literal_type< interval >::value, - "interval is not literal type" - ); - typedef typename base_type::type base_type_u; - static_assert( - std::is_literal_type< interval >::value, - "interval is not tliteral type" - ); + using t_base_type = typename base_type::type; + using u_base_type = typename base_type::type; - constexpr static interval t { - interval( - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) - ) - }; - constexpr static interval u { - interval( - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) - ) + constexpr static const interval t_interval{ + base_value(std::numeric_limits::min()), + base_value(std::numeric_limits::max()) }; - using base_type_r = typename boost::mpl::if_c< - std::numeric_limits::is_signed - || std::numeric_limits::is_signed, + constexpr static const interval u_interval{ + base_value(std::numeric_limits::min()), + base_value(std::numeric_limits::max()) + }; + + using result_base_type = typename boost::mpl::if_c< + std::numeric_limits::is_signed + || std::numeric_limits::is_signed, std::intmax_t, std::uintmax_t >::type; - constexpr static checked_result> r { - divide_nz(t, u) + constexpr static checked_result> r { + divide_nz(t_interval, u_interval) }; - constexpr static const interval default_interval{}; - - constexpr static const interval result_interval { + constexpr static const interval result_interval { r.no_exception() ? - static_cast>(r) + static_cast>(r) : - default_interval + interval{} }; - typedef typename safe_range< - base_type_r, + using type = typename result_type< + result_base_type, result_interval.l, - result_interval.u, - P, - E - >::type type; + result_interval.u + >::type; }; // forward to correct divide implementation @@ -314,50 +278,38 @@ struct automatic { } /////////////////////////////////////////////////////////////////////// - template + template struct modulus_result { - typedef typename base_type::type base_type_t; - static_assert( - std::is_literal_type< interval >::value, - "interval is not literal type" - ); - typedef typename base_type::type base_type_u; - static_assert( - std::is_literal_type< interval >::value, - "interval is not tliteral type" - ); + using t_base_type = typename base_type::type; + using u_base_type = typename base_type::type; - constexpr static const interval t { + constexpr static const interval t_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - constexpr static const interval u { + constexpr static const interval u_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - using base_type_r = std::make_unsigned_t; + using r_base_type = std::make_unsigned_t; - constexpr static const checked_result> r - { modulus_nz(t, u) }; + constexpr static const checked_result> r + { modulus_nz(t_interval, u_interval) }; - constexpr static const interval default_interval{}; - - constexpr static const interval result_interval = + constexpr static const interval result_interval = r.no_exception() ? - static_cast>(r) + static_cast>(r) : - default_interval + interval{} ; - typedef typename safe_range< - base_type_r, + using type = typename result_type< + r_base_type, result_interval.l, - result_interval.u, - P, - E - >::type type; + result_interval.u + >::type; }; // forward to correct modulus implementation @@ -371,15 +323,17 @@ struct automatic { } /////////////////////////////////////////////////////////////////////// - template + template struct left_shift_result { - typedef typename base_type::type t_base_type; - typedef typename base_type::type u_base_type; - constexpr static const interval t = { + using t_base_type = typename base_type::type; + using u_base_type = typename base_type::type; + + constexpr static const interval t_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - constexpr static const interval u = { + + constexpr static const interval u_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; @@ -387,36 +341,34 @@ struct automatic { typedef calculate_max_t max_t; constexpr static const checked_result> r - = left_shift(t, u); - - constexpr static const interval< max_t> default_interval{}; + = left_shift(t_interval, u_interval); constexpr static const interval result_interval = r.no_exception() ? static_cast>(r) : - default_interval + interval< max_t>{} ; - typedef typename safe_range< + using type = typename result_type< max_t, result_interval.l, - result_interval.u, - P, - E - >::type type; + result_interval.u + >::type; }; /////////////////////////////////////////////////////////////////////// - template + template struct right_shift_result { - typedef typename base_type::type t_base_type; - typedef typename base_type::type u_base_type; - constexpr static const interval t = { + using t_base_type = typename base_type::type; + using u_base_type = typename base_type::type; + + constexpr static const interval t_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - constexpr static const interval u = { + + constexpr static const interval u_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; @@ -424,24 +376,32 @@ struct automatic { typedef calculate_max_t max_t; constexpr static const checked_result> r - = right_shift(t, u); - - constexpr static const interval< max_t> default_interval{}; + = right_shift(t_interval, u_interval); constexpr static const interval result_interval = r.no_exception() ? static_cast>(r) : - default_interval + interval< max_t>{} ; - typedef typename safe_range< + using type = typename result_type< max_t, result_interval.l, - result_interval.u, - P, - E - >::type type; + result_interval.u + >::type; + }; + + /////////////////////////////////////////////////////////////////////// + template + struct bitwise_result { + using t_base_type = typename base_type::type; + using u_base_type = typename base_type::type; + using type = typename boost::mpl::if_c< + (sizeof(t_base_type) > sizeof(u_base_type)), + t_base_type, + u_base_type + >::type; }; }; diff --git a/include/concept/promotion_policy.hpp b/include/concept/promotion_policy.hpp index fdd983d..ce3e766 100644 --- a/include/concept/promotion_policy.hpp +++ b/include/concept/promotion_policy.hpp @@ -21,14 +21,14 @@ template struct PromotionPolicy { typedef int T; typedef int U; - typedef void E; - typedef typename PP::template addition_result a_type; - typedef typename PP::template subtraction_result s_type; - typedef typename PP::template multiplication_result m_type; - typedef typename PP::template division_result d_type; - typedef typename PP::template modulus_result mod_type; - typedef typename PP::template left_shift_result ls_type; - typedef typename PP::template right_shift_result rs_type; + typedef typename PP::template addition_result a_type; + typedef typename PP::template subtraction_result s_type; + typedef typename PP::template multiplication_result m_type; + typedef typename PP::template division_result d_type; + typedef typename PP::template modulus_result mod_type; + typedef typename PP::template left_shift_result ls_type; + typedef typename PP::template right_shift_result rs_type; + typedef typename PP::template bitwise_result bw_type; checked_result::type> test(){ return PP::template divide::type>(0, 0); diff --git a/include/cpp.hpp b/include/cpp.hpp index acb1463..fa480d6 100755 --- a/include/cpp.hpp +++ b/include/cpp.hpp @@ -22,6 +22,7 @@ #include // integer type selection #include +#include "utility.hpp" #include "safe_common.hpp" #include "checked.hpp" @@ -150,38 +151,27 @@ struct cpp { typename std::make_signed< select_signed > >::type >::type >::type >::type; - template - struct safe_type_promotion { - using base_type_t = typename base_type::type; - using base_type_u = typename base_type::type; - using result_base_type = usual_arithmetic_conversions< - integral_promotion, - integral_promotion - >; - using type = safe_base< - result_base_type, - std::numeric_limits::min(), - std::numeric_limits::max(), - P, - E - >; - }; + template + using result_type = usual_arithmetic_conversions< + integral_promotion::type>, + integral_promotion::type> + >; - template + template struct addition_result { - using type = typename safe_type_promotion::type; + using type = typename result_type::type; }; - template + template struct subtraction_result { - typedef typename safe_type_promotion::type type; + using type = typename result_type::type; }; - template + template struct multiplication_result { - typedef typename safe_type_promotion::type type; + using type = typename result_type::type; }; - template + template struct division_result { - typedef typename safe_type_promotion::type type; + using type = typename result_type::type; }; // forward to correct divide implementation template @@ -193,17 +183,30 @@ struct cpp { return checked::divide(t, u); } - template + template struct modulus_result { - typedef typename safe_type_promotion::type type; + using type = typename result_type::type; }; - template + // forward to correct modulus implementation + template + checked_result + static constexpr modulus( + const T & t, + const U & u + ){ + return checked::modulus(t, u); + } + template struct left_shift_result { - typedef typename safe_type_promotion::type type; + using type = typename result_type::type; }; - template + template struct right_shift_result { - typedef typename safe_type_promotion::type type; + using type = typename result_type::type; + }; + template + struct bitwise_result { + using type = typename result_type::type; }; }; diff --git a/include/exception_policies.hpp b/include/exception_policies.hpp index 22571ff..60fdd0f 100644 --- a/include/exception_policies.hpp +++ b/include/exception_policies.hpp @@ -92,66 +92,10 @@ struct throw_exception { // would otherwise trap at runtime. Hence expressions such as i/j // will trap at compile time unless j can be guaranteed to not be zero. -/* -struct trap_exception { - template - constexpr static void overflow_error(const T *) { - static_assert(std::is_void::value, "overflow_error"); - } - template - constexpr static void underflow_error(const T *) { - static_assert(std::is_void::value, "underflow_error"); - } - template - constexpr static void range_error(const T *) { - static_assert(std::is_void::value, "range_error"); - } - template - constexpr static void domain_error(const T *) { - static_assert(std::is_void::value, "domain_error"); - } -}; - -template -bool noconstexpr(const T & t){ - return false; -} - -struct trap_exception { - template - constexpr static bool overflow_error(const T & t) { - return noconstexpr(t); - //static_assert(noconstexpr(t), "overflow_error"); - } - template - constexpr static void underflow_error(const T & t) { - noconstexpr(t); - //static_assert(std::is_void::value, "underflow_error"); - } - template - constexpr static void range_error(const T & t) { - noconstexpr(t); - //static_assert(std::is_void::value, "range_error"); - } - template - constexpr static void domain_error(const T & t) { - noconstexpr(t); - //static_assert(std::is_void::value, "domain_error"); - } -}; -*/ // meant to be trap the case where a program MIGHT throw an exception struct trap_exception { -/* - - constexpr static void range_error(const char * message) { - throw std::range_error(message); - } - constexpr static void domain_error(const char * message) { - //static_assert(std::is_void::value, "domain_error"); - } -*/}; +}; } // namespace numeric } // namespace boost diff --git a/include/interval.hpp b/include/interval.hpp index e207724..70a739d 100644 --- a/include/interval.hpp +++ b/include/interval.hpp @@ -69,7 +69,19 @@ struct interval { // on checked_result yield tribool. If either argument is an exception // condition, he result is indeterminate. The result of && on two // tribools is indeterminant if either is indeterminate. - return l <= t.l && u >= t.u ; + return + safe_compare::less_than_equal(l, t.l) + && + safe_compare::greater_than_equal(u, t.u) + ; + } + template + constexpr bool includes(const T & t) const { + return + ! safe_compare::less_than(u,t) + && + ! safe_compare::less_than(t,l) + ; } }; diff --git a/include/native.hpp b/include/native.hpp index 5f761a5..070b657 100644 --- a/include/native.hpp +++ b/include/native.hpp @@ -23,16 +23,6 @@ namespace boost { namespace numeric { -// forward declaration - safe type -template< - class Stored, - Stored Min, - Stored Max, - class P, // promotion polic - class E // exception policy -> -class safe_base; - struct native { // Standard C++ type promotion for expressions doesn't depend // on the operation being performed so we can just as well @@ -40,49 +30,27 @@ struct native { // purpose. template - using result_type = decltype(T() + U()); + using result_type = + decltype( + typename base_type::type() + + typename base_type::type() + ); - template - struct safe_type_promotion { - using base_type_t = typename base_type::type; - using base_type_u = typename base_type::type; - using result_base_type = result_type; - using type = safe_base< - result_base_type, - std::numeric_limits::min(), - std::numeric_limits::max(), - P, - E - >; - }; - - template + template struct addition_result { - typedef typename safe_type_promotion::type type; + using type = result_type; }; - template + template struct subtraction_result { - typedef typename safe_type_promotion::type type; + using type = result_type; }; - template + template struct multiplication_result { - typedef typename safe_type_promotion::type type; + using type = result_type; }; - template + template struct division_result { - typedef typename safe_type_promotion::type type; - }; - template - struct modulus_result { - typedef typename safe_type_promotion::type type; - }; - template - struct left_shift_result { - typedef typename safe_type_promotion::type type; - }; - template - struct right_shift_result { - typedef typename safe_type_promotion::type type; + using type = result_type; }; // forward to correct divide implementation @@ -94,6 +62,12 @@ struct native { ){ return checked::divide(t, u); } + + template + struct modulus_result { + using type = result_type; + }; + // forward to correct modulus implementation template checked_result @@ -103,6 +77,20 @@ struct native { ){ return checked::modulus(t, u); } + + template + struct left_shift_result { + using type = result_type; + }; + template + struct right_shift_result { + using type = result_type; + }; + + template + struct bitwise_result { + using type = result_type; + }; }; } // numeric diff --git a/include/safe_base.hpp b/include/safe_base.hpp index 9a26609..e5ce7e6 100644 --- a/include/safe_base.hpp +++ b/include/safe_base.hpp @@ -127,9 +127,8 @@ std::istream & operator>>( safe_base & t ); -template< - std::intmax_t N> -class safe_literal; +template +class safe_literal_impl; ///////////////////////////////////////////////////////////////// // Main implementation @@ -166,39 +165,58 @@ class safe_base { > friend class safe_base; - template - constexpr bool validate(const T & t) const; + friend class std::numeric_limits< + safe_base + >; + + //template + //constexpr bool validate(const T & t) const; template constexpr Stored validated_cast(const T & t) const; - template - constexpr Stored validated_cast(const safe_literal & t) const; + template + constexpr Stored validated_cast(const safe_literal_impl & t) const; + +protected: public: - //////////////////////////////////////////////////////////// // constructors - // default constructor - constexpr explicit safe_base() {} - template - constexpr safe_base(const T & t); - - constexpr safe_base(const safe_base & rhs) : - m_t(rhs.m_t) + constexpr explicit safe_base(const Stored & rhs, std::false_type); + /* + constexpr explicit safe_base(const Stored & rhs) : + m_t(validated_cast(rhs)) {} - - constexpr safe_base(const Stored & rhs) : + constexpr explicit safe_base(validated_wrapper && rhs) : m_t(rhs) {} +*/ + // default constructor + constexpr explicit safe_base() { + // this permits creating of invalid instances. This is inline + // with C++ built-in but violates the premises of the whole library + // choice are: + // do nothing - violates premise of he library that all safe objects + // are valid + // initialize to valid value - violates C++ behavior of types. + // add "initialized" flag. Preserves fixes the above, but doubles + // "overhead" + // still pending on this. + } - //template - //constexpr safe_base(const safe_literal & rhs); - - // note: Rule of Five. Don't specify custom destructor, - // custom move, custom copy, custom assignment, custom - // move assignment. Let the compiler build the defaults. + template + constexpr /*explicit*/ safe_base(const T & t); + + //constexpr safe_base(const safe_base & rhs) : + // m_t(rhs.m_t) + //{} + + // note: Rule of Five. Don't specify + // custom destructor, custom destructor, custom assignment + // custom move, custom move assignment + // Let the compiler build the defaults. public: ///////////////////////////////////////////////////////////////// // casting operators for intrinsic integers @@ -213,18 +231,13 @@ public: >::type = 0 > constexpr operator R () const; + constexpr operator Stored () const; ///////////////////////////////////////////////////////////////// // modification binary operators template constexpr safe_base & operator=(const T & rhs){ - if(! validate(rhs)){ - static_assert(std::is_literal_type::value, "expecting a literal"); - E::range_error( - "Invalid value passed on assignment" - ); - } - m_t = static_cast(rhs); + m_t = validated_cast(rhs); return *this; } @@ -325,10 +338,10 @@ class numeric_limits > using SB = boost::numeric::safe_base; public: constexpr static SB min() noexcept { - return SB(Min); + return SB(Min, std::false_type()); } constexpr static SB max() noexcept { - return SB(Max); + return SB(Max, std::false_type()); } }; diff --git a/include/safe_base_operations.hpp b/include/safe_base_operations.hpp index 82c2f86..6d5ca57 100644 --- a/include/safe_base_operations.hpp +++ b/include/safe_base_operations.hpp @@ -26,6 +26,7 @@ #include "safe_base.hpp" #include "safe_literal.hpp" +//#include "safe_range.hpp" #include "safe_compare.hpp" #include "checked_result.hpp" #include "interval.hpp" @@ -39,98 +40,76 @@ namespace numeric { template struct validate_detail { struct exception_possible { - template - constexpr static bool check_value( - const Min & min, - const Max & max, + template + constexpr static Stored return_value( const T & t ){ // INT08-C - bool validated = - ! safe_compare::less_than( - max, - base_value(t) - ) - && - ! safe_compare::less_than( - base_value(t), - min - ); - if(! validated) + if(! interval().includes(t)) E::range_error("Value out of range for this safe type"); - return validated; - } - constexpr static void domain_error(){ - E::domain_error("Stored type cannot hold argument"); + checked_result r = checked::cast(t); + if(!r.no_exception()){ + E::domain_error("Stored type cannot hold argument"); + } + return static_cast(r); } }; struct exception_not_possible { - template - constexpr static bool check_value( - const Min & min, - const Max & max, + template + constexpr static Stored return_value( const T & t ){ - return true; + return static_cast(t); } - constexpr static void domain_error(){} }; }; template template -constexpr bool safe_base:: -validate(const T & t) const { +constexpr Stored safe_base:: +validated_cast(const T & t) const { constexpr const interval::type> t_interval( base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) ); - constexpr const interval this_interval(Min, Max); + constexpr const interval this_interval{}; // if static values don't overlap, the program can never function static_assert( indeterminate(t_interval < this_interval), "safe type cannot be constructed with this type" ); - return boost::mpl::if_c< + using type = typename boost::mpl::if_c< this_interval.includes(t_interval), typename validate_detail::exception_not_possible, typename validate_detail::exception_possible - >::type::check_value(Min, Max, t); + >::type; + return type::template return_value(base_value(t)); } template -template +template constexpr Stored safe_base:: -validated_cast(const T & t) const { - if(validate(t)){ - checked_result r = checked::cast(base_value(t)); - if(!r.no_exception()){ - E::domain_error("Stored type cannot hold argument"); - } - } +validated_cast(const safe_literal_impl & t) const { + constexpr const interval this_interval{}; + // if static values don't overlap, the program can never function + static_assert( + this_interval.includes(N), + "safe type cannot be constructed value" + ); return static_cast(t); } -template -template -constexpr Stored safe_base:: -validated_cast(const safe_literal & t) const { - if(validate(t)){ - constexpr checked_result r = checked::cast(N); - boost::mpl::if_c< - r.no_exception(), - typename validate_detail::exception_not_possible, - typename validate_detail::exception_possible - >::type::domain_error(); - } - return static_cast(N); -} - ///////////////////////////////////////////////////////////////// // construction and assignment operators +template +constexpr safe_base:: +safe_base(const Stored & rhs, std::false_type) : + m_t(rhs) +{} + // construction from some arbitrary type T template template @@ -158,10 +137,12 @@ struct cast_detail { template constexpr static R return_value(const T & t){ + if(! interval().includes(t)) + E::range_error("Value out of range for this safe type"); checked_result r = checked::cast(t); - if(! r.no_exception()) - E::range_error(r); - return r; + if(!r.no_exception()) + E::domain_error("Stored type cannot hold argument"); + return static_cast(r); } }; @@ -194,6 +175,12 @@ operator R () const { >::type::return_value(m_t); } +template< class Stored, Stored Min, Stored Max, class P, class E> +constexpr safe_base:: +operator Stored () const { + return m_t; +} + ///////////////////////////////////////////////////////////////// // binary operators @@ -285,40 +272,72 @@ template struct addition_result { using P = common_policies; - using type = typename P::promotion_policy::template addition_result< - T, - U, - typename P::promotion_policy, - typename P::exception_policy - >::type; - using result_type = type; - using result_base_type = typename base_type::type; using exception_policy = typename P::exception_policy; + using promotion_policy = typename P::promotion_policy; + using result_base_type = + typename promotion_policy::template addition_result< + T, U + >::type; - static_assert( - boost::numeric::is_safe::value, - "Promotion failed to return safe type" - ); + using t_base_type = typename base_type::type; + using u_base_type = typename base_type::type; + + // filter out case were overflow cannot occur + // note: subtle trickery. Suppose t is safe_range. Then + // std::numeric_limits::min() will be safe_range t_interval{ + base_value(std::numeric_limits::min()), + base_value(std::numeric_limits::max()) + }; + + constexpr static const interval u_interval{ + base_value(std::numeric_limits::min()), + base_value(std::numeric_limits::max()) + }; + + // when we add the temporary intervals above, we'll get a new interval + // with the correct range for the sum ! + constexpr static const checked_result> r_interval + = add(t_interval, u_interval); + + constexpr static const interval type_iterval = + r_interval.no_exception() ? + static_cast>(r_interval) + : + interval{} + ; + + using type = safe_base< + result_base_type, + type_iterval.l, + type_iterval.u, + promotion_policy, + exception_policy + >; + + constexpr static bool no_exception() { + return r_interval.no_exception(); + } struct exception_possible { - static result_type - constexpr return_value(const T & t, const U & u){ + constexpr static result_base_type return_value(const T & t, const U & u){ checked_result r = checked::add( base_value(t), base_value(u) ); dispatch(r); - return result_type(static_cast(r)); + return static_cast(r); } }; struct exception_not_possible { - static result_type - constexpr return_value(const T & t, const U & u){ - return result_type( + constexpr static result_base_type return_value(const T & t, const U & u){ + return static_cast(base_value(t)) + static_cast(base_value(u)) - ); + ; } }; }; @@ -334,35 +353,14 @@ typename boost::lazy_enable_if< constexpr inline operator+(const T & t, const U & u){ // argument dependent lookup should guarentee that we only get here using ar = addition_result; - using result_base_type = typename ar::result_base_type; - using t_base_type = typename base_type::type; - using u_base_type = typename base_type::type; - - // filter out case were overflow cannot occur - // note: subtle trickery. Suppose t is safe_range. Then - // std::numeric_limits::min() will be safe_range t_interval( - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) + return typename ar::type( + boost::mpl::if_c< + ar::no_exception(), + typename ar::exception_not_possible, + typename ar::exception_possible + >::type::return_value(t, u), + std::false_type() // don't need to revalidate ); - - constexpr const interval u_interval( - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) - ); - - // when we add the temporary intervals above, we'll get a new interval - // with the correct range for the sum ! - constexpr const checked_result> r_interval - = add(t_interval, u_interval); - - return boost::mpl::if_c< - r_interval.no_exception(), - typename ar::exception_not_possible, - typename ar::exception_possible - >::type::return_value(t, u); } ///////////////////////////////////////////////////////////////// @@ -370,40 +368,67 @@ constexpr inline operator+(const T & t, const U & u){ template struct subtraction_result { using P = common_policies; - using type = typename P::promotion_policy::template subtraction_result< - T, - U, - typename P::promotion_policy, - typename P::exception_policy - >::type; - using result_type = type; - using result_base_type = typename base_type::type; using exception_policy = typename P::exception_policy; + using promotion_policy = typename P::promotion_policy; + using result_base_type = + typename promotion_policy::template subtraction_result< + T, U + >::type; - static_assert( - boost::numeric::is_safe::value, - "Promotion failed to return safe type" - ); + using t_base_type = typename base_type::type; + using u_base_type = typename base_type::type; + + constexpr static const interval t_interval{ + base_value(std::numeric_limits::min()), + base_value(std::numeric_limits::max()) + }; + + constexpr static const interval u_interval{ + base_value(std::numeric_limits::min()), + base_value(std::numeric_limits::max()) + }; + + // when we add the temporary intervals above, we'll get a new interval + // with the correct range for the sum ! + constexpr static const checked_result> r_interval + = subtract(t_interval, u_interval); + + constexpr static const interval type_iterval = + r_interval.no_exception() ? + static_cast>(r_interval) + : + interval{} + ; + + using type = safe_base< + result_base_type, + type_iterval.l, + type_iterval.u, + promotion_policy, + exception_policy + >; + + constexpr static bool no_exception() { + return r_interval.no_exception(); + } struct exception_possible { - static result_type - constexpr return_value(const T & t, const U & u){ + constexpr static result_base_type return_value(const T & t, const U & u){ checked_result r = checked::subtract( base_value(t), base_value(u) ); dispatch(r); - return result_type(static_cast(r)); + return static_cast(r); } }; struct exception_not_possible { - static result_type - constexpr return_value(const T & t, const U & u){ - return static_cast(result_type( + constexpr static result_base_type return_value(const T & t, const U & u){ + return static_cast(base_value(t)) - static_cast(base_value(u)) - )); + ; } }; }; @@ -418,29 +443,14 @@ typename boost::lazy_enable_if< >::type constexpr operator-(const T & t, const U & u){ using sr = subtraction_result; - using result_base_type = typename sr::result_base_type; - - using t_base_type = typename base_type::type; - using u_base_type = typename base_type::type; - - // filter out case were overflow cannot occur - constexpr const interval t_interval( - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) + return typename sr::type( + boost::mpl::if_c< + sr::no_exception(), + typename sr::exception_not_possible, + typename sr::exception_possible + >::type::return_value(t, u), + std::false_type() // don't need to revalidate ); - constexpr const interval u_interval( - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) - ); - - constexpr const checked_result> r_interval - = subtract(t_interval, u_interval); - - return boost::mpl::if_c< - r_interval.no_exception(), - typename sr::exception_not_possible, - typename sr::exception_possible - >::type::return_value(t, u); } ///////////////////////////////////////////////////////////////// @@ -448,41 +458,73 @@ constexpr operator-(const T & t, const U & u){ template struct multiplication_result { - typedef common_policies P; - typedef typename P::promotion_policy::template multiplication_result< - T, - U, - typename P::promotion_policy, - typename P::exception_policy - >::type type; - using result_type = type; - using result_base_type = typename base_type::type; + using P = common_policies; using exception_policy = typename P::exception_policy; + using promotion_policy = typename P::promotion_policy; + using result_base_type = + typename promotion_policy::template multiplication_result< + T, U + >::type; - static_assert( - boost::numeric::is_safe::value, - "Promotion failed to return safe type" - ); + using t_base_type = typename base_type::type; + using u_base_type = typename base_type::type; + + // filter out case were overflow cannot occur + // note: subtle trickery. Suppose t is safe_range. Then + // std::numeric_limits::min() will be safe_range t_interval{ + base_value(std::numeric_limits::min()), + base_value(std::numeric_limits::max()) + }; + + constexpr static const interval u_interval{ + base_value(std::numeric_limits::min()), + base_value(std::numeric_limits::max()) + }; + + // when we add the temporary intervals above, we'll get a new interval + // with the correct range for the sum ! + constexpr static const checked_result> r_interval + = multiply(t_interval, u_interval); + + constexpr static const interval type_iterval = + r_interval.no_exception() ? + static_cast>(r_interval) + : + interval{} + ; + + using type = safe_base< + result_base_type, + type_iterval.l, + type_iterval.u, + promotion_policy, + exception_policy + >; + + constexpr static bool no_exception() { + return r_interval.no_exception(); + } struct exception_possible { - static result_type - constexpr return_value(const T & t, const U & u){ + constexpr static result_base_type return_value(const T & t, const U & u){ checked_result r = checked::multiply( base_value(t), base_value(u) ); dispatch(r); - return result_type(static_cast(r)); + return static_cast(r); } }; struct exception_not_possible { - static result_type - constexpr return_value(const T & t, const U & u){ - return result_type( + constexpr static result_base_type return_value(const T & t, const U & u){ + return static_cast(base_value(t)) * static_cast(base_value(u)) - ); + ; } }; }; @@ -498,30 +540,14 @@ typename boost::lazy_enable_if< constexpr operator*(const T & t, const U & u){ // argument dependent lookup should guarentee that we only get here using mr = multiplication_result; - using result_base_type = typename mr::result_base_type; - using t_base_type = typename base_type::type; - using u_base_type = typename base_type::type; - - constexpr const interval t_interval( - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) + return typename mr::type( + boost::mpl::if_c< + mr::no_exception(), + typename mr::exception_not_possible, + typename mr::exception_possible + >::type::return_value(t, u), + std::false_type() // don't need to revalidate ); - - constexpr const interval u_interval( - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) - ); - - // when we multiply the temporary intervals above, we'll get a new interval - // with the correct range for the product! - constexpr const checked_result> r_interval - = multiply(t_interval, u_interval); - - return boost::mpl::if_c< - r_interval.no_exception(), - typename mr::exception_not_possible, - typename mr::exception_possible - >::type::return_value(t, u); } ///////////////////////////////////////////////////////////////// @@ -529,43 +555,72 @@ constexpr operator*(const T & t, const U & u){ template struct division_result { - typedef common_policies P; - typedef typename P::promotion_policy::template division_result< - T, - U, - typename P::promotion_policy, - typename P::exception_policy - >::type type; - using result_type = type; - using result_base_type = typename base_type::type; + using P = common_policies; using exception_policy = typename P::exception_policy; using promotion_policy = typename P::promotion_policy; + using result_base_type = + typename promotion_policy::template division_result< + T, U + >::type; - static_assert( - boost::numeric::is_safe::value, - "Promotion failed to return safe type" - ); + using t_base_type = typename base_type::type; + using u_base_type = typename base_type::type; + + constexpr static const interval t_interval{ + base_value(std::numeric_limits::min()), + base_value(std::numeric_limits::max()) + }; + + constexpr static const interval u_interval{ + base_value(std::numeric_limits::min()), + base_value(std::numeric_limits::max()) + }; + + constexpr static const checked_result> r_interval + = divide_nz(t_interval, u_interval); + + constexpr static const interval result_iterval = + r_interval.no_exception() ? + static_cast>(r_interval) + : + interval{} + ; + + using type = safe_base< + result_base_type, + result_iterval.l, + result_iterval.u, + promotion_policy, + exception_policy + >; + + constexpr static bool no_exception() { + return + // if no over/under flow or domain error possible + r_interval.no_exception() + // and if the denominator cannot contain zero + && (u_interval.l <= 0 && u_interval.u >=0 ) + ; + } struct exception_possible { - static result_type - constexpr return_value(const T & t, const U & u){ + static type constexpr return_value(const T & t, const U & u){ checked_result r = promotion_policy::template divide( base_value(t), base_value(u) ); dispatch(r); - return result_type(static_cast(r)); + return static_cast(r); } }; struct exception_not_possible { - static result_type - constexpr return_value(const T & t, const U & u){ - return result_type( + constexpr static type return_value(const T & t, const U & u){ + return static_cast(base_value(t)) / static_cast(base_value(u)) - ); + ; } }; }; @@ -581,33 +636,14 @@ typename boost::lazy_enable_if< constexpr operator/(const T & t, const U & u){ // argument dependent lookup should guarentee that we only get here using dr = division_result; - using result_base_type = typename dr::result_base_type; - using t_base_type = typename base_type::type; - using u_base_type = typename base_type::type; - - constexpr const interval t_interval( - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) + return typename dr::type( + boost::mpl::if_c< + dr::no_exception(), + typename dr::exception_not_possible, + typename dr::exception_possible + >::type::return_value(t, u), + std::false_type() // don't need to revalidate ); - - constexpr const interval u_interval( - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) - ); - - // when we divide the temporary intervals above, we'll get a new interval - // with the correct range for the result! - constexpr const checked_result> r_interval = - divide(t_interval, u_interval); - - return boost::mpl::if_c< - // if no over/under flow or domain error possible - r_interval.no_exception() - // and if the denominator cannot contain zero - && (u_interval.l <= 0 && u_interval.u >=0 ), - typename dr::exception_not_possible, - typename dr::exception_possible - >::type::return_value(t, u); } ///////////////////////////////////////////////////////////////// @@ -616,46 +652,73 @@ constexpr operator/(const T & t, const U & u){ template struct modulus_result { typedef common_policies P; - typedef typename P::promotion_policy::template modulus_result< - T, - U, - typename P::promotion_policy, - typename P::exception_policy - >::type type; - using result_type = type; - using result_base_type = typename base_type::type; using exception_policy = typename P::exception_policy; using promotion_policy = typename P::promotion_policy; + using result_base_type = + typename promotion_policy::template modulus_result< + T, U + >::type; - static_assert( - boost::numeric::is_safe::value, - "Promotion failed to return safe type" - ); + using t_base_type = typename base_type::type; + using u_base_type = typename base_type::type; + + constexpr static const interval t_interval{ + base_value(std::numeric_limits::min()), + base_value(std::numeric_limits::max()) + }; + + constexpr static const interval u_interval{ + base_value(std::numeric_limits::min()), + base_value(std::numeric_limits::max()) + }; + + constexpr static const checked_result> r_interval + = modulus_nz(t_interval, u_interval); + + constexpr static const interval result_iterval = + r_interval.no_exception() ? + static_cast>(r_interval) + : + interval{} + ; + + using type = safe_base< + result_base_type, + result_iterval.l, + result_iterval.u, + promotion_policy, + exception_policy + >; + + constexpr static bool no_exception() { + return + // if no over/under flow or domain error possible + ((u_interval.l > 0 || 0 > u_interval.u) + // and if the denominator cannot contain zero + && (u_interval.l <= 0 && u_interval.u >=0 )) + ; + } struct exception_possible { - static result_type - constexpr return_value(const T & t, const U & u){ - using t_type = decltype(base_value(t) % base_value(u)); - checked_result r = - promotion_policy::template modulus( - base_value(t), - base_value(u) - ); - + constexpr static result_base_type return_value(const T & t, const U & u){ + checked_result r = checked::modulus( + base_value(t), + base_value(u) + ); dispatch(r); - return result_type(static_cast(r)); + return static_cast(r); } }; struct exception_not_possible { - static result_type - constexpr return_value(const T & t, const U & u){ - return result_type( + constexpr static result_base_type return_value(const T & t, const U & u){ + return static_cast(base_value(t)) % static_cast(base_value(u)) - ); + ; } }; + }; template @@ -667,26 +730,16 @@ typename boost::lazy_enable_if< modulus_result >::type inline operator%(const T & t, const U & u){ - // argument dependent lookup should guarentee that we only get here - // only if one of the types is a safe type. Verify this here using mr = modulus_result; - using result_base_type = typename mr::result_base_type; - using u_base_type = typename base_type::type; - // filter out case were overflow cannot occur - constexpr const interval u_interval( - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) + return typename mr::type( + boost::mpl::if_c< + mr::no_exception(), + typename mr::exception_not_possible, + typename mr::exception_possible + >::type::return_value(t, u), + std::false_type() // don't need to revalidate ); - - return boost::mpl::if_c< - // if no over/under flow or domain error possible - ((u_interval.l > 0 || 0 > u_interval.u) - // and if the denominator cannot contain zero - && (u_interval.l <= 0 && u_interval.u >=0 )), - typename mr::exception_not_possible, - typename mr::exception_possible - >::type::return_value(t, u); } ///////////////////////////////////////////////////////////////// @@ -840,38 +893,65 @@ constexpr operator<=(const T & lhs, const U & rhs) { // left shift template struct left_shift_result { - typedef common_policies P; - typedef typename P::promotion_policy::template left_shift_result< - T, - U, - typename P::promotion_policy, - typename P::exception_policy - >::type type; - using result_type = type; - using result_base_type = typename base_type::type; + using P = common_policies; using exception_policy = typename P::exception_policy; + using promotion_policy = typename P::promotion_policy; + using result_base_type = + typename promotion_policy::template left_shift_result< + T, U + >::type; + + using t_base_type = typename base_type::type; + using u_base_type = typename base_type::type; + + constexpr static const interval t_interval{ + base_value(std::numeric_limits::min()), + base_value(std::numeric_limits::max()) + }; + + constexpr static const interval u_interval{ + base_value(std::numeric_limits::min()), + base_value(std::numeric_limits::max()) + }; + + constexpr static const checked_result> r_interval { + left_shift(t_interval, u_interval) + }; + + constexpr static const interval result_iterval = + r_interval.no_exception() ? + static_cast>(r_interval) + : + interval{} + ; + + using type = safe_base< + result_base_type, + result_iterval.l, + result_iterval.u, + promotion_policy, + exception_policy + >; + + constexpr static bool no_exception() { + return r_interval.no_exception(); + } - static_assert( - boost::numeric::is_safe::value, - "Promotion failed to return safe type" - ); struct exception_possible { - static result_type - constexpr return_value(const T & t, const U & u){ + constexpr static type return_value(const T & t, const U & u){ const checked_result r = checked::left_shift( base_value(t), base_value(u) ); dispatch(r); - return result_type(static_cast(r)); + return static_cast(r); } }; struct exception_not_possible { - static result_type - constexpr return_value(const T & t, const U & u){ + constexpr static type return_value(const T & t, const U & u){ // just return the normal calcuation - return result_type(t << u); + return t << u; } }; }; @@ -890,27 +970,10 @@ typename boost::lazy_enable_if_c< constexpr inline operator<<(const T & t, const U & u){ // INT13-CPP using lsr = left_shift_result; - using result_base_type = typename lsr::result_base_type; - using t_base_type = typename base_type::type; - using u_base_type = typename base_type::type; - - constexpr const interval t_interval( - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) - ); - - constexpr const interval u_interval( - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) - ); - - constexpr const checked_result> r_interval { - left_shift(t_interval, u_interval) - }; return boost::mpl::if_c< // if no over/under flow or domain error possible - r_interval.no_exception(), + lsr::no_exception(), typename lsr::exception_not_possible, typename lsr::exception_possible >::type::return_value(t, u); @@ -919,37 +982,65 @@ constexpr inline operator<<(const T & t, const U & u){ // right shift template struct right_shift_result { - typedef common_policies P; - typedef typename P::promotion_policy::template right_shift_result< - T, - U, - typename P::promotion_policy, - typename P::exception_policy - >::type type; - using result_type = type; - using result_base_type = typename base_type::type; + using P = common_policies; using exception_policy = typename P::exception_policy; + using promotion_policy = typename P::promotion_policy; + using result_base_type = + typename promotion_policy::template right_shift_result< + T, U + >::type; + + using t_base_type = typename base_type::type; + using u_base_type = typename base_type::type; + + constexpr static const interval t_interval{ + base_value(std::numeric_limits::min()), + base_value(std::numeric_limits::max()) + }; + + constexpr static const interval u_interval{ + base_value(std::numeric_limits::min()), + base_value(std::numeric_limits::max()) + }; + + constexpr static const checked_result> r_interval { + right_shift(t_interval, u_interval) + }; + + constexpr static const interval result_iterval = + r_interval.no_exception() ? + static_cast>(r_interval) + : + interval{} + ; + + using type = safe_base< + result_base_type, + result_iterval.l, + result_iterval.u, + promotion_policy, + exception_policy + >; + + constexpr static bool no_exception() { + return r_interval.no_exception(); + } - static_assert( - boost::numeric::is_safe::value, - "Promotion failed to return safe type" - ); struct exception_possible { - static result_type - constexpr return_value(const T & t, const U & u){ + constexpr static type return_value(const T & t, const U & u){ const checked_result r = checked::right_shift( base_value(t), base_value(u) ); dispatch(r); - return result_type(static_cast(r)); + return static_cast(r); } }; + struct exception_not_possible { - static result_type - constexpr return_value(const T & t, const U & u){ + constexpr static type return_value(const T & t, const U & u){ // just return the normal calcuation - return result_type(t >> u); + return t >> u; } }; }; @@ -968,27 +1059,10 @@ typename boost::lazy_enable_if_c< constexpr inline operator>>(const T & t, const U & u){ // INT13-CPP using rsr = right_shift_result; - using result_base_type = typename rsr::result_base_type; - using t_base_type = typename base_type::type; - using u_base_type = typename base_type::type; - - constexpr const interval t_interval( - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) - ); - - constexpr const interval u_interval( - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) - ); - - constexpr const checked_result> r_interval { - right_shift(t_interval, u_interval) - }; return boost::mpl::if_c< // if no over/under flow or domain error possible - r_interval.no_exception(), + rsr::no_exception(), typename rsr::exception_not_possible, typename rsr::exception_possible >::type::return_value(t, u); @@ -1000,20 +1074,13 @@ constexpr inline operator>>(const T & t, const U & u){ // operator | template struct bitwise_result { - typedef common_policies P; - typedef typename P::promotion_policy::template or_result< - T, - U, - typename P::promotion_policy, - typename P::exception_policy - >::type type; - using result_type = type; - using result_base_type = typename base_type::type; + using P = common_policies; using exception_policy = typename P::exception_policy; - static_assert( - boost::numeric::is_safe::value, - "Promotion failed to return safe type" - ); + using promotion_policy = typename P::promotion_policy; + using result_base_type = + typename promotion_policy::template bitwise< + T, U + >::type; static_assert( std::numeric_limits::is_integer && (! std::numeric_limits::is_signed) @@ -1117,26 +1184,24 @@ std::ostream & operator<<( return os; } -namespace detail { - - template +namespace { + template struct Temp { T value; }; // primary template - - template - struct Temp< - T, - typename std::enable_if< - (std::is_same::value - || std::is_same::value) - > - > - { + template<> struct Temp { int value; - }; - -} // detail + }; // secondary template + template<> struct Temp { + int value; + }; // secondary template + template<> struct Temp { + int value; + }; // secondary template + template<> struct Temp { + int value; + }; // secondary template +} template< class T, @@ -1149,8 +1214,7 @@ std::istream & operator>>( std::istream & is, safe_base & t ){ - - detail::Temp tx; + Temp tx; is >> tx.value; if(is.fail()) E::range_error("error in file input"); diff --git a/include/safe_compare.hpp b/include/safe_compare.hpp index 26a0bb7..efff550 100644 --- a/include/safe_compare.hpp +++ b/include/safe_compare.hpp @@ -80,8 +80,6 @@ namespace detail { template constexpr bool less_than(const T & lhs, const U & rhs) { - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - static_assert(std::is_integral::value, "only intrinsic integers permitted"); return detail::less_than< std::numeric_limits::is_signed, std::numeric_limits::is_signed @@ -89,71 +87,18 @@ constexpr bool less_than(const T & lhs, const U & rhs) { } template -constexpr bool greater_than_equal(const T & lhs, const U & rhs) { - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - static_assert(std::is_integral::value, "only intrinsic integers permitted"); +constexpr bool greater_than(const T & lhs, const U & rhs) { return less_than(rhs, lhs); } -namespace detail { - // both arguments unsigned or signed - template - struct greater_than { - template - constexpr static bool invoke(const T & t, const U & u){ - return t > u; - } - }; - - // T unsigned, U signed - template<> - struct greater_than { - template - constexpr static bool invoke(const T & t, const U & u){ - return - (u < 0) ? - true - : - greater_than::invoke( - t, - static_cast::type &>(u) - ) - ; - } - }; - // T signed, U unsigned - template<> - struct greater_than { - template - constexpr static bool invoke(const T & t, const U & u){ - return - (t < 0) ? - false - : - greater_than::invoke( - static_cast::type &>(t), - u - ) - ; - } - }; -} // detail - template -constexpr bool greater_than(const T & lhs, const U & rhs) { - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - return detail::greater_than< - std::numeric_limits::is_signed, - std::numeric_limits::is_signed - >::template invoke(lhs, rhs); +constexpr bool greater_than_equal(const T & lhs, const U & rhs) { + return ! less_than(lhs, rhs); } template constexpr bool less_than_equal(const T & lhs, const U & rhs) { - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - return greater_than(rhs, lhs); + return ! greater_than(lhs, rhs); } namespace detail { @@ -202,8 +147,6 @@ namespace detail { template constexpr bool equal(const T & lhs, const U & rhs) { - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - static_assert(std::is_integral::value, "only intrinsic integers permitted"); return detail::equal< std::numeric_limits::is_signed, std::numeric_limits::is_signed @@ -212,8 +155,6 @@ constexpr bool equal(const T & lhs, const U & rhs) { template constexpr bool not_equal(const T & lhs, const U & rhs) { - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - static_assert(std::is_integral::value, "only intrinsic integers permitted"); return ! detail::equal< std::numeric_limits::is_signed, std::numeric_limits::is_signed diff --git a/include/safe_literal.hpp b/include/safe_literal.hpp index 87631be..876e914 100644 --- a/include/safe_literal.hpp +++ b/include/safe_literal.hpp @@ -20,55 +20,46 @@ namespace boost { namespace numeric { -template -class safe_literal; +template +class safe_literal_impl; -template -struct is_safe > : public std::true_type +template +struct is_safe > : public std::true_type {}; -template -struct base_type > { - typedef std::intmax_t type; +template +struct base_type > { + using type = T; }; -template -constexpr std::intmax_t base_value( - const safe_literal & st +template +constexpr T base_value( + const safe_literal_impl & st ) { return N; } -template +template std::ostream & operator<<( std::ostream & os, - const safe_literal & t + const safe_literal_impl & t ); -template -class safe_literal { - std::intmax_t m_t; +template +class safe_literal_impl { + T m_t; - friend std::ostream & operator<< ( + friend std::ostream & operator<< ( std::ostream & os, - const safe_literal & t + const safe_literal_impl & t ); - template< - class StoredX, - StoredX MinX, - StoredX MaxX, - class PX, // promotion polic - class EX // exception policy - > - friend class safe_base; - public: //////////////////////////////////////////////////////////// // constructors // default constructor - constexpr safe_literal() : + constexpr safe_literal_impl() : m_t(N) {} @@ -89,6 +80,12 @@ public: } }; +template +using safe_literal = safe_literal_impl; + +template +using safe_unsigned_literal = safe_literal_impl; + } // numeric } // boost diff --git a/include/safe_range.hpp b/include/safe_range.hpp index 32c4cc2..fcb3566 100644 --- a/include/safe_range.hpp +++ b/include/safe_range.hpp @@ -13,8 +13,8 @@ // http://www.boost.org/LICENSE_1_0.txt) #include // intmax_t, uintmax_t -#include // (u)int_t<>::least, exact +#include "utility.hpp" #include "safe_base.hpp" #include "safe_base_operations.hpp" #include "native.hpp" @@ -26,49 +26,19 @@ namespace boost { namespace numeric { -namespace detail { - constexpr int ulog(boost::uintmax_t x){ - unsigned i = 0; - for(; x > 0; ++i) - x >>= 1; - return i; - } - constexpr int log(std::intmax_t x){ - if(x < 0) - x = ~x; - return ulog(x) + 1; - } - template< - std::intmax_t MIN, - std::intmax_t MAX - > - using signed_stored_type = typename boost::int_t< - std::max(log(MIN), log(MAX)) - >::least ; - - template< - std::uintmax_t MIN, - std::uintmax_t MAX - > - using unsigned_stored_type = typename boost::uint_t< - std::max(ulog(MIN), ulog(MAX)) - >::least ; - -} // detail - ///////////////////////////////////////////////////////////////// // safe_signed_range template < - std::intmax_t MIN, - std::intmax_t MAX, + std::intmax_t Min, + std::intmax_t Max, class P = native, class E = throw_exception > using safe_signed_range = safe_base< - typename detail::signed_stored_type, - static_cast >(MIN), - static_cast >(MAX), + typename boost::numeric::signed_stored_type, + static_cast >(Min), + static_cast >(Max), P, E >; @@ -77,15 +47,15 @@ using safe_signed_range = safe_base< // safe_unsigned_range template < - std::uintmax_t MIN, - std::uintmax_t MAX, + std::uintmax_t Min, + std::uintmax_t Max, class P = native, class E = throw_exception > using safe_unsigned_range = safe_base< - typename detail::unsigned_stored_type, - static_cast >(MIN), - static_cast >(MAX), + typename boost::numeric::unsigned_stored_type, + static_cast >(Min), + static_cast >(Max), P, E >; diff --git a/include/static_if.hpp b/include/static_if.hpp deleted file mode 100644 index caed123..0000000 --- a/include/static_if.hpp +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef BOOST_STATIC_IF_HPP -#define BOOST_STATIC_IF_HPP - -// MS compatible compilers support #pragma once -#if defined(_MSC_VER) && (_MSC_VER >= 1020) -# pragma once -#endif - -// Copyright (c) 2012 Robert Ramey -// -// Distributed under the Boost Software License, Version 1.0. (See -// accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -// due To Paul Fultz II - -namespace boost { -namespace utility { - namespace detail { - struct identity - { - template - T operator()(T&& x) const - { - return std::forward(x); - } - }; - - template< bool Condition > - struct static_if_statement - { - template< typename F > constexpr void then ( F const& f ) { f(identity()); } - template< typename F > constexpr void else_ ( F const& ) { } - }; - - template< > - struct static_if_statement - { - template< typename F > constexpr void then ( F const& ) { } - template< typename F > constexpr void else_ ( F const& f ) { f(identity()); } - }; - } // detail - - template< bool Condition, typename F > - detail::static_if_statement constexpr static_if ( F const& f ) - { - detail::static_if_statement if_; - if_.then(f); - return if_; - } -} // utility -} // boost - -/* - Then you can call it like this: - - template< typename T > - void assign ( T& x, T const& y ) - { - x = y; - - static_if::value>([](auto f) - { - assert(f(x) == f(y)); - std::cout << "asserted for: " << typeid(T).name() << std::endl; - }) - .else_([] (auto) - { - std::cout << "cannot assert for: " << typeid(T).name() << std::endl; - }); - } -*/ - -#define BOOST_STATIC_IF(condition) boost::utility::static_if([&](auto f) -#define BOOST_STATIC_ELSE .else_([&] (auto) - -#endif // BOOST_STATIC_IF_HPP diff --git a/include/utility.hpp b/include/utility.hpp new file mode 100644 index 0000000..b912c39 --- /dev/null +++ b/include/utility.hpp @@ -0,0 +1,52 @@ +#ifndef BOOST_NUMERIC_UTILITY_HPP +#define BOOST_NUMERIC_UTILITY_HPP + +// MS compatible compilers support #pragma once +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// Copyright (c) 2015 Robert Ramey +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include // intmax_t, uintmax_t +#include +#include // (u)int_t<>::least, exact + +namespace boost { +namespace numeric { + + constexpr int ulog(boost::uintmax_t x){ + unsigned i = 0; + for(; x > 0; ++i) + x >>= 1; + return i; + } + constexpr int log(std::intmax_t x){ + if(x < 0) + x = ~x; + return ulog(x) + 1; + } + template< + std::intmax_t Min, + std::intmax_t Max + > + using signed_stored_type = typename boost::int_t< + std::max(log(Min), log(Max)) + >::least ; + + template< + std::uintmax_t Min, + std::uintmax_t Max + > + using unsigned_stored_type = typename boost::uint_t< + std::max(ulog(Min), ulog(Max)) + >::least ; + +} // numeric +} // boost + +#endif // BOOST_NUMERIC_UTILITY_HPP diff --git a/test/test_auto.cpp b/test/test_auto.cpp index 7e53c1a..4b47715 100644 --- a/test/test_auto.cpp +++ b/test/test_auto.cpp @@ -5,7 +5,7 @@ #include "../include/automatic.hpp" int test_log(){ - using namespace boost::numeric::detail; + using namespace boost::numeric; assert(ulog(127) == 7); assert(ulog(128) == 8); assert(ulog(129) == 8); diff --git a/test/test_compare.cpp b/test/test_compare.cpp index 522e28c..32ad7ec 100644 --- a/test/test_compare.cpp +++ b/test/test_compare.cpp @@ -106,7 +106,7 @@ bool test_compare( test_compare_detail(t1, t2, expected_result); return false; } - + std::cout << std::endl; return true; // correct result } @@ -183,5 +183,6 @@ void break_check(unsigned int i, unsigned int j){ int main(int argc, char * argv[]){ bool rval = true; TEST_EACH_VALUE_PAIR + std::cout << (rval ? "success!" : "failure") << std::endl; return ! rval ; } diff --git a/test/test_range.cpp b/test/test_range.cpp index 274e2ac..dfce68b 100644 --- a/test/test_range.cpp +++ b/test/test_range.cpp @@ -2,23 +2,24 @@ #include #include +#include "../include/utility.hpp" #include "../include/safe_range.hpp" template void display_ulog(T Max){ std::cout << "ulog(" << Max << ") = " - << boost::numeric::detail::ulog(Max) << std::endl; + << boost::numeric::ulog(Max) << std::endl; } template void display_log(T Max){ std::cout << "log(" << Max << ") = " - << boost::numeric::detail::log(Max) << std::endl; + << boost::numeric::log(Max) << std::endl; } bool test_log(){ - using namespace boost::numeric::detail; + using namespace boost::numeric; assert(ulog(127) == 7); // 7 bits assert(ulog(127) == 7); // 7 bits assert(ulog(128) == 8); // 8 bits @@ -42,7 +43,7 @@ bool test_log(){ bool test1(){ using namespace boost::numeric; - typedef detail::signed_stored_type<-256, 254> t1; + typedef signed_stored_type<-256, 254> t1; safe_signed_range<-128, 127> s1(1); safe_signed_range<-256, 254> s2(2);