diff --git a/README.md b/README.md index dfcae81..95f90a3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ safe_numerics ============= -Arithmetic operations in C++ are NOT guarenteed to yield a correct mathematical result. This feature is inherited from the early days of C. The behavior of int, unsigned int and others were designed to map closely to the underlying hardware. Computer hardware implements these types as a fixed number of bits. When the result of arithmetic operations exceeds this number of bits, the result is undefined and usually not what the programmer intended. It is incumbent up the C++ programmer to guarentee that this behavior does not result in incorrect behavior of the program. This library implements special versions of these data types which behave exactly like the original ones EXCEPT that the results of these operations are checked to be sure that an exception will be thrown anytime an attempt is made to store the result of an undefined operation. +Arithmetic operations in C++ are NOT guarenteed to yield a correct mathematical result. This feature is inherited from the early days of C. The behavior of int, unsigned int and others were designed to map closely to the underlying hardware. Computer hardware implements these types as a fixed number of bits. When the result of arithmetic operations exceeds this number of bits, the result is undefined and usually not what the programmer intended. It is incumbent upon the C++ programmer to guarantee that this behavior does not result in incorrect behavior of the program. This library implements special versions of these data types which behave exactly like the original ones EXCEPT that the results of these operations are checked to be sure that an exception will be thrown anytime an attempt is made to store the result of an undefined operation. Additionally, we define data types safe_signed_range and safe_unsigned_range which will throw an exception if an attempt is made to store a result which is outside the closed range [MIN, MAX] diff --git a/doc/boostbook/acknowledgements.xml b/doc/boostbook/acknowledgements.xml index 1d6fa47..db0337b 100644 --- a/doc/boostbook/acknowledgements.xml +++ b/doc/boostbook/acknowledgements.xml @@ -9,13 +9,13 @@ - David LaBlanc + David LeBlanc This library is inspired by David LeBlanc's SafeInt Library . I found this library very well done in every way - and useful in my embedded systems work. This motivated me to take to + and useful in my embedded systems work. This motivated me to take it to the "next level". @@ -28,11 +28,11 @@ Andrzej Commented and reviewed the library as it was originally posted on the Boost Library Incubator. - The the consequent back and forth motivated me to invest more effort + The consequent back and forth motivated me to invest more effort in developing documentation and examples to justify the utility, indeed the necessity, for this library. He also noted many errors in - code, documentation, and tests. Without his interested and effort, I - do not believe the library would have progressed beyond it's initial + code, documentation, and tests. Without his interest and effort, I + do not believe the library would have progressed beyond its initial stages. diff --git a/doc/boostbook/automatic.xml b/doc/boostbook/automatic.xml index b3a58f8..8804f60 100644 --- a/doc/boostbook/automatic.xml +++ b/doc/boostbook/automatic.xml @@ -112,7 +112,7 @@ int main(){ // In such cases, there is no runtime overhead from using safe types. safe_t x = 127; safe_t y = 2; - auto z = x + y; // z is guarenteed correct without any runtime overhead or interrupt. + auto z = x + y; // z is guaranteed correct without any runtime overhead or exception. return 0; } diff --git a/doc/boostbook/checked.xml b/doc/boostbook/checked.xml index 3faa762..0771273 100644 --- a/doc/boostbook/checked.xml +++ b/doc/boostbook/checked.xml @@ -29,7 +29,7 @@ template<class R, class T, class U> constexpr checked_result<R> checked::multiply(const T & t, const U & u); -// safe division on unsafe types +// safe division on primitive types template<class R, class T, class U> constexpr checked_result<R> checked::divide(const T & t, const U & u); @@ -38,7 +38,7 @@ template<class R, class T, class U> constexpr checked_result<R> checked::divide_automatic(const T & t, const U & u); -// safe modulus on unsafe types +// safe modulus on primitive types template<class R, class T, class U> constexpr checked_result<R> checked::modulus(const T & t, const U & u); @@ -86,7 +86,7 @@ checked::bitwise_xor(const T & t, const U & u);
Complexity - Each function performs one and only one arithmetic operation + Each function performs one and only one arithmetic operation.
@@ -110,7 +110,7 @@ checked_result<result_base_type> r = checked::multiply<int>(24, 42); Notes Some compilers have command line switches (e.g. -ftrapv) which - enable special behavior such erroneous integer operations are detected at + enable special behavior such that erroneous integer operations are detected at run time. The library has been implemented in such a way that these facilities are not used. It's possible they might be helpful in particular environment. These could be be exploited by re-implementing some functions diff --git a/doc/boostbook/checked_result.xml b/doc/boostbook/checked_result.xml index 952425d..efc6d27 100644 --- a/doc/boostbook/checked_result.xml +++ b/doc/boostbook/checked_result.xml @@ -9,7 +9,7 @@
Description - checked_result is a wrapper class designed to hold result of some + checked_result is a wrapper class designed to hold the result of some operation. It can hold either the result of the operation or information on why the operation failed to produce a valid result. Note that this type is an internal feature of the library and shouldn't be exposed to library @@ -220,7 +220,7 @@ void - invoke exception in accordance exception_type + invoke exception in accordance with exception_type value diff --git a/doc/boostbook/cpp.xml b/doc/boostbook/cpp.xml index dc1f801..242cb0e 100644 --- a/doc/boostbook/cpp.xml +++ b/doc/boostbook/cpp.xml @@ -8,17 +8,17 @@ Description This policy is used to promote safe types in arithmetic expressions - according the standard rules in the C++ standard. But rather than using + according to the 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++ + This policy is useful for running test programs which use C++ 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 the desk top. + another can be tested on the desktop.
@@ -108,7 +108,7 @@ 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 anything else. One debugs this code by using the "burn" and "crash" method - - you burn the chip (download the code) run the code, observe the results, + - you burn the chip (download the code), run the code, observe the results, make changes and try again. This is a crude method which is usually the one used. But it can be quite time consuming. @@ -121,11 +121,11 @@ other problems), make our algorithm testing environment differ from our target environment. We can address this by defining INT as a safe integer with a range of 8 bits. By using a custom promotion policy, we can force - the evaluation of C++ expressions int the test environment to be the same - as that in the target environment. Also in our target environment, we can - trap any overflows or other errors. So we can write and test our code on - our desktop system and download the code to the target knowing that it - just has to work. This is a huge time saver and confidence builder. The + the evaluation of C++ expressions in the test environment to be the same as that + in the target environment. Also in our target environment, we can trap any + overflows or other errors. So we can write and test our code on our + desktop system and download the code to the target knowing that it just + has to work. This is a huge time saver and confidence builder. The following code is taken from a real project which has used this method. @@ -161,7 +161,7 @@ using uint16 = safe_t<std::uint16_t>; using uint32 = safe_t<std::uint32_t>; //////////////////////////////////////////////////////////////// -// Mock defines, functions etc which are in he "real application +// Mock defines, functions etc which are in the "real application" ... @@ -182,6 +182,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. + 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.
diff --git a/doc/boostbook/eliminate_runtime_penalty.xml b/doc/boostbook/eliminate_runtime_penalty.xml index 692e474..b3123c4 100644 --- a/doc/boostbook/eliminate_runtime_penalty.xml +++ b/doc/boostbook/eliminate_runtime_penalty.xml @@ -7,7 +7,7 @@ Up until now, we've focused on detecting when incorrect results are produced and handling these occurrences either by throwing an exception or invoking some designated function. We've achieved our goal of detecting and - handling arithmetically incorrect behavior - but at what cost. It is a fact + handling arithmetically incorrect behavior - but at what cost? It is a fact that many C++ programmers will find this trade-off unacceptable. So the question arises as to how we might minimize or eliminate this runtime penalty. @@ -17,12 +17,6 @@ special exception policy: trap_exception.
- - In this document we use the term "trap" to mean invocation of some - message at compile time. We use "throw" to indicate a runtime exception. - - - Now, any expression which So the result of the sum of two integer types may result in another integer type. If the values are large, the result can exceed the size that the resulting integer type can hold. This is what we call "overflow". The - C/C++ standard characterises this as undefined behavior and leaves to + C/C++ standard characterizes this as undefined behavior and leaves to compiler implementors the decision as to how such a situation will be handled. Usually, this means just truncating the result to fit into the result type - which sometimes will make the result arithmetically - incorrect. However, depending on the compiler, compile time switch - settings, the such case may result in some sort of run time + incorrect. However, depending on the compiler and compile time switch + settings, such cases may result in some sort of run time exception. The complete signature for a safe integer type is: @@ -79,16 +73,16 @@ safe; The promotion rules implemented in the default native type promotion - policy are consistent with those of standard C++ + linkend="safe_numerics.promotion_policy.models.native">native + type promotion policy are consistent with those of standard C++ Up until now, we've focused on detecting when this happens and - invoking an interrupt or other kind of error handler. + invoking an exception 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: + linkend="safe_numerics.promotion_policy.models.automatic">automatic + type promotion policy, we can change the rules of C++ arithmetic for safe + types to something like the following: @@ -116,16 +110,16 @@ 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 + linkend="safe_numerics.promotion_policy.models.automatic">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 !!! The usage of + is no need to check for errors - they can't happen!!! The usage of the trap_exception + linkend="safe_numerics.exception_policy.models.trap_exception">trap_exception exception policy enforces this guarantee. @@ -156,7 +150,7 @@ long z = (long)x + (long)y; // can never overflow generated. Depending on the application, it should be rare to generate error checking code, and even more rare to actually invoke it. Any such instances are detected at compile time by the trap_exception + linkend="safe_numerics.exception_policies.trap_exception">trap_exception exception policy. This small example illustrates how to use automatic type promotion @@ -178,19 +172,19 @@ z = <long>[-4294967296,4294967294] = 2147483649 that: the automatic - type promotion policy has rendered the result of the some of two + linkend="safe_numerics.promotion_policy.models.automatic">automatic + type promotion policy has rendered the result of the sum of two integers as a long type. our program compiles without error - even when using the trap_exception + linkend="safe_numerics.exception_policies.trap_exception">trap_exception exception policy - We do not need to use try/catch idiom to handle + We do not need to use the try/catch idiom to handle arithmetic errors - we will have none. @@ -268,15 +262,15 @@ z = <signed char>[-24,82] = 77 As before, we define a type safe_t to reflect our - view of legal values for this program. This uses automatic - type promotion policy as well as trap_exception exception policy to enforce elimination of runtime penalties. - The function f accepts only arguments of type + The function f accepts only arguments of type safe_t so there is no need to check the input values. This performs the functionality of programming by contract with no @@ -295,7 +289,7 @@ z = <signed char>[-24,82] = 77 - On calling of the function f, arguments of type + On calling of the function f, arguments of type input_safe_t are converted to values of type safe_t . In this particular example, it can be determined at compile time that construction of an instance of a diff --git a/doc/boostbook/exception_policy_concept.xml b/doc/boostbook/exception_policy_concept.xml index 8d7c810..0de9da6 100644 --- a/doc/boostbook/exception_policy_concept.xml +++ b/doc/boostbook/exception_policy_concept.xml @@ -29,14 +29,14 @@ EP A type that fulfills the requirements of an - ExceptionPollicy + ExceptionPolicy message - A const char * which refers to a text message - about the cause of an exception + A const char * which refers to a text message about the + cause of an exception @@ -49,11 +49,11 @@ Any operations which result in integers which cannot be represented as some Numeric type will throw an exception. - + - + @@ -145,7 +145,7 @@ If an exceptional condition is detected at runtime throw the exception. Safe types use this exception policy as the default if no - other one is specified. + other policy is specified. @@ -156,13 +156,15 @@ + template<void (*F)(const char *), void (*G)(const char + *), void (*H)(const char *)> + boost::numeric::no_exception_support If you want to specify specific behavior for particular exception types, use this policy. The most likely situation is where - you don't have exception support and you want to handle runtime errors - by calling your own special functions. This should permit usage of the - library in environments where C++ exceptions are not supported. + you don't have exception support and you want to trap "exceptions" by + calling your own special functions. diff --git a/doc/boostbook/faq.xml b/doc/boostbook/faq.xml index aa0329b..cc7f69b 100644 --- a/doc/boostbook/faq.xml +++ b/doc/boostbook/faq.xml @@ -55,7 +55,7 @@ - I used "safe" in large part this is what has been used by other + I used "safe" in large part because this is what has been used by other similar libraries. Maybe a better word might have been "correct" but that would raise similar concerns. I'm not inclined to change this. I've tried to make it clear in the documentation what the problem that @@ -71,7 +71,7 @@ Actually, I believe that this can/should be applied to any type - T which satisfies the type requirement "Numeric" type as defined in + T which satisfies the type requirement Numeric type as defined in the documentation. So there should be specializations safe<float> and related types as well as new types like safe<fixed_decimal> etc. But the current @@ -83,7 +83,7 @@ Isn't putting a defensive check just before any potential - undefined behavior is often considered a bad practice? + undefined behavior often considered a bad practice? @@ -104,7 +104,7 @@ As far as is known as of this writing, the library does not - presume that the underlying hardware is two's compliment. However, + presume that the underlying hardware is two's complement. However, this has yet to be verified in a rigorous way. @@ -118,7 +118,7 @@ safe<T> behaves like a "number" just as int does. It has max, min, etc Any code which uses numeric limits to test - a type T should works with safe<T>. + a type T should work with safe<T>. safe<T> is a drop-in replacement for T so it has to implement all the operations. @@ -141,7 +141,7 @@ arithmetic is not intended, such as array indexes. Finally, the modulus for such an integer would vary depending upon the machine architecture. For these reasons, in the context of this library, an - unsigned integer is considered to a representation of a subset of + unsigned integer is considered to be a representation of a subset of integers. Note that this decision is consistent with INT30-C, “Ensure that unsigned integer operations do not wrap” in the CERT C Secure Coding Standard @@ -163,7 +163,7 @@ meta-programming. But this wasn't enough. It became apparent that the only way to really minimize run-time penalty was to implement compile-time integer range arithmetic - a pretty elaborate sub - library. By doing range arithmetic at compiler-time, I could skip + library. By doing range arithmetic at compile-time, I could skip runtime checking on many/most integer operations. C++11 constexpr wasn't quite enough to do the job. C++14 constexpr can do the job. The library currently relies very heavily on C++14 constexpr. I think that @@ -180,7 +180,7 @@ - C++ has evolved way beyond the original C language. But C++ it's + C++ has evolved way beyond the original C language. But C++ is still (mostly) compatible with C. So most C programs can be compiled with a C++ compiler. The problems of incorrect arithmetic afflict both C and C++. Suppose we have a legacy C program designed for some @@ -207,7 +207,7 @@ using pic16_promotion = boost::numeric::cpp< 16, // long 32 // long long >; -// define safe types used desktop version of the program. +// define safe types used in the desktop version of the program. template <typename T> // T is char, int, etc data type using safe_t = boost::numeric::safe< T, @@ -231,7 +231,7 @@ typedef long int_least16_t; - Run the tests and change code to address any thrown + Run the tests and change the code to address any thrown exceptions. diff --git a/doc/boostbook/integer_concept.xml b/doc/boostbook/integer_concept.xml index ec261c8..b5fc8b1 100644 --- a/doc/boostbook/integer_concept.xml +++ b/doc/boostbook/integer_concept.xml @@ -9,10 +9,10 @@
Description - A type is fulfills the requirements of an Integer if it has the + A type fulfills the requirements of an Integer if it has the properties of a integer. - More specifically, a type T is Integer if there exists + More specifically, a type T is Integer if there exists a specialization of std::numeric_limits<T> for which std::numeric_limits<T>.is_integer is equal to true. See the documentation for standard library class @@ -20,7 +20,7 @@ specializations for all built-in numeric types. Note that this concept is distinct from the C++ standard library type traits is_integral and is_arithmetic. These latter - fulfill the requirement of the concept Numeric. But there are types which + fulfill the requirements of the concept Numeric. But there are types which fulfill this concept for which is_arithmetic<T>::value == false. For example see safe<int>.
@@ -134,7 +134,7 @@ V - and of t and u padded out max # bits in t, u + and of t and u padded out to max # bits in t, u @@ -142,7 +142,7 @@ V - or of t and u padded out max # bits in t, u + or of t and u padded out to max # bits in t, u @@ -150,7 +150,7 @@ V - exclusive or of t and u padded out max # bits in t, + exclusive or of t and u padded out to max # bits in t, u diff --git a/doc/boostbook/interval.xml b/doc/boostbook/interval.xml index fbeb050..e132782 100644 --- a/doc/boostbook/interval.xml +++ b/doc/boostbook/interval.xml @@ -16,7 +16,7 @@
Template Parameters - R must model the type requirements R must model the type requirements ofNumeric
@@ -59,7 +59,7 @@ l, u - lower and upper Numeric limits of an interval + Lower and upper Numeric limits of an interval @@ -103,7 +103,7 @@
Valid Expressions - Note that all expressions are constexpr . + Note that all expressions are constexpr. diff --git a/doc/boostbook/native.xml b/doc/boostbook/native.xml index 023d4c1..7f0d7d3 100644 --- a/doc/boostbook/native.xml +++ b/doc/boostbook/native.xml @@ -8,7 +8,7 @@ Description This type contains the functions to return a safe type corresponding - to the C++ type resulting for a given arithmetic operation. + to the C++ type resulting from a given arithmetic operation. Usage of this policy with safe types will produce the exact same arithmetic results that using normal unsafe integer types will. Hence this @@ -70,12 +70,12 @@ int main(){ safe_int8 x = 127; safe_int8 y = 2; safe_int8 z; - // rather than producing and invalid result an exception is thrown + // rather than producing an invalid result an exception is thrown z = x + y; assert(false); // never arrive here } catch(std::exception & e){ - // which can catch here + // which we can catch here std::cout << e.what() << std::endl; } @@ -84,7 +84,7 @@ int main(){ safe_int8 x = 127; safe_int8 y = 2; safe<int, native> z; // z can now hold the result of the addition of any two 8 bit numbers - z = x + y; // is guarenteed correct without any runtime overhead or interrupt. + z = x + y; // is guaranteed correct without any runtime overhead or exception. return 0; } diff --git a/doc/boostbook/no_exception_support.xml b/doc/boostbook/no_exception_support.xml index f6f3e6b..808e2cf 100644 --- a/doc/boostbook/no_exception_support.xml +++ b/doc/boostbook/no_exception_support.xml @@ -22,7 +22,7 @@ Template Parameters Function objects to be invoked are specified for each error - condition are specified via template parameters. + condition via template parameters. diff --git a/doc/boostbook/numeric_concept.xml b/doc/boostbook/numeric_concept.xml index 42145bf..1ddbc9c 100644 --- a/doc/boostbook/numeric_concept.xml +++ b/doc/boostbook/numeric_concept.xml @@ -11,9 +11,9 @@ A type is Numeric if it has the properties of a number. - More specifically, a type T is Numeric if there exists + More specifically, a type T is Numeric if there exists a specialization of std::numeric_limits<T>. See the - documentation for standard library class numeric_limits. The + documentation for the standard library class numeric_limits. The standard library includes such specializations for all the built-in numeric types. Note that this concept is distinct from the C++ standard library type traits is_integral and @@ -36,13 +36,13 @@ T, U, V - A type that is a model of the Numeric + A type that is a model of Numeric t, u - An object of type modeling Numeric + An object of a type modeling Numeric diff --git a/doc/boostbook/safe.xml b/doc/boostbook/safe.xml index 79c2a01..99c6479 100644 --- a/doc/boostbook/safe.xml +++ b/doc/boostbook/safe.xml @@ -4,14 +4,14 @@
safe<T, PP, EP> - +
Description 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. + an arithmetically correct value or to trap in some way.
@@ -35,7 +35,8 @@ T - Underlying type from on which safe type based + Underlying type from which a safe type is being + derived @@ -98,7 +99,7 @@ Integer<T> - The underlying type. Currently only integer types + The underlying type. Currently only integer types are supported @@ -143,7 +144,7 @@ Implements all expressions and only those expressions defined by the SafeNumeric type - requirements. This, the result type of such an expression will be another + requirements. Thus, the result type of such an expression will be another safe type. The actual type of the result of such an expression will depend upon the specific promotion policy template parameter.
@@ -169,7 +170,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 constituent types of the operation. Here we refer to these rules a + the constituent types of the operation. Here we refer to these rules as "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 @@ -179,9 +180,9 @@
As a Drop-in replacement for standard integer types. - The following program will throw an exception and emit a error - message at runtime if any of several events result in incorrect - arithmetic results. Behavior of this program could vary according to the + The following program will throw an exception and emit an error + message at runtime if any of several events result in an incorrect + arithmetic type. Behavior of this program could vary according to the machine architecture in question. #include <exception> @@ -219,27 +220,24 @@ int main(){ wants to switch between safe and built-in types via an alias, this type of code will have to be fixed so that implicit conversions to built-in types do not occur. In our view, this will be a net improvement to the - code in any case. Remember that explicit variable casts *(via - static_cast) are allowed but the casting is guaranteed not to alter the - arithmetic value of the variable. + code in any case.
Guarantee correct behavior at compile time. - In some instance catching an error at run time is not sufficient. + In some instances, 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 - exception policy. + the trap_exception exception policy rather than the default + throw_exception policy. The following program will emit a compile error at any statement which might possibly result in incorrect behavior. - This is because there is no way to guarantee that the expression - i * i will return an arithmetically correct result. Since - we know that the program cannot compile if there is any possibility of - arithmetic error, we can dispense with the exception handling used - above. + This is because there is no way to guarantee that the expression i + * i will return an arithmetically correct result. Since we know that the + program cannot compile if there is any possibility of arithmetic error, + we can dispense with the exception handling used above. #include <iostream> #include <boost/numeric/safe.hpp> @@ -260,15 +258,14 @@ void f(){ types to larger sizes before doing the arithmetic. 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 + arithmetic wouldn't exist if the result 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 - accommodate the largest value that an expression can yield. All this - work is done at compile time so the runtime overhead is diminished. In - those cases where it's still possible to generate an incorrect result, - an exception still might be invoked. + non-default type promotion policy automatic. The policy stores 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 - no checking for exceptions is necessary (input is of course an + exception). The following example illustrates this. #include <boost/numeric/safe.hpp> #include <iostream> @@ -283,13 +280,6 @@ void f(){ std::numeric_limits<safe_int>::max() * std::numeric_limits<safe_int>::max() ); // always true } - - Here is an example where safe types are not drop-in replacements - for their underlying types. While reading the program, one must remember - that normal type promotion rules don't apply. That is, your program - might better reflect your true intentions and it might be less likely to - contain errors. But, strictly speaking, it's not really C++ any more. -
diff --git a/doc/boostbook/safe_introduction.xml b/doc/boostbook/safe_introduction.xml index 56d3207..36acb4c 100644 --- a/doc/boostbook/safe_introduction.xml +++ b/doc/boostbook/safe_introduction.xml @@ -79,9 +79,9 @@ applies to other operations such as subtraction, multiplication etc. . C/C++ often automatically and silently converts some integer types to others in the course of implementing binary operations and similar - problems occur in this case as well. Since the problems and their - solutions are similar, we'll confine the current discussion to just this - one example. + problems occur in this case as well. Since the problems and their solution + are similar, We'll confine the current discussion to just this one + example.
@@ -90,11 +90,10 @@ 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 to be arithmetically correct or to - invoke an error. Using this library, the above example would be rendered - as: + operations are guaranteed to be either arithmetically correct or invoke an + error. Using this library, the above example would be rendered as: - #include <boost/safe_numerics/safe_integer.hpp> + #include <boost/safe_numeric/safe_integer.hpp> using namespace boost::numeric; safe<int> f(safe<int> x, safe<int> y){ return x + y; // throw exception if correct result cannot be returned @@ -102,8 +101,8 @@ safe<int> f(safe<int> x, safe<int> y){ - Library code in this document resides in the name space - boost::numeric. This name space has generally been + Library code in this document resides in the namespace + boost::numeric. This namespace has generally been eliminated from text, code and examples in order to improve readability of the text. The addition expression is checked at runtime or (if possible) at @@ -215,10 +214,10 @@ safe<int> f(safe<int> x, safe<int> y){ - Enforce of other program requirements using ranged integer - types. The library includes types for safe ranges and safe - literals of signed and unsigned types. These types can be - used to improve program correctness and performance. + Enforce other program requirements using ranged integer + types. The library includes the types safe_range<Min, + Max> and safe_literal<N>. These types + can be used to improve program correctness and performance.
diff --git a/doc/boostbook/safe_literal.xml b/doc/boostbook/safe_literal.xml index fe6f6bc..34468ee 100644 --- a/doc/boostbook/safe_literal.xml +++ b/doc/boostbook/safe_literal.xml @@ -11,7 +11,7 @@ Description A safe type which holds a literal value. This is required to be able - to initialize other safe type in such a way that exception code is not + to initialize other safe types in such a way that an exception code is not generated. It is also useful when creating constexpr versions of safe types. It contains one immutable value known at compile time and hence can be used in any constexpr expression. @@ -115,7 +115,7 @@ Inherited Valid Expressions SafeLiteral types are immutable. Hence they only inherit those valid - expressions which don't change the value. The excludes assignment, + expressions which don't change the value. This excludes assignment, increment, and decrement operators. Other than that, they can be used anywhere a SafeNumeric type can be used. diff --git a/doc/boostbook/safe_numeric_concept.xml b/doc/boostbook/safe_numeric_concept.xml index 1842c3e..b0cbaf7 100644 --- a/doc/boostbook/safe_numeric_concept.xml +++ b/doc/boostbook/safe_numeric_concept.xml @@ -44,8 +44,7 @@ T, U Types fulfilling Numeric or Integer type + linkend="safe_numerics.numeric">Numeric type requirements
@@ -56,7 +55,7 @@ - S + S, S1, S2 A type fulfilling SafeNumeric type requirements @@ -70,20 +69,19 @@ op - C++ infix operator which is supported by underlying numeric - type + C++ infix operator prefix_op - C++ prefix operator: ++, --, -, +, ~ + C++ prefix operator: -, +, ~ postfix_op - C++ postfix operator: ++, -- + C++ postfix operator @@ -101,7 +99,7 @@ - + @@ -148,7 +146,7 @@ prefix_op S - S + unspecified S invoke safe C++ operator prefix_op and return another SafeNumeric type. @@ -157,7 +155,7 @@ S postfix_op - S + unspecified S invoke safe C++ operator postfix_op and return another SafeNumeric type. @@ -166,20 +164,20 @@ s assign_op t - S + S1 convert t to type S1 and assign it to s1. If the - value t cannot be represented as an instance of type S, it is an - error. + value t cannot be represented as an instance of type S1, it is + an error. S(t) - S + unspecified S - construct a instance of S from a value of type T. f - the value t cannot be represented as an instance of type S, it + construct an instance of S from a value of type T. If + the value t cannot be represented as an instance of type S1, it is an error. @@ -188,7 +186,7 @@ S - construct a uninitialized instance of + construct an uninitialized instance of S. @@ -202,57 +200,16 @@ requirements for a SafeNumeric type. - - get_promotion_policy<S>::type - - PP - - return the Promotion Policy associated with this - type. If there is no such policy associated with - this type, void shall be returned. - - - - get_exception_policy<S>::type - - EP - - return the Exception Policy associated with this - type. If there is no such policy associated with - this type, void shall be returned. - - - - base_type<S>::type - - Numeric - - return the underlying Numeric type on which this - Safe type is based. - - - - base_value(s) - - T - - return the underlying value of an instance of the - Safe type. - - static_cast<T>(s) T - convert the value of s to type T. In contrast to - base_value(s) described above, T may be any Numeric type. If the - value of s cannot be correctly represented as a type T, it is an - error which will be handled according to the Exception Policy of - the variable s. This is an explicit, checked cast. Implicit - casting from a safe type to a built-in integer type is expressly - prohibited and should invoke a compile time - error. + convert the value of s to type T. If the value of s + cannot be correctly represented as a type T, it is an error. + Note that implicit casting from a safe type to a built-in + integer type is expressly prohibited and should invoke a compile + time error. @@ -260,7 +217,7 @@ - Result of any binary operation where one or both of the operands + The result of any binary operation where one or both of the operands is a SafeNumeric type is also a SafeNumeric type. @@ -270,19 +227,8 @@ - Binary expressions that are not assignments place similar - requirements on both Exception and Promotion policies of Safe - arguments. - - at least one argument must be a Safe type with a non void - policy. - - - - if both arguments are Safe types with non void policies, - the policies must be the same. - - + Binary expressions which are not assignments require that + promotion and exception policies be identical. @@ -295,9 +241,8 @@ int main(){ safe<long> y; f(y); // compile time error return 0; -}This behavior distinguishes a Safe type from its underlying - type T and prevents a safe<T> from being a - "drop-in" replacement for a T. +}This behavior prevents a safe<T> from + being a "drop-in" replacement for a T. @@ -305,20 +250,21 @@ int main(){
Complexity Guarantees - There are no complexity guarantees explicitly enforced here. Usage - of safe operations should add a fixed amount of time to compilation with - each usage. + + There are no complexity guarantees explicitly enforced here. + However, it would be very surprising if any implementation were to be more + complex that O(0);
Invariants - The fundamental requirement of a SafeNumeric type is that implements + The fundamental requirement of a SafeNumeric type is that it implements all C++ operations permitted on its base type in a way the prevents the return of an incorrect arithmetic result. Various implementations of this - concept may handle circumstances which produce such results differently - (throw exception, compile time trap, etc..) no implementation should - return an arithmetically incorrect result. + concept may handle circumstances which produce such results differently + (throw exception, compile time trap, etc..) no implementation should return + an arithmetically incorrect result.
diff --git a/doc/boostbook/safe_numerics.xml b/doc/boostbook/safe_numerics.xml index f922e76..eb16165 100644 --- a/doc/boostbook/safe_numerics.xml +++ b/doc/boostbook/safe_numerics.xml @@ -126,9 +126,9 @@ All unary/binary expressions where one of the operands is a - "safe" type are Overloaded. These overloads are declared and defined + "safe" type are overloaded. These overloads are declared and defined in the header file "safe_integer.hpp". SFINAE - "Substitution Failure - Is Not An Error and std::enable_if are key features of + Is Not An Error" and std::enable_if are key features of C++ used to define these overloads in a correct manner. @@ -143,12 +143,12 @@ Given the ranges of the operands, determine the range of the result of the operation using interval arithmetic. This is - implemented in the "interval.hpp" header file using constexpr + implemented in the "interval.hpp" header file using the constexpr facility of C++14. - if the range of the result type includes the range of the + If the range of the result type includes the range of the result of the operation, no run time checking of the result is necessary. So the operation reduces to the original built-in C/C++ operation. @@ -165,7 +165,7 @@ - if a valid result has been obtained, it is passed to the + If a valid result has been obtained, it is passed to the caller. @@ -192,10 +192,10 @@
Performance Tests - Our goal creating facilities which make it possible write programs + Our goal is to create facilities which make it possible to write programs known to be correct. But we also want programmers to actually use the facilities we provide here. This won't happen if using these facilities - impacts performance to a significant degree. Although we've take + impacts performance to a significant degree. Although we've taken precautions to avoid doing this, the only real way to know is to create and run some tests. diff --git a/doc/boostbook/safe_range.xml b/doc/boostbook/safe_range.xml index f27220a..bb2120a 100644 --- a/doc/boostbook/safe_range.xml +++ b/doc/boostbook/safe_range.xml @@ -14,7 +14,7 @@ [MIN, MAX]. A safe_signed_range<MIN, MAX, PP, EP> or safe_unsigned_range<MIN, MAX, PP, EP> can be used anywhere an arithmetic type is permitted. Any expression which uses either - of these types is guaranteed to return an arithmetically correct value or + of these types is guaranteed to return an arithmetically correct value or to trap in some way.
@@ -39,9 +39,8 @@ MIN, MAX - Minimum and maximum Numeric values that the - range can represent. + Minimum and maximum values that the range can + represent. @@ -61,17 +60,15 @@ PP - Promotion - Policy. A type which specifies the result type of an - expression using safe types. + Promotion Policy. A type which specifies the result type of + an expression using safe types. EP - Exception - Policy. A type containing members which are called when a - correct result cannot be returned + Exception Policy. A type containing members which are + called when a correct result cannot be returned @@ -103,19 +100,19 @@ MIN - must be Numeric literal + must be a non-negative literal - The minimum maximum value that this type may hold + The minimum non-negative integer value that this type may + hold MAX - must be a Numeric literal + must be a non-negative literal - The maximum lowest value that this type may hold + The maximum non-negative integer value that this type may + hold @@ -165,7 +162,7 @@ Implements all expressions and only those expressions defined by the SafeNumeric type - requirements. This, the result type of such an expression will be another + requirements. Thus, the result type of such an expression will be another safe type. The actual type of the result of such an expression will depend upon the specific promotion policy template parameter.
@@ -180,12 +177,11 @@
Example of use - #include <safe/numerics/safe_range.hpp> // safe_unsigned range -#include <safe/numerics/safe_integer.hpp> // safe + #include <safe/numeric/safe_range.hpp> void f(){ using namespace boost::numeric; - safe_unsigned_range<7, 24> i; + safe_unsigned_range<7, 24> i // since the range is included in [0,255], the underlying type of i // will be an unsigned char. i = 0; // throws out_of_range exception @@ -197,9 +193,9 @@ void f(){ // the range of i is [7, 24] and the range of j is [0,255] // if either or both types are safe types, the result is a safe type - // determined by promotion policy. - // With the default native promotion policy k will be safe<unsigned int> - static_assert(std::is_same<decltype(k), safe<unsigned int>>); + // determined by promotion policy. With the default native promotion policy + // k will be safe<unsigned int> + static_assert(std::is_same<decltype(k), safe<unsigned int>); }
diff --git a/doc/boostbook/throw_exception.xml b/doc/boostbook/throw_exception.xml index 9430e5e..c0458fe 100644 --- a/doc/boostbook/throw_exception.xml +++ b/doc/boostbook/throw_exception.xml @@ -7,7 +7,7 @@
Description - This exception policy throws a an exception derived from + This exception policy throws an exception derived from std::exception any time some operation would result in an incorrect result. This is the default exception handling policy.
@@ -31,7 +31,7 @@ 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 + for safe types. Hence it usually is not necessary to request it explicitly. #include "../../include/safe_integer.hpp" diff --git a/doc/boostbook/trap_exception.xml b/doc/boostbook/trap_exception.xml index 0249aa2..d46d67b 100644 --- a/doc/boostbook/trap_exception.xml +++ b/doc/boostbook/trap_exception.xml @@ -7,7 +7,7 @@
Description - This exception policy will trap at compile time any operation which + This exception policy will trap at compile time any operation that 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 @@ -32,8 +32,6 @@
Example of use - The following - #include "../../include/safe_integer.hpp" #include "../../include/automatic.hpp" #include "../../include/exception_policies.hpp" diff --git a/doc/boostbook/tutorial.xml b/doc/boostbook/tutorial.xml index 66f3d90..4eb3fd9 100644 --- a/doc/boostbook/tutorial.xml +++ b/doc/boostbook/tutorial.xml @@ -15,15 +15,12 @@ sense. This is called "overflow". Since word size can differ between machines, code which produces mathematically correct results in one set of circumstances may fail when re-compiled on a machine with different - hardware. - - When this occurs in a C++ program, the program will usually continue - to execute with no indication that the results are wrong. It is the - programmer's responsibility to write his code so as to ensure such - undefined behavior is avoided. + hardware. When this occurs, Most C++ compilers will continue to execute + with no indication that the results are wrong. It is the programmer's + responsibility to ensure such undefined behavior is avoided. This program demonstrates this problem. The solution is to replace - instances of type int with safe<int> + instances of int type with safe<int> type. Arithmetic Operations Can Overflow Silently A variation of the above is when a value is incremented/decremented - beyond it's domain. + beyond its domain. This is a common problem with for loops. @@ -47,9 +44,8 @@
Implicit Conversions Can Lead to Erroneous Results - At CPPCon 2016 Jon Kalb gave a very entertaining (and disturbing - example) lightening talk + At CPPCon 2016 Jon Kalb gave a very entertaining (and disturbing) lightning talk related to C++ expressions. The talk included a very, very simple example similar to the @@ -61,7 +57,7 @@ A normal person reads the above code and has to be dumbfounded by this. The code doesn't do what the text - according to the rules of algebra - says it does. But C++ doesn't follow the rules of algebra - it - has it's own rules. There is generally no compile time error. You can get + has its own rules. There is generally no compile time error. You can get a compile time warning if you set some specific compile time switches. The explanation lies in reviewing how C++ reconciles binary expressions (a < b is an expression here) where operands are different @@ -70,7 +66,7 @@ Determines the "best" common type for the two operands. In - this case, application the the rules in the C++ standard dictate + this case, application of the rules in the C++ standard dictate that this type will be an unsigned int. @@ -82,7 +78,7 @@ - Performs the calculation - in this case it's, + Performs the calculation - in this case it's <, the "less than" operation. Since 1 is less than 4294967295 the program prints "b is less than a". @@ -92,21 +88,21 @@ should be pretty familiar with the implicit conversion rules of the C++ standard. These are available in a copy of the standard and also in the canonical reference book The C++ - Programming Language . (both are over 1200 pages + Programming Language (both are over 1200 pages long!). Even experienced programmers won't spot this issue and know to take precautions to avoid it. And this is a relatively easy one to spot. In the more general case this will use integers which don't correspond to easily recognizable numbers and/or will be buried as a part of some more complex expression. - This example generated a good amount of web traffic along with every - one's pet suggestions. See for example This example generated a good amount of web traffic along with + everyone's pet suggestions. See for example a blog - post with every one's favorite "solution". All the proposed + post with everyone's favorite "solution". All the proposed "solutions" have disadvantages and attempts to agree on how handle this are ultimately fruitless in spite of, or maybe because of, the emotional - content. Our solution is by far the simplest: Just use the safe + content. Our solution is by far the simplest: just use the safe numerics library as shown in the example above. Note that in this particular case, there is absolutely no extra @@ -134,7 +130,7 @@ Not using safe numerics Using safe numerics 10000 detected error:converted negative value to unsigned -This solution is simple, Just replace instances of the +This solution is simple, just replace instances of int with safe<int>.
@@ -148,17 +144,12 @@ detected error:converted negative value to unsigned Collections - like standard arrays, vectors do array index checking in some function + like standard arrays and 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 guarantee that under no + assigning legal ranges to variables. This will guarantee that under no circumstances will the variable contain a value outside of the specified - range. Creating the bounds type can also be somewhat error-prone. It would - be quite easy for C++ programmers who are used to half-open ranges to use - safe_unsigned_range<0, i_array.size()>. On the other hand it would - certainly be easy to create a simple function - safe_array_bounds<T> which would return guaranteed - correct array bounds. + range.
@@ -191,7 +182,7 @@ detected error:converted negative value to unsigned - invocation some configuration functions which convert these + invocation of some configuration functions which convert these hardware events into C++ exceptions It's not all that clear how one would detect and recover @@ -199,7 +190,7 @@ detected error:converted negative value to unsigned ignore the issue which usually results in immediate program termination when this situation occurs. - This library will detect for divide by zero errors before the + This library will detect divide by zero errors before the operation is invoked. Any errors of this nature are handled according to the ErrorPolicy selected by the library user. @@ -211,8 +202,8 @@ detected error:converted negative value to unsigned Programming by Contract is Too Slow Programming by Contract is a highly regarded technique. There has - been much written about it and it has been proposed as an addition to the - C++ language GarciaCrowl & + been much written about it and it has been proposed as an addition to the C++ + language GarciaCrowl & 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 @@ -224,18 +215,18 @@ detected error:converted negative value to unsigned runtime cost. The Safe Numerics Library has facilities which, in many cases, can - check guarantee parameter requirements with little or no runtime overhead. + check guaranteed parameter requirements with little or no runtime overhead. Consider the following example: - In the example above the function convert incurs significant runtime + 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. 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. + is moved to the 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.
diff --git a/examples/example1.cpp b/examples/example1.cpp index c1a1365..0c6cd5f 100644 --- a/examples/example1.cpp +++ b/examples/example1.cpp @@ -15,10 +15,7 @@ int main(int argc, const char * argv[]){ int 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); - std::cout << static_cast(z) << " != " << x + y << std::endl; + std::cout << z << " != " << x + y << std::endl; std::cout << "error NOT detected!" << std::endl; } catch(std::exception){ @@ -31,11 +28,11 @@ int main(int argc, const char * argv[]){ safe x = INT_MAX; safe y = 2; safe z; - // rather than producing and invalid result an exception is thrown + // rather than producing an invalid result an exception is thrown z = x + y; } catch(std::exception & e){ - // which can catch here + // which we can catch here std::cout << e.what() << std::endl; } return 0; diff --git a/examples/example13.cpp b/examples/example13.cpp index 35cbac8..2e3ce16 100644 --- a/examples/example13.cpp +++ b/examples/example13.cpp @@ -4,9 +4,9 @@ #include "../include/safe_integer.hpp" int main(int argc, const char * argv[]){ - // problem: checking of externally produced value can be overlooked + // problem: cannot recover from arithmetic errors std::cout << "example 7: "; - std::cout << "cannot recover From arithmetic errors" << std::endl; + std::cout << "cannot recover from arithmetic errors" << std::endl; std::cout << "Not using safe numerics" << std::endl; try{ @@ -21,7 +21,7 @@ int main(int argc, const char * argv[]){ std::cout << "error detected!" << std::endl; } - // solution: assign externally retrieved values to safe equivalents + // solution: replace int with safe std::cout << "Using safe numerics" << std::endl; try{ using namespace boost::numeric; diff --git a/examples/example4.cpp b/examples/example4.cpp index 1ff4f22..39d17e6 100644 --- a/examples/example4.cpp +++ b/examples/example4.cpp @@ -24,7 +24,7 @@ int main(){ std::cout << "error detected!" << std::endl; } - // solution: replace int with safe and char with safe + // solution: replace int with safe and unsigned int with safe std::cout << "Using safe numerics" << std::endl; try{ using namespace boost::numeric; diff --git a/examples/example6.cpp b/examples/example6.cpp index 4ed945f..db53d6e 100644 --- a/examples/example6.cpp +++ b/examples/example6.cpp @@ -22,7 +22,7 @@ int main(int argc, const char * argv[]){ std::cout << "error detected!" << std::endl; } - // solution: asign externally retrieved values to safe equivalents + // solution: assign externally retrieved values to safe equivalents std::cout << "Using safe numerics" << std::endl; { using namespace boost::numeric; diff --git a/examples/example84.cpp b/examples/example84.cpp index c017f71..2467d2d 100644 --- a/examples/example84.cpp +++ b/examples/example84.cpp @@ -16,7 +16,7 @@ using safe_t = safe_signed_range< trap_exception >; -// define variables use for input +// define variables used for input using input_safe_t = safe_signed_range< -24, 82,