diff --git a/CMake/CMakeLists.txt b/CMake/CMakeLists.txt index 5075363..d2afe82 100644 --- a/CMake/CMakeLists.txt +++ b/CMake/CMakeLists.txt @@ -116,18 +116,15 @@ link_directories("${Boost_LIBRARY_DIRS}") ########################### ########################### -# testing and submitting test results +# testing and submitting test results to the test dashboard include (CTest) if(0) -## This file should be placed in the root directory of your project. -## Then modify the CMakeLists.txt file in the root directory of your -## project to incorporate the testing dashboard. -## # The following are required to uses Dart and the Cdash dashboard -## ENABLE_TESTING() -## INCLUDE(CTest) +## Create a file named CTestConfig.cmake adjacent to the current file. +## This new file should contain the following: + set(CTEST_PROJECT_NAME "Safe Numerics") set(CTEST_NIGHTLY_START_TIME "01:00:00 UTC") @@ -139,79 +136,52 @@ set(CTEST_DROP_SITE_CDASH TRUE) endif() +function( [arg1 [arg2 [arg3 ...]]]) + COMMAND1(ARGS ...) + COMMAND2(ARGS ...) + ... +endfunction() + +########################### +# test targets + # the "include (CTest)" above includes enable_testing() # so the following line isn't necessary # enable_testing() -message(STATUS "test_cast") -add_executable( test_cast ../test/test_cast.cpp ) -target_link_libraries( test_cast ${Boost_LIBRARIES} ) -add_test(NAME test_cast COMMAND test_cast) - -message(STATUS "test_add") -add_executable( test_add ../test/test_add.cpp ) -target_link_libraries( test_add ${Boost_LIBRARIES} ) -add_test(NAME test_add COMMAND test_add) - -message(STATUS "test_subtract") -add_executable( test_subtract ../test/test_subtract.cpp ) -target_link_libraries( test_subtract ${Boost_LIBRARIES} ) -add_test(NAME test_subtract COMMAND test_subtract) - -message(STATUS "test_multiply") -add_executable( test_multiply ../test/test_multiply.cpp ) -target_link_libraries( test_multiply ${Boost_LIBRARIES} ) -add_test(NAME test_multiply COMMAND test_multiply) - -message(STATUS "test_divide") -add_executable( test_divide ../test/test_divide.cpp ) -target_link_libraries( test_divide ${Boost_LIBRARIES} ) -add_test(NAME test_divide COMMAND test_divide) - -message(STATUS "test_modulus") -add_executable( test_modulus ../test/test_modulus.cpp ) -target_link_libraries( test_modulus ${Boost_LIBRARIES} ) -add_test(NAME test_modulus COMMAND test_modulus) - -message(STATUS "test_compare") -add_executable( test_compare ../test/test_compare.cpp) -target_link_libraries( test_compare ${Boost_LIBRARIES} ) -add_test(NAME test_compare COMMAND test_compare) - -message(STATUS "test_conversion") -add_executable( test_conversion ../test/test_conversion.cpp) -target_link_libraries( test_conversion ${Boost_LIBRARIES} ) -add_test(NAME test_conversion COMMAND test_conversion) - -message(STATUS "test0") -add_executable( test0 ../test/test.cpp) -target_link_libraries( test0 ${Boost_LIBRARIES} ) -add_test(NAME test0 COMMAND test) - -# examples -message(STATUS "example1") -add_executable( example1 ../examples/example1.cpp) -target_link_libraries( example1 ${Boost_LIBRARIES} ) -add_test(NAME example1 COMMAND example1) - -message(STATUS "example2") -add_executable( example2 ../examples/example2.cpp) -target_link_libraries( example2 ${Boost_LIBRARIES} ) -add_test(NAME example2 COMMAND example2) - -message(STATUS "example3") -add_executable( example3 ../examples/example3.cpp) -target_link_libraries( example3 ${Boost_LIBRARIES} ) -add_test(NAME example3 COMMAND example3) - -message(STATUS "example4") -add_executable( example4 ../examples/example4.cpp) -target_link_libraries( example4 ${Boost_LIBRARIES} ) -add_test(NAME example4 COMMAND example4) +file(GLOB test_list + RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}/../test/*.cpp" +) +foreach(file_path ${test_list}) + string(REPLACE "../test/" "" file_name ${file_path}) + string(REPLACE ".cpp" "" base_name ${file_name}) + message(STATUS ${base_name}) + add_executable(${base_name} ${file_path}) + target_link_libraries(${base_name} ${Boost_LIBRARIES} ) +endforeach(file_path) # end test targets #################### +########################### +# examples + +file(GLOB example_list + RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}/../examples/*.cpp" +) +foreach(file_path ${example_list}) + string(REPLACE "../examples/" "" file_name ${file_path}) + string(REPLACE ".cpp" "" base_name ${file_name}) + message(STATUS ${base_name}) + add_executable(${base_name} ${file_path}) + target_link_libraries(${base_name} ${Boost_LIBRARIES} ) +endforeach(file_path) + +# end examples targets +#################### + #################### # add include headers to IDE diff --git a/CMake/CTestConfig.cmake b/CMake/CTestConfig.cmake index da7fa7a..a0b5924 100644 --- a/CMake/CTestConfig.cmake +++ b/CMake/CTestConfig.cmake @@ -2,8 +2,6 @@ ## Then modify the CMakeLists.txt file in the root directory of your ## project to incorporate the testing dashboard. ## # The following are required to uses Dart and the Cdash dashboard -## ENABLE_TESTING() -## INCLUDE(CTest) set(CTEST_PROJECT_NAME "Safe Numerics") set(CTEST_NIGHTLY_START_TIME "01:00:00 UTC") diff --git a/doc/boostbook/numeric_concept.xml b/doc/boostbook/numeric_concept.xml index 6644b93..bb8abce 100644 --- a/doc/boostbook/numeric_concept.xml +++ b/doc/boostbook/numeric_concept.xml @@ -15,9 +15,9 @@ library includes such specializations for all the primitive numeric types. Note that this concept is distinct from the C++ standard library type traits is_integral and is_arithmetic. These - latter fullfill the requirement of the concept Numeric. But there are - types T which fullfill this concept for which - is_arithmetic<T>::value == false. For example see + latter fulfill the requirement of the concept Numeric. But there are types + T which fulfill this concept for which is_arithmetic<T>::value + == false. For example see safe_signed_integer<int>. @@ -65,7 +65,7 @@ std::numeric_limits<T> The numeric_limits class template provides a C++ program - with information about various properties of the implementation’s + with information about various properties of the implementation's representation of the arithmetic types. See C++ standard 18.3.2.2. @@ -180,7 +180,7 @@ T - predecrement + pre decrement @@ -188,7 +188,7 @@ T - preincrement + pre increment @@ -276,7 +276,7 @@ T - shift t right by ubits + shift t right by u bits @@ -308,7 +308,8 @@ bool - true if t greathan or equal to u, false otherwise + true if t greater than or equal to u, false + otherwise @@ -456,5 +457,7 @@ int, safe_signed_integer<int>, safe_signed_range<int>, etc. + + The definition of this concept diff --git a/doc/boostbook/overflow.xml b/doc/boostbook/overflow.xml index 7b5cb51..d702f6d 100644 --- a/doc/boostbook/overflow.xml +++ b/doc/boostbook/overflow.xml @@ -16,13 +16,12 @@
Description - If evironment supports C++ exceptions, this function throws the + If environment supports C++ exceptions, this function throws the exception . If the environment does not support C++ exceptions, the user should - implement this function and expect it to be called when appropriate. - Otherwise, function is implemented by the library so that it throws the - standard library exception std::out_of_range(msg). + implement this function and expect it to be called when + appropriate. boost/config.hpp defines BOOST_NO_EXCEPTIONS when the environment doesn't support exceptions. It is by checking for the diff --git a/doc/boostbook/safe.xml b/doc/boostbook/safe.xml index 01086ec..8dcb3dc 100644 --- a/doc/boostbook/safe.xml +++ b/doc/boostbook/safe.xml @@ -47,6 +47,12 @@ object of type safe<T> + + + su + + object of type safe<U> + @@ -92,10 +98,6 @@ Model of Numeric - - If the resulting type of the operation t op u is V, the resulting - type of the operations st op su, t op su and st op u will be - safe<V>;
@@ -113,7 +115,7 @@ Expression - Result + Result Type Description @@ -123,8 +125,8 @@ st op u - safe<decltype(t - op u)> + typeof(t op + u) op is any valid binary operator for type T @@ -133,8 +135,8 @@ t op su - safe<decltype(t - op u)> + typeof(t op + u) op is any valid binary operator for type T @@ -143,20 +145,21 @@ st op su - safe<decltype(t - op u)> + typeof(t op + u) op is any valid binary operator for type T - T * U + st(t) - typeof(T * - U) + safe<T> - The underlying integer type + construct a instance of safe<T> from + t diff --git a/doc/boostbook/safe_cast.xml b/doc/boostbook/safe_cast.xml index 99d3d5f..c094cb2 100644 --- a/doc/boostbook/safe_cast.xml +++ b/doc/boostbook/safe_cast.xml @@ -16,7 +16,9 @@ T safe_cast(const U & u); Converts one Numeric type to another. Throws an std::out_of_range exception if - such a conversion is not possible without changing the value. + such a conversion is not possible without changing the value. This + function is part of the implementation of the safe numerics library. It's + been made publicly because it might be useful in related contexts.
@@ -58,17 +60,11 @@ T safe_cast(const U & u);
Preconditions - The value of u must be representabl by the type T. If + The value of u must be representable by the type T. If this is not true, an std::out_of_range exception will be thrown.
-
- Complexity - - Constant - O(0). -
-
Header diff --git a/doc/boostbook/safe_compare.xml b/doc/boostbook/safe_compare.xml index 770b62a..e0fc432 100644 --- a/doc/boostbook/safe_compare.xml +++ b/doc/boostbook/safe_compare.xml @@ -35,8 +35,10 @@ bool safe_compare::not_equal(const T & lhs, const U & rhs); + check that an error is made in the conversion. This function guarantees a + correct result regardless of the types of the arguments. It's used in the + implementation of the safe numerics library. It has been made publicly + accessible in because it might be useful in other contexts.
diff --git a/doc/boostbook/safe_introduction.xml b/doc/boostbook/safe_introduction.xml index fd9190e..c18a751 100644 --- a/doc/boostbook/safe_introduction.xml +++ b/doc/boostbook/safe_introduction.xml @@ -7,18 +7,49 @@
Problem - Arithmetic operations in C++ are NOT guarenteed to yield a correct + Arithmetic operations in C++ are NOT guaranteed 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/C++ - programmer to guarentee that this behavior does not result in incorrect - behavior of the program. There are no language facilities which do this. - They have to be explicitly addressed in the program code. This is - exceeding tedious and laborious for a programmer to do. Besides, adding - this code would introduce another source of errors. + arithmetic operations exceeds this number of bits, the result will not be + arithmetically correct. The following example illustrates this + problem. + + int f(int x, int y){ + // this returns an invalid result for some legal values of x and y ! + return x + y; +} + + + It is incumbent up the C/C++ programmer to guarantee that this + 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. SeeINT32-C seems to recommend the following + approach. + + int f(int x, int y){ + if (((y > 0) && (x > (INT_MAX - y))) + || ((y < 0) && (x < (INT_MIN - x)))) { + /* Handle error */ + } + return x + y; +} + + + This will indeed trap the error. However, it would be tedious and + laborious for a programmer to do alter his code to do. Altering code in + this way for all arithmetic operations would likely render the code + unreadable and add another source of potential programming errors. This + approach is clearly not functional when the expression is even a little + more complex as is shown in the following example. + + int f(int x, int y, int z){ + // this returns an invalid result for some legal values of x and y ! + return x + y * z; +} +
@@ -26,11 +57,22 @@ This library implements special versions of int, unsigned, etc. which behave exactly like the original ones EXCEPT that the results of - these operations are checked to be sure any possible errors resulting from - undefined arithmetic behavior are trapped at compile time (if possible) or - at runtime. This will permit one to write arithmetic expressions without - resulting in an erroneous result. Instead, one and only one of the - following is guarenteed to occur. + these operations are guaranteed to be either arithmetically correct or + invoke an error. Using this library, the above would be rendered + as: + + #include <boost/safe_numeric/safe_integer.hpp> + +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) + 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. @@ -50,14 +92,37 @@ 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]. By using these types, on can guarentee correct - runtime behavior without the need for exception handling. + closed range [MIN, MAX]. +
+ +
+ Summary + + Using techniques of C++ including overloading, template + metaprogramming, and others, this 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 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. + + 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 it's + misleading to characterize this library as addressing undefined behavior + of C/C++ numeric expressions.
Requirements - This library is is composed entirely of C++ Headers. I requires a + This library is composed entirely of C++ Headers. I requires a compiler compatible with the C++11 standard. The following Boost Libraries must be installed in order to use this @@ -101,7 +166,8 @@ Analogous issues arise for floating point types but they are not currently addressed by this version of the library. User or Library defined types such as arbitrary precision integers can also have this problem. Extension - of this library to these other types is not currently under - development + of this library to these other types is not currently under development + but may be addressed in the future. This is one reason why the library + name is "safe numeric" rather than "safe integer" library.
diff --git a/doc/boostbook/safe_numerics.xml b/doc/boostbook/safe_numerics.xml index 4cefda2..4a0afef 100644 --- a/doc/boostbook/safe_numerics.xml +++ b/doc/boostbook/safe_numerics.xml @@ -5,7 +5,7 @@ name="Safe Numerics"> Safe Numerics - + Robert @@ -19,12 +19,14 @@ - http://www.boost.org/LICENSE_1_0.txt">Subject to Boost Software License + http://www.boost.org/LICENSE_1_0.txt">Subject + to Boost Software License Safe integer operations - Numerics + Numerics It was a lot of code in one header - 6400 lines. Very unwieldy - to understand and modify. + to understand, modify and maintain. @@ -68,15 +70,7 @@ - The package I downloaded didn't have a test suite - - - - I believe the original SafeInt - library is not easily found. MSVC 10 has this built in so I seems that - they've decided to make it less attractive to use on other - systems. + I could find not test suite for the library. @@ -91,7 +85,7 @@
- Concepts + Type Requirements @@ -100,14 +94,14 @@
Types + + - -
@@ -123,6 +117,13 @@ xmlns:xi="http://www.w3.org/2001/XInclude"/>
+
+ Exception Safety + + All operations in this library are exception safe and meet the + strong guarantee. +
+
Rationale @@ -135,7 +136,7 @@ - There are a number of + There are a number of reasons: @@ -155,24 +156,129 @@ Why is there no policy driven design for handling - overflows - - The question was - to which type does one apply it to? - Consider the following example: - - safe<int, overflow_policy_1> t1 = 2; -safe<int, overflow_policy_2> t2 = 4; -unsigned int x = t1 - t2; // which policy should be invoked? + overflows? + + + This is planned for a future version of the library. However, + attempts to implement this idea have so far failed as it turns out + to more technically challenging than one would think. Rather than + wait for a future event that might never happen, it was decided to + release the library without this feature. + Why is Boost.Convert not used. + + I couldn't figure out how to use it from the documentation. + + + + + + Why is the library named "safe ..." rather than something like + "checked ..." ? + + + I used "safe" in large part 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 the library addressed is + + + + + + Given that the library is called "numerics" why is floating + point arithmetic not addressed? + + + + Actually, I believe that this can/should be applied to any + type T which satisfies the type requirement "Numeric" type as + defined in the documentation. So there should be specializations + safe<float> et. al. and eventually safe<fixed_decimal> + etc. But the current version of the library only addresses integer + types. Hopefully the library will evolve to match the promise + implied by it's name. + + + + + + Isn't putting a defensive check just before any potential UB, + is often considered a bad practice? + + + + By whom? Is leaving code which can produce incorrect results + better? Note that the documentation contains references to various + sources which recommend exactly this approach to mitigate the + problems created by this C/C++ behavior. + + + + + + Why are safe_compare and safe_cast included in the library? + The don't really fit with the "drop-in" replacement idea for + built-in types. + + + + They are part of the implementation of the library. I thought + they would be useful outside the formal context of the library so I + make access to them public. + + + + + + It looks like it presumes two's complement arithmetic at the + hardware level. So this library is not portable - correct? What + about other hardware architectures? + + + + Correct. Almost all hardware in current use two's complement + arithmetic. This library works all and only on all such machines. + For example, if one were to implement 48 bit integers with two's + complement arithmetic, the save .. versions could be used without + change. To support hardware which does not use two's complement + arithmetic, at least part of the library would have to be + re-implemented. + + + + + + Why do you specialize numeric_limits for "safe" types? Do you + need it? + + + + 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>. safe<T> is a drop-in + replacement for T so it has to implement all the operations. + + + + + + + + + + +
@@ -183,49 +289,220 @@ unsigned int x = t1 - t2; // which policy should be invoked?

This is the initial version.
-
- Acknowledgements +
+ Bibliography - David - LeBlanc's is the author of SafeInt3 - library which motivated this work -
+ + + Robert C. Seacord + -
- References + + <ulink + url="http://www.cert.org/secure-coding/publications/books/secure-coding-c-c-second-edition.cfm?">Secure + Coding in C and C++</ulink> + - - - David LeBlanc, Codeplex - SafeInt Page - + 2nd Edition - - Daniel Plakosh, Safe - Integer Operations - + Addison-Wesley Professional - - Omer Katz, SafeInt - code proposal, Boost.SafeInt - + April 12, 2013 - - Software Engineering Institute, Carnegie Mellon, 978-0321822130 + + Seacord + + + + + Robert C. Seacord + + + + <ulink url="https://www.securecoding.cert.org/confluence/display/seccode/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow?showComments=false">INT32-C. Ensure that operations on signed integers do not result in - overflow</ulink></para> - </listitem> + overflow</ulink> + - - Will Dietz, Peng Li,y John Regehr,y and Vikram Adve, + Software Engineering Institute, + Carnegie Mellon University + + + August 17, 2014 + + INT32-C + + + + + Daniel Plakosh + + + + <ulink + url="https://buildsecurityin.us-cert.gov/bsi/articles/knowledge/coding/312-BSI.html">Safe + Integer Operations</ulink> + + + + U.S. Department of + Homeland Security + + + May 10, 2013 + + Plakosh + + + + + Will Dietz + + + + Peng Li + + + + John Regehr + + + + Vikram Adve + + + + <ulink url="http://www.cs.utah.edu/~regehr/papers/overflow12.pdf">Understanding - Integer Overflow in C/C++</ulink></para> - </listitem> - </orderedlist> + Integer Overflow in C/C++</ulink> + + + + Proceedings + of the 34th International Conference on Software Engineering (ICSE), + Zurich, Switzerland + + + June 2012 + + + + + Lawerence Crowl + + + + <ulink url="http://www.cert.org/secure-coding/publications/books/secure-coding-c-c-second-edition.cfm?"> + <ulink + url="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3352.html">C++ + Binary Fixed-Point Arithmetic</ulink> + </ulink> + + + + JTC1/SC22/WG21 - + The C++ Standards Committee - ISOCPP + + + January 15, 2012 + + Crowl + + + + + Forum Posts + + + + <ulink url="http://www.cert.org/secure-coding/publications/books/secure-coding-c-c-second-edition.cfm?"> + <ulink + url="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3352.html">C++ + Binary Fixed-Point Arithmetic</ulink> + </ulink> + + + + ISO + C++ Standard Future Proposals + + + Forum + + + Posts of various authors regarding proposal to add safe integer + to C++ standard libraries + + + + + + David LeBlanc + + + + <ulink + url="https://msdn.microsoft.com/en-us/library/ms972705.aspx">Integer + Handling with the C++ SafeInt Class</ulink> + + + + Microsoft Developer Network + + + January 7, 2004 + + LeBlanc + + + + + David LeBlanc + + + + <ulink url="https://safeint.codeplex.com">SafeInt</ulink> + + + + CodePlex + + + Dec 3, 2014 + + LeBlanc + + + + + Omer Katz + + + + <ulink url="http://www.cert.org/secure-coding/publications/books/secure-coding-c-c-second-edition.cfm?"> + <ulink + url="http://boost.2283326.n4.nabble.com/SafeInt-code-proposal-td2663669.html">SafeInt + code proposal</ulink> + </ulink> + + + + Boost + Developer's List + + + Katz + + + Posts of various authors regarding a proposed SafeInt library + for boost + +
diff --git a/doc/boostbook/safe_signed_range.xml b/doc/boostbook/safe_signed_range.xml index 58d5393..25fd5b9 100644 --- a/doc/boostbook/safe_signed_range.xml +++ b/doc/boostbook/safe_signed_range.xml @@ -2,8 +2,8 @@
- safe_signed_range<boost::intmax_t MIN, - boost::intmax_tMAX> + safe_signed_range<boost::intmax_t MIN, boost::intmax_t + MAX>
Description @@ -13,6 +13,47 @@ in assigning an integer value outside of this range.
+
+ Notation + + + + + + + + + + Symbol + + Description + + + + + + T, U, V + + Types which model the Numeric concept + + + + t, u, v + + objects of types T + + + + ssr + + object of type safe_unsigned_range + + + + +
+
Template Parameters @@ -70,7 +111,7 @@ The usage of this type in an arithmetic expression will result in another type fulfilling the Numeric concept. + linkend="safe_numerics.numeric">Numeric concept. Operations on safe_signed_range will result in the same
diff --git a/doc/boostbook/safe_unsigned_range.xml b/doc/boostbook/safe_unsigned_range.xml index 6221bb7..cd63525 100644 --- a/doc/boostbook/safe_unsigned_range.xml +++ b/doc/boostbook/safe_unsigned_range.xml @@ -2,14 +2,56 @@
- safe_unsigned_range<MIN, MAX> + safe_unsigned_range<boost::uintmax_t MIN, boost::uintmax_t + MAX>
Description This type holds a integer in the range [MIN, MAX]. It will throw a std::out_of_range exception for any operation which would - result in assigning an integer value outside of this range. + result in assigning an integer value outside of this range. +
+ +
+ Notation + + + + + + + + + + Symbol + + Description + + + + + + T, U, V + + Types which model the Numeric concept + + + + t, u, v + + objects of types T + + + + sur + + object of type safe_unsigned_range + + + +
@@ -37,7 +79,7 @@ MIN - must be a positive integer literal + must be a non-negative integer literal The minimum integer value that this type may hold @@ -53,9 +95,7 @@ - MIN < MAX - - + MIN <= MAX @@ -71,7 +111,73 @@ unsigned type will result in another unsigned type fulfilling the Numeric concept. This will be the smallest unsigned integer type of sufficient size to hold the result of - the operation. + the operation. +
+ +
+ Valid Expressions + + + + + + + + + + + + Expression + + Result Type + + Description + + + + + + st op u + + typeof(t op + u) + + op is any valid binary operator for type + T + + + + t op su + + typeof(t op + u) + + op is any valid binary operator for type + T + + + + st op su + + typeof(t op + u) + + op is any valid binary operator for type + T + + + + st(t) + + safe<T> + + construct a instance of safe<T> from + t + + + +
diff --git a/doc/boostbook/tutorial.xml b/doc/boostbook/tutorial.xml index 05adacb..496524d 100644 --- a/doc/boostbook/tutorial.xml +++ b/doc/boostbook/tutorial.xml @@ -2,11 +2,10 @@
- Tutorial + Tutorial and Motivating Examples
- Problem: Arithmetic operations can yield in correct - results. + Arithmetic operations 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 @@ -14,7 +13,7 @@ produces correct results in one set of circumstances may fail when re-compiled on a machine with different hardware. When this occurs, Most C++ compilers will continue to execute with no indication that the results - are wrong. It is the programmer's responsabiity to ensure such undefined + 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 @@ -26,7 +25,17 @@
- Problem: Undetected overflow + Undetected overflow + + A variation of the above is when a value is incremented/decremented + beyond it's domain. This is a common problem with for loops. + + +
+ +
+ Undetected underflow A variation of the above is when a value is incremented/decremented beyond it's domain. This is a common problem with for loops. @@ -35,41 +44,63 @@ parse="text" xmlns:xi="http://www.w3.org/2001/XInclude"/>
-
- Problem: Implicit conversions change data values +
+ Implicit conversions change data values - A simple assign or arithment expression will generally convert all - the terms to the same type. Sometimes this can silently change values. For - example, when a signed data variable contains a negative type, assigning - to a unsigned type will be permitted by any C/C++ compiler but will be - treated as large unsigned value. Most modern compilers will emit a compile - time warning when this conversion is performed. The user may then decide - to change some data types or apply a static_cast. This is - less than satisfactory for two reasons: + A simple assignment or arithmetic expression will generally convert + all the terms to the same type. Sometimes this can silently change values. + For example, when a signed data variable contains a negative type, + assigning to a unsigned type will be permitted by any C/C++ compiler but + will be treated as large unsigned value. Most modern compilers will emit a + compile time warning when this conversion is performed. The user may then + decide to change some data types or apply a static_cast. This + is less than satisfactory for two reasons: - It may be unwield to change all the types to signed or + It may be unwieldy to change all the types to signed or unsigned. - Litering one's program with static_cast + Littering one's program with static_cast makes it more difficult to read. We may believe that our signed type will never contain a negative value. If we use a static_cast to suppress the - warning, we'll fail to detect a program error when it is commited. + warning, we'll fail to detect a program error when it is committed. This is aways a risk with casts. This solution is the same as the above, Just replace instances of - the int with safe<int>. + the int with safe<int>. +
- +
+ Array index value can exceed array limits + + Using an intrinsic C++ array, it's very easy to exceed array limits. + This can fail to be detected when it occurs and create bugs which are hard + to find. There are several ways to address this, but one of the simplest + would be to use safe_unsigned_range; + + +
+ +
+ Checking of initialization values can be easily overlooked + + It's way too easy to overlook the checking of parameters received + from outside the current program.Without + safe integer, one will have to insert new code every time an integer + variable is retrieved. This is a tedious and error prone procedure.
diff --git a/doc/html/acknowledgements.html b/doc/html/acknowledgements.html index 74f2fdd..db48cea 100644 --- a/doc/html/acknowledgements.html +++ b/doc/html/acknowledgements.html @@ -7,7 +7,7 @@ - + @@ -15,23 +15,24 @@

Safe Numerics

-PrevUpHomeNext +PrevUpHomeNext

Acknowledgements

-

David - LeBlanc's is the author of SafeInt3 - library which motivated this work

+

David + LeBlanc is the author of SafeInt3 library which + motivated this work

-

-PrevUpHomeNext +PrevUpHomeNext
diff --git a/doc/html/change_log.html b/doc/html/change_log.html index bbabe14..e3951e7 100644 --- a/doc/html/change_log.html +++ b/doc/html/change_log.html @@ -24,7 +24,8 @@ -

diff --git a/doc/html/concepts.html b/doc/html/concepts.html index 3bee30c..709e57e 100644 --- a/doc/html/concepts.html +++ b/doc/html/concepts.html @@ -26,7 +26,7 @@ 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 @@ -41,9 +41,9 @@

-Notation

+Notation
-

Table 1. Notation

+

Table 1. Notation

@@ -65,9 +65,9 @@

-Associated Types

+Associated Types
-

Table 2. Associated Types

+

Table 2. Associated Types

@@ -86,13 +86,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 3. General

+

Table 3. General

@@ -122,7 +122,7 @@


-

Table 4. Unary Operators

+

Table 4. Unary Operators

@@ -174,7 +174,7 @@

-

Table 5. Binary Operators

+

Table 5. Binary Operators

@@ -330,21 +330,23 @@

-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/functions.html b/doc/html/functions.html index 546b511..cae2f0f 100644 --- a/doc/html/functions.html +++ b/doc/html/functions.html @@ -30,20 +30,20 @@ safe_cast<T, U>

-Synopsis

+Synopsis
template<class T, class U>
 T safe_cast(const U & u);

-Description

+Description

Converts one Numeric type to another. Throws an std::out_of_range exception if such a conversion is not possible without changing the value.

-Type requirements

+Type requirements
@@ -67,25 +67,25 @@ T safe_cast(const U & u);

-Preconditions

+Preconditions

The value of u must be representabl by the type T. If this is not true, an std::out_of_range exception will be thrown.

-Complexity

+Complexity

Constant - O(0).

-Header

+Header

#include <boost/numeric/safe_cast.hpp>

-Example of use

+Example of use
#include <boost/numeric/safe_cast.hpp> 
 #include <boost/numeric/safe_integer.hpp> 
 
@@ -106,7 +106,7 @@ void f(){
 safe_compare<T, U>
 

-Synopsis

+Synopsis

safe_compare is several functions:.

template<class T, class U>
 bool safe_compare::less_than(const T & lhs, const U & rhs);
@@ -128,7 +128,7 @@ bool safe_compare::not_equal(const T & lhs, const U & rhs);

-Description

+Description

With normal comparison operators, comparison of unsigned types to signed types will be done by converting the unsigned type to a signed type before comparing. Unfortunately this is not always possible. Most C++ @@ -138,7 +138,7 @@ bool safe_compare::not_equal(const T & lhs, const U & rhs);

-Type requirements

+Type requirements
@@ -162,13 +162,13 @@ bool safe_compare::not_equal(const T & lhs, const U & rhs);

-Header

+Header

#include <boost/numeric/safe_compare.hpp>

-Example of use

+Example of use
#include <boost/numeric/safe_compare.hpp>
 
 void f(){
@@ -191,14 +191,14 @@ void g(){
 overflow
 

-Synopsis

+Synopsis

This function is invoked by the library whenever it is not possible to produce a result for an arithmetic operation.

void overflow(char const * const msg);

-Description

+Description

If evironment supports C++ exceptions, this function throws the exception .

If the environment does not support C++ exceptions, the user should @@ -212,13 +212,13 @@ void g(){

-Header

+Header

#include <boost/safe_numerics/overflow.hpp>

-Example of use

+Example of use
#include <cstdio>
 
 void overflow(char const * const msg){
@@ -229,7 +229,7 @@ void overflow(char const * const msg){
 
 

-See Also

+See Also

See rationale for more information on this function

@@ -237,7 +237,8 @@ void overflow(char const * const msg){
-

diff --git a/doc/html/index.html b/doc/html/index.html index 0b3dd9c..98eaab1 100644 --- a/doc/html/index.html +++ b/doc/html/index.html @@ -22,7 +22,8 @@
diff --git a/doc/html/introduction.html b/doc/html/introduction.html index 194110a..6b4b4cc 100644 --- a/doc/html/introduction.html +++ b/doc/html/introduction.html @@ -7,7 +7,7 @@ - + @@ -21,43 +21,73 @@

Introduction

-Problem

+Problem

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/C++ - programmer to guarentee that this behavior does not result in incorrect - behavior of the program. There are no language facilities which do this. - They have to be explicitly addressed in the program code. This is - exceeding tedious and laborious for a programmer to do. Besides, adding - this code would introduce another source of errors.

+ arithmetic operations exceeds this number of bits, the result will not be + arithmetically correct. The following example illustrates this + problem.

+
int f(int x, int y){
+    // this returns an invalid result for some legal values of x and y !
+    return x + y;
+}
+
+

It is incumbent up the C/C++ programmer to guarentee that this + 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]

+
int f(int x, int y){
+    // this returns an invalid result for some legal values of x and y !
+    return x + y;
+}
+
+

+

This is exceeding tedious and laborious for a programmer to do. + Besides, adding this code will introduce another source of potential + errors.

-Solution

+Solution

This library implements special versions of int, unsigned, etc. which behave exactly like the original ones EXCEPT that the results of - these operations are checked to be sure any possible errors resulting from - undefined arithmetic behavior are trapped at compile time (if possible) or - at runtime.

-

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].

+ these operations are guarenteed to be either arithmetically correct or +

+

+

+

are checked to be sure any possible errors resulting from undefined + arithmetic behavior are trapped at compile time (if possible) or at + runtime. This will permit one to write arithmetic expressions without + resulting in an erroneous result. Instead, one and only one of the + following is guarenteed to occur.

+
    +
  • the expression will emit a compilation error.

  • +
  • the expression will invoke a runtime exception.

  • +
  • the expression will yield the correct mathematical + result

  • +
+

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]. By using these types, on can guarentee correct + runtime behavior without the need for exception handling.

-Requirements

+Requirements

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

The following Boost Libraries must be installed in order to use this @@ -72,10 +102,21 @@

  • integer traits

  • +
    +

    +Scope

    +

    This library currently applies only to built-in integer types. + Analogous issues arise for floating point types but they are not currently + addressed by this version of the library. User or Library defined types + such as arbitrary precision integers can also have this problem. Extension + of this library to these other types is not currently under + development

    +
    -

    diff --git a/doc/html/notes.html b/doc/html/notes.html index add7938..21a549b 100644 --- a/doc/html/notes.html +++ b/doc/html/notes.html @@ -6,7 +6,7 @@ - + @@ -50,7 +50,8 @@ -

    diff --git a/doc/html/rationale.html b/doc/html/rationale.html index 63722e6..9c5d7c4 100644 --- a/doc/html/rationale.html +++ b/doc/html/rationale.html @@ -21,15 +21,21 @@

    Rationale

    -
    -
    1. Why does a binary operation on two +
    +
    1. Why does a binary operation on two safe<int> values not return another safe type ?
    -
    2. Why is there no policy driven design for handling +
    2. Why is there no policy driven design for handling overflows
    -
    3. Why is Boost.Convert not used. +
    3. Why is Boost.Convert not used. +
    +
    4. +
    +
    5. +
    +
    6.
    @@ -38,7 +44,7 @@ + + + + + + + + + + + +
    -

    1.

    +

    1.

    Why does a binary operation on two safe<int> values not return another @@ -59,21 +65,21 @@

    -

    2.

    +

    2.

    Why is there no policy driven design for handling overflows

    -

    The question was - to which type does one apply it to? - Consider the following example:

    -
    safe<int, overflow_policy_1> t1 = 2;
    -safe<int, overflow_policy_2> t2 = 4;
    -unsigned int x = t1 - t2; // which policy should be invoked?
    +

    This is planned for a future version of the library. However, + attempts to implement this idea have so far failed as it turns out + to more technically challanging than one would think. Rather than + wait for a future event that might never happen, it was decided to + release the library without this feature.

    -

    3.

    +

    3.

    Why is Boost.Convert not used.

    @@ -81,13 +87,32 @@ unsigned int x = t1 - t2; // which policy should be invoked? documentation.

    +

    4.

    +

    +

    5.

    +

    +

    6.

    +

    -

    diff --git a/doc/html/references.html b/doc/html/references.html index 060ce9c..d238f03 100644 --- a/doc/html/references.html +++ b/doc/html/references.html @@ -19,23 +19,101 @@

    References

    -
      -
    1. David LeBlanc, Codeplex - SafeInt Page

    2. -
    3. Daniel Plakosh, Safe - Integer Operations

    4. -
    5. Omer Katz, SafeInt - code proposal, Boost.SafeInt

    6. -
    7. Software Engineering Institute, Carnegie Mellon, INT32-C. - Ensure that operations on signed integers do not result in - overflow

    8. -
    9. Will Dietz, Peng Li,y John Regehr,y and Vikram Adve, Understanding - Integer Overflow in C/C++

    10. -
    +
    +

    +Bibliography

    +
    +

    Omer Katz. + + SafeInt + code proposal + + . + Boost + Developer's List + . Katz

    +
    + +
    +

    Daniel Plakosh. + Safe + Integer Operations + . + U.S. Department of + Homeland Security + . May 10, 2013. Plakosh

    +
    +
    +

    David LeBlanc. + Integer + Handling with the C++ SafeInt Class + . + Microsoft Developer + Network + . January 7, 2004. LeBlanc

    +
    +
    +

    David LeBlanc. + Codeplex SafeInt + Page + . + CodePlexProject Hosting for + Open Source Software + . December 3, 2014. LeBlanc

    +
    + + +
    +

    Robert C. Seacord. + Secure + Coding in C and C++ + . 2nd Edition. Addison-Wesley Professional. April 12, 2013. 978-0321822130. Seacord

    +
    + +
    -

    diff --git a/doc/html/tutorial.html b/doc/html/tutorial.html index 66019cb..46ef8c5 100644 --- a/doc/html/tutorial.html +++ b/doc/html/tutorial.html @@ -1,7 +1,7 @@ -Tutorial +Tutorial and Motivating Examples @@ -19,17 +19,19 @@

    -Tutorial

    +Tutorial and Motivating Examples
    +

    -Problem: Arithmetic operations can yield in correct - results.

    +Arithmetic operations can yield in correct 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 @@ -46,23 +48,22 @@ #include <iostream> #include "../include/safe_integer.hpp" -//#include "../include/safe_compare.hpp" void detected_msg(bool detected){ std::cout << (detected ? "error detected!" : "error NOT detected! ") << std::endl; } int main(int argc, const char * argv[]){ + // problem: undetected erroneous expression evaluation std::cout << "example 1:"; std::cout << "undetected erroneous expression evaluation" << std::endl; std::cout << "Not using safe numerics" << std::endl; try{ - char x = 127; - char y = 2; - char z; + signed char x = 127; + signed char y = 2; + signed char z; // this produces an invalid result ! z = x + y; - // it is the wrong result !!! assert(z != 129); // but assert fails to detect it since C++ implicitly // converts variables to int before evaluating he expression! @@ -71,18 +72,18 @@ int main(int argc, const char * argv[]){ detected_msg(false); } catch(...){ - assert(false); // never arrive here + assert(false); // we never arrive here } // solution: replace char with safe<char> std::cout << "Using safe numerics" << std::endl; try{ using namespace boost::numeric; - safe<char> x = 127; - safe<char> y = 2; - safe<char> z; - // rather than producing and invalid result an exception is thrown + safe<signed char> x = 127; + safe<signed char> y = 2; + safe<signed char> z; + // rather than producing an invalid result an exception is thrown z = x + y; - assert(false); // never arrive here + assert(false); // we never arrive here } catch(std::range_error & e){ // which can catch here @@ -95,7 +96,7 @@ int main(int argc, const char * argv[]){

    -Problem: Undetected overflow

    +Undetected overflow

    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>
    @@ -103,14 +104,131 @@ int main(int argc, const char * argv[]){
     #include <iostream>
     
     #include "../include/safe_integer.hpp"
    -//#include "../include/safe_compare.hpp"
     
     void detected_msg(bool detected){
         std::cout << (detected ? "error detected!" : "error NOT detected! ") << std::endl;
     }
     
     int main(int argc, const char * argv[]){
    -    std::cout << "example 3: ";
    +    // solution: undetected overflow in data type
    +    std::cout << "example 2:";
    +    std::cout << "undetected overflow in data type" << std::endl;
    +    try{
    +        int x = INT_MAX;
    +        // the following silently produces an incorrect result
    +        ++x;
    +        std::cout << x << " != " << INT_MAX << " + 1" << std::endl;
    +        detected_msg(false);
    +    }
    +    catch(...){
    +        assert(false); // we never arrive here
    +    }
    +    // solution: replace int with safe<int>
    +    try{
    +        using namespace boost::numeric;
    +        safe<int> x = INT_MAX;
    +        // throws exception when result is past maximum possible 
    +        ++x;
    +        assert(false); // we never arrive here
    +    }
    +    catch(std::range_error & e){
    +        std::cout << e.what();
    +        detected_msg(true);
    +    }
    +    return 0;
    +}
    +
    + +
    +

    +Undetected underflow

    +

    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>
    +#include <stdexcept>
    +#include <iostream>
    +
    +#include "../include/safe_integer.hpp"
    +#include "../include/safe_compare.hpp"
    +
    +void detected_msg(bool detected){
    +    std::cout << (detected ? "error detected!" : "error NOT detected! ") << std::endl;
    +}
    +
    +int main(int argc, const char * argv[]){
    +    std::cout << "example 3:";
    +    std::cout << "undetected underflow in data type" << std::endl;
    +    std::cout << "Not using safe numerics" << std::endl;
    +    try{
    +        unsigned int x = 0;
    +        // the following silently produces an incorrect result
    +        --x;
    +        // because C/C++ implicitly converts mis-matched arguments to int
    +        // suggests that the operation is correct
    +        assert(x == -1);
    +        // even though it's not !!!
    +
    +        // however, safe_compare does detect the error
    +        assert(! boost::numeric::safe_compare::equal(x, -1));
    +        std::cout << x << " != " << -1;
    +        detected_msg(false);
    +    }
    +    catch(...){
    +        assert(false); // never arrive here
    +    }
    +    // solution: replace unsigned int with safe<unsigned int>
    +    std::cout << "Using safe numerics" << std::endl;
    +    try{
    +        using namespace boost::numeric;
    +        safe<unsigned int> x = 0;
    +        // decrement unsigned to less than zero throws exception
    +        --x;
    +        assert(false); // never arrive here
    +    }
    +    catch(std::range_error & e){
    +        std::cout << e.what();
    +        detected_msg(true);
    +    }
    +    return 0;
    +}
    +
    +
    +
    +

    +Implicit conversions change data values

    +

    A simple assign or arithment expression will generally convert all + the terms to the same type. Sometimes this can silently change values. For + example, when a signed data variable contains a negative type, assigning + to a unsigned type will be permitted by any C/C++ compiler but will be + treated as large unsigned value. Most modern compilers will emit a compile + time warning when this conversion is performed. The user may then decide + to change some data types or apply a static_cast. This is + less than satisfactory for two reasons:

    +
      +
    • It may be unwield to change all the types to signed or + unsigned.

    • +
    • Litering one's program with static_cast + makes it more difficult to read.

    • +
    • We may believe that our signed type will never contain a + negative value. If we use a static_cast to suppress the + warning, we'll fail to detect a program error when it is commited. + This is aways a risk with casts.

    • +
    +

    This solution is the same as the above, Just replace instances of + the int with safe<int>.

    +
    #include <cassert>
    +#include <stdexcept>
    +#include <iostream>
    +
    +#include "../include/safe_integer.hpp"
    +
    +void detected_msg(bool detected){
    +    std::cout << (detected ? "error detected!" : "error NOT detected! ") << std::endl;
    +}
    +
    +int main(int argc, const char * argv[]){
    +    // problem: implicit conversions change data values
    +    std::cout << "example 4: ";
         std::cout << "implicit conversions change data values" << std::endl;
         std::cout << "Not using safe numerics" << std::endl;
         try{
    @@ -141,71 +259,118 @@ int main(int argc, const char * argv[]){
     

    -Problem: Implicit conversions change data values

    -

    A simple assign or arithment expression will generally convert all - the terms to the same type. Sometimes this can silently change values. For - example, when a signed data variable contains a negative type, assigning - to a unsigned type will be permitted by any C/C++ compiler but will be - treated as large unsigned value. Most modern compilers will emit a compile - time warning when this conversion is performed. The user may then decide - to change some data types or apply a static_cast. This is - less than satisfactory for two reasons:

    -
      -
    • It may be unwield to change all the types to signed or - unsigned.

    • -
    • Litering one's program with static_cast - makes it more difficult to read.

    • -
    • We may believe that our signed type will never contain a - negative value. If we use a static_cast to suppress the - warning, we'll fail to detect a program error when it is commited. - This is aways a risk with casts.

    • -
    -

    This solution is the same as the above, Just replace instances of - the int with safe<int>.

    +Array index value can exceed array limits
    +

    Using an intrinsice C++ array, it's very easy to exceed array + limits. This can fail to be detected when it occurs and create bugs which + are hard to find. There are several ways to address this, but one of the + simplest would be to use safe_unsigned_range;

    #include <cassert>
     #include <stdexcept>
     #include <iostream>
     
    -#include "../include/safe_integer.hpp"
    -//#include "../include/safe_compare.hpp"
    +#include "../include/safe_range.hpp"
     
     void detected_msg(bool detected){
         std::cout << (detected ? "error detected!" : "error NOT detected! ") << std::endl;
     }
     
     int main(int argc, const char * argv[]){
    -    std::cout << "example 2:";
    -    std::cout << "undetected overflow in data type" << std::endl;
    +    // problem: array index values can exceed array bounds
    +    std::cout << "example 5: ";
    +    std::cout << "array index values can exceed array bounds" << std::endl;
    +    std::cout << "Not using safe numerics" << std::endl;
    +    int i_array[37];
    +
         try{
    -        int x = INT_MAX;
    -        // the following silently produces an incorrect result
    -        ++x;
    -        std::cout << x << " != " << INT_MAX << " + 1" << std::endl;
    +        unsigned int i_index = 43;
    +        // the following corrupts memory.
    +        // This may or may not be detected at run time.
    +        i_array[i_index] = 84;
             detected_msg(false);
         }
         catch(...){
             assert(false); // never arrive here
         }
    -    // solution: replace int with safe<int>
    +    // solution: replace unsigned array index with safe_unsigned_range
    +    std::cout << "Using safe numerics" << std::endl;
         try{
             using namespace boost::numeric;
    -        safe<int> x = INT_MAX;
    -        // throws exception when result is past maximum possible 
    -        ++x;
    -        assert(false); // never arrive here
    +        safe_unsigned_range<0, sizeof(i_array)/sizeof(int) - 1> i_index;
    +        i_index = 36; // this works fine
    +        i_array[i_index] = 84;
    +        i_index = 37; // throw exception here!
    +        i_array[i_index] = 84; // so we never arrive here
    +        assert(false);
         }
         catch(std::range_error & e){
    -        std::cout << e.what();
    +        std::cout << e.what() << std::endl;
             detected_msg(true);
         }
         return 0;
     }
     
    +
    +

    +Checking of intialization values can be easily overlooked

    +

    It's way too easy to overlook the checking of parameters received + from outside the current program.

    +
    #include <cassert>
    +#include <stdexcept>
    +#include <iostream>
    +
    +#include "../include/safe_integer.hpp"
    +
    +void detected_msg(bool detected){
    +    std::cout << (detected ? "error detected!" : "error NOT detected! ") << std::endl;
    +}
    +
    +int main(int argc, const char * argv[]){
    +    // problem: checking of externally produced value can be overlooked
    +    std::cout << "example 6: ";
    +    std::cout << "checking of externally produced value can be overlooked" << std::endl;
    +    std::cout << "Not using safe numerics" << std::endl;
    +
    +    try{
    +        int x, y, z;
    +        std::cin >> x >> y; // get integer values from the user
    +        z = x + y;
    +        std::cout << z;  // display sum of the values
    +    }
    +    catch(...){
    +        assert(false); // never arrive here
    +    }
    +    // solution: asign externally retrieved values to safe equivalents
    +    std::cout << "Using safe numerics" << std::endl;
    +    try{
    +        using namespace boost::numeric;
    +        safe<int> x, y, z;
    +        std::cin >> x >> y; // get integer values from the user
    +        try{
    +            z = x + y;
    +            //std::cout << z << std::endl;  // display sum of the values
    +        }
    +        catch(...){
    +            std::cout << "sum exceeds capacity of this computer" << std::endl;
    +        }
    +        assert(false);
    +    }
    +    catch(std::range_error & e){
    +        std::cout << e.what() << std::endl;
    +        detected_msg(true);
    +    }
    +    return 0;
    +}
    +
    +

    Without + safe integer, one will have to insert new code every time an integer + variable is retrieved. This is a tedious and error prone procedure.

    +
    -

    diff --git a/doc/html/types.html b/doc/html/types.html index 08528bd..35d5938 100644 --- a/doc/html/types.html +++ b/doc/html/types.html @@ -31,14 +31,14 @@ safe_unsigned_range<MIN, MAX>

    -Description

    +Description

    This type holds a integer in the range [MIN, MAX]. It will throw a std::out_of_range exception for any operation which would result in assigning an integer value outside of this range.

    -Template Parameters

    +Template Parameters
    @@ -71,7 +71,7 @@

    -Model of

    +Model of

    Numeric

    The usage of this type in an arithmetic expression with another unsigned type will result in another unsigned type fulfilling the Numeric concept. This will be the @@ -80,13 +80,13 @@

    -Header

    +Header

    #include <safe/numeric/safe_range.hpp>

    -Example of use

    +Example of use
    #include <safe/numeric/safe_range.hpp>
     
     void f(){
    @@ -106,7 +106,7 @@ void f(){
     
     

    -See Also

    +See Also

    std::out_of_range

    safe_signed_range

    @@ -117,14 +117,14 @@ void f(){ boost::intmax_tMAX>

    -Description

    +Description

    This type holds a integer in the range [MIN, MAX]. It will throw a std::out_of_range exception for operation which would result in assigning an integer value outside of this range.

    -Template Parameters

    +Template Parameters
    @@ -157,7 +157,7 @@ void f(){

    -Model of

    +Model of

    Numeric

    The usage of this type in an arithmetic expression will result in another type fulfilling the Numeric concept.

    @@ -165,13 +165,13 @@ void f(){

    -Header

    +Header

    #include <boost/safe_numerics/safe_range.hpp>

    -Example of use

    +Example of use
    #include <safe/numeric/safe_range.hpp>
     
     void f(){
    @@ -189,7 +189,7 @@ void f(){
     
     

    -See Also

    +See Also

    std::out_of_range

    safe_unsigned_range

    @@ -199,13 +199,13 @@ void f(){ safe<T>

    -Description

    +Description

    A safe<T> can be used anywhere a type T is used. When T is used in operation which overflows, a exception is thrown

    -Notation

    +Notation
    @@ -233,7 +233,7 @@ void f(){

    -Template Parameters

    +Template Parameters
    @@ -255,7 +255,7 @@ void f(){

    -Model of

    +Model of

    Numeric

    If the resulting type of the operation t op u is V, the resulting type of the operations st op su, t op su and st op u will be @@ -263,7 +263,7 @@ void f(){

    -Valid Expressions

    +Valid Expressions
    @@ -308,13 +308,13 @@ void f(){

    -Header

    +Header

    #include <boost/safe_numerics/safe_integer.hpp>

    -Example of use

    +Example of use

    The following program will emit an error message on a machine where int is only 16 bits but run without problem on a machine where int is 32 bits.

    @@ -335,13 +335,13 @@ void f(){

    -Notes

    +Notes

    Footnotes (if any) that are referred to by other parts of the page.

    -See Also

    +See Also

    Footnotes (if any) that are referred to by other parts of the page.

    @@ -349,7 +349,8 @@ void f(){
    -

    diff --git a/examples/example1.cpp b/examples/example1.cpp index 25647b0..923bac5 100644 --- a/examples/example1.cpp +++ b/examples/example1.cpp @@ -3,23 +3,22 @@ #include #include "../include/safe_integer.hpp" -//#include "../include/safe_compare.hpp" void detected_msg(bool detected){ std::cout << (detected ? "error detected!" : "error NOT detected! ") << std::endl; } int main(int argc, const char * argv[]){ + // problem: undetected erroneous expression evaluation std::cout << "example 1:"; std::cout << "undetected erroneous expression evaluation" << std::endl; std::cout << "Not using safe numerics" << std::endl; try{ - char x = 127; - char y = 2; - char z; + signed char x = 127; + signed char y = 2; + signed char z; // this produces an invalid result ! z = x + y; - // it is the wrong result !!! assert(z != 129); // but assert fails to detect it since C++ implicitly // converts variables to int before evaluating he expression! @@ -28,18 +27,18 @@ int main(int argc, const char * argv[]){ detected_msg(false); } catch(...){ - assert(false); // never arrive here + assert(false); // we never arrive here } // solution: replace char with safe std::cout << "Using safe numerics" << std::endl; try{ using namespace boost::numeric; - safe x = 127; - safe y = 2; - safe z; - // rather than producing and invalid result an exception is thrown + safe x = 127; + safe y = 2; + safe z; + // rather than producing an invalid result an exception is thrown z = x + y; - assert(false); // never arrive here + assert(false); // we never arrive here } catch(std::range_error & e){ // which can catch here diff --git a/examples/example2.cpp b/examples/example2.cpp index d36fb3a..ebd7fbc 100644 --- a/examples/example2.cpp +++ b/examples/example2.cpp @@ -3,13 +3,13 @@ #include #include "../include/safe_integer.hpp" -//#include "../include/safe_compare.hpp" void detected_msg(bool detected){ std::cout << (detected ? "error detected!" : "error NOT detected! ") << std::endl; } int main(int argc, const char * argv[]){ + // solution: undetected overflow in data type std::cout << "example 2:"; std::cout << "undetected overflow in data type" << std::endl; try{ @@ -20,7 +20,7 @@ int main(int argc, const char * argv[]){ detected_msg(false); } catch(...){ - assert(false); // never arrive here + assert(false); // we never arrive here } // solution: replace int with safe try{ @@ -28,7 +28,7 @@ int main(int argc, const char * argv[]){ safe x = INT_MAX; // throws exception when result is past maximum possible ++x; - assert(false); // never arrive here + assert(false); // we never arrive here } catch(std::range_error & e){ std::cout << e.what(); diff --git a/examples/example3.cpp b/examples/example3.cpp index 30f91d8..60eec17 100644 --- a/examples/example3.cpp +++ b/examples/example3.cpp @@ -3,36 +3,44 @@ #include #include "../include/safe_integer.hpp" -//#include "../include/safe_compare.hpp" +#include "../include/safe_compare.hpp" void detected_msg(bool detected){ std::cout << (detected ? "error detected!" : "error NOT detected! ") << std::endl; } int main(int argc, const char * argv[]){ - std::cout << "example 3: "; - std::cout << "implicit conversions change data values" << std::endl; + std::cout << "example 3:"; + std::cout << "undetected underflow in data type" << std::endl; std::cout << "Not using safe numerics" << std::endl; try{ - int x = -1000; + unsigned int x = 0; // the following silently produces an incorrect result - char y = x; + --x; + // because C/C++ implicitly converts mis-matched arguments to int + // suggests that the operation is correct + assert(x == -1); + // even though it's not !!! + + // however, safe_compare does detect the error + assert(! boost::numeric::safe_compare::equal(x, -1)); + std::cout << x << " != " << -1; detected_msg(false); } catch(...){ assert(false); // never arrive here } - // solution: replace int with safe and char with safe + // solution: replace unsigned int with safe std::cout << "Using safe numerics" << std::endl; try{ using namespace boost::numeric; - safe x = -1000; - // throws exception when conversion change data value - safe y = x; + safe x = 0; + // decrement unsigned to less than zero throws exception + --x; assert(false); // never arrive here } catch(std::range_error & e){ - std::cout << e.what() << std::endl; + std::cout << e.what(); detected_msg(true); } return 0; diff --git a/examples/example4.cpp b/examples/example4.cpp index 9a28759..c5c67e6 100644 --- a/examples/example4.cpp +++ b/examples/example4.cpp @@ -3,44 +3,36 @@ #include #include "../include/safe_integer.hpp" -#include "../include/safe_compare.hpp" void detected_msg(bool detected){ std::cout << (detected ? "error detected!" : "error NOT detected! ") << std::endl; } int main(int argc, const char * argv[]){ - std::cout << "example 4:"; - std::cout << "undetected underflow in data type" << std::endl; + // problem: implicit conversions change data values + std::cout << "example 4: "; + std::cout << "implicit conversions change data values" << std::endl; std::cout << "Not using safe numerics" << std::endl; try{ - unsigned int x = 0; + int x = -1000; // the following silently produces an incorrect result - --x; - // because C/C++ implicitly converts mis-matched arguments to int - // suggests that the operation is correct - assert(x == -1); - // even though it's not !!! - - // however, safe_compare does detect the error - assert(! boost::numeric::safe_compare::equal(x, -1)); - std::cout << x << " != " << -1; + char y = x; detected_msg(false); } catch(...){ assert(false); // never arrive here } - // solution: replace unsigned int with safe + // solution: replace int with safe and char with safe std::cout << "Using safe numerics" << std::endl; try{ using namespace boost::numeric; - safe x = 0; - // decrement unsigned to less than zero throws exception - --x; + safe x = -1000; + // throws exception when conversion change data value + safe y = x; assert(false); // never arrive here } catch(std::range_error & e){ - std::cout << e.what(); + std::cout << e.what() << std::endl; detected_msg(true); } return 0; diff --git a/include/safe_integer.hpp b/include/safe_integer.hpp index cd59371..f282448 100644 --- a/include/safe_integer.hpp +++ b/include/safe_integer.hpp @@ -63,13 +63,16 @@ struct safe : public detail::safe_integer_base::type { template safe(const U & u) : detail::safe_integer_base::type(u) - {} - /* + { + // verify that T is an integer type + BOOST_STATIC_ASSERT_MSG( + std::numeric_limits::is_integer, + "U is not an integer type" + ); + } safe(const T & t) : detail::safe_integer_base::type(t) {} - */ - }; } // numeric diff --git a/include/safe_range.hpp b/include/safe_range.hpp index 7b5189c..b0fc62c 100644 --- a/include/safe_range.hpp +++ b/include/safe_range.hpp @@ -201,7 +201,7 @@ namespace detail { template static typename check_subtraction_result::type subtract(const T & t, const U & u){ - typedef BOOST_TYPEOF_TPL(T() + U()) result_type; + typedef BOOST_TYPEOF_TPL(T() - U()) result_type; if(boost::numeric::is_unsigned::value){ if(u < 0) overflow("safe range left operand value altered"); diff --git a/test/test.cpp b/test/test.cpp deleted file mode 100644 index b259dd9..0000000 --- a/test/test.cpp +++ /dev/null @@ -1,162 +0,0 @@ -// 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) - -#include -#include // EXIT_SUCCESS -#include - -#include - -#include "../include/safe_range.hpp" -#include "../include/safe_integer.hpp" - -bool test1(){ - std::cout << "test1" << std::endl; - boost::numeric::safe_signed_range<-64, 63> x, y, z; - x = 1; - y = 2; - z = 3; - z = x + y; - z = x - y; - typedef boost::mpl::print< - boost::numeric::addition_result_bits< - boost::numeric::safe_signed_range<-64, 63>, - int - >::type - >::type t1; - typedef boost::mpl::print< - boost::numeric::bits< - boost::numeric::safe_signed_range<-64, 63> - >::type - >::type t2; - typedef boost::mpl::print< - boost::numeric::bits< - int - >::type - >::type t3; - - try{ - short int yi, zi; - yi = y; - zi = x + yi; - } - catch(std::exception e){ - // none of the above should trap. Mark failure if they do - std::cout << e.what() << std::endl; - return false; - } - return true; -} - -bool test2(){ - std::cout << "test2" << std::endl; - boost::numeric::safe_unsigned_range<0, 64> x, y, z; - x = 1; - y = 2; - z = 3; - - bool success = false; - try{ - z = x - y; // should trap here - } - catch(std::exception e){ - success = true; - } - if(success == false) - return false; - - try{ - int yi = y; - z = x + yi; // should trap here - } - catch(std::exception e){ - // none of the above should trap. Mark failure if they do - std::cout << e.what() << std::endl; - return false; - } - return true; -} - -bool test3(){ - std::cout << "test3" << std::endl; - boost::numeric::safe x, y, z; - x = 1; - y = 2; - z = 3; - try{ - z = x + y; - z = x - y; - int yi, zi; - zi = x + yi; - z = x + yi; - } - catch(std::exception e){ - // none of the above should trap. Mark failure if they do - std::cout << e.what() << std::endl; - return false; - } - return true; -} - -bool test4(){ - std::cout << "test4" << std::endl; - boost::numeric::safe x, y, z; - x = 1; - y = 2; - z = 3; - z = x + y; - bool success = false; - try{ - z = x - y; // should trap here - } - catch(std::exception e){ - success = true; - } - if(success == false) - return false; - unsigned int yi, zi; - zi = x; - zi = x + yi; - z = x + yi; - zi = x + y; - return true; -} - -#include - -bool test5(){ - std::cout << "test5" << std::endl; - boost::numeric::safe x, y, z; - x = 1; - y = 2; - z = 3; - z = x + y; - bool success = false; - try{ - z = x - y; // should trap here - } - catch(std::exception e){ - success = true; - } - if(success == false) - return false; - boost::uint64_t yi, zi; - zi = x; - zi = x + yi; - z = x + yi; - zi = x + y; - return true; -} - -int main(int argc, char *argv[]){ - return ( - test1() && - test2() && - test3() && - test4() && - test5() - ) ? EXIT_SUCCESS : EXIT_FAILURE; -} diff --git a/test/test_divide1.cpp b/test/test_divide1.cpp deleted file mode 100644 index dfa58c2..0000000 --- a/test/test_divide1.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// 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) - -#include "test_divide.hpp" - -#define TESTX(a, b, c, d) \ - TEST_IMPL( \ - a, \ - b, \ - boost::numeric::safe, \ - boost::numeric::safe \ - ) \ -/**/ - -bool test_divide1(){ - bool rval = true; - #pragma message("7") - EACH_TYPE1(boost::uint64_t); - #pragma message("0") - EACH_TYPE1(boost::int8_t); - #pragma message("1") - EACH_TYPE1(boost::uint8_t); - #pragma message("2") - EACH_TYPE1(boost::int16_t); - #pragma message("3") - EACH_TYPE1(boost::uint16_t); - #pragma message("4") - EACH_TYPE1(boost::int32_t); - #pragma message("5") - EACH_TYPE1(boost::uint32_t); - #pragma message("6") - EACH_TYPE1(boost::int64_t); - return rval; -} diff --git a/test/test_modulus1.cpp b/test/test_modulus1.cpp deleted file mode 100644 index 93b5335..0000000 --- a/test/test_modulus1.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// 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) - -#include "test_modulus.hpp" - -#define TESTX(a, b, c, d) \ - TEST_IMPL( \ - a, \ - b, \ - boost::numeric::safe, \ - boost::numeric::safe \ - ) \ -/**/ - -bool test_modulus1(){ - bool rval = true; - #pragma message("0") - EACH_TYPE1(boost::int8_t); - #pragma message("1") - EACH_TYPE1(boost::uint8_t); - #pragma message("2") - EACH_TYPE1(boost::int16_t); - #pragma message("3") - EACH_TYPE1(boost::uint16_t); - #pragma message("4") - EACH_TYPE1(boost::int32_t); - #pragma message("5") - EACH_TYPE1(boost::uint32_t); - #pragma message("6") - EACH_TYPE1(boost::int64_t); - #pragma message("7") - EACH_TYPE1(boost::uint64_t); - return rval; -} diff --git a/test/test_multiply1.cpp b/test/test_multiply1.cpp deleted file mode 100644 index 97cc3c9..0000000 --- a/test/test_multiply1.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// 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) - -#include "test_multiply.hpp" - -#define TESTX(a, b, c, d) \ - TEST_IMPL( \ - a, \ - b, \ - boost::numeric::safe, \ - boost::numeric::safe \ - ) \ -/**/ - -bool test_multiply1(){ - bool rval = true; - #pragma message("0") - EACH_TYPE1(boost::int8_t); - #pragma message("1") - EACH_TYPE1(boost::uint8_t); - #pragma message("2") - EACH_TYPE1(boost::int16_t); - #pragma message("3") - EACH_TYPE1(boost::uint16_t); - #pragma message("4") - EACH_TYPE1(boost::int32_t); - #pragma message("5") - EACH_TYPE1(boost::uint32_t); - #pragma message("6") - EACH_TYPE1(boost::int64_t); - #pragma message("7") - EACH_TYPE1(boost::uint64_t); - return rval; -} diff --git a/test/test_subtract1.cpp b/test/test_subtract1.cpp deleted file mode 100644 index d42c8b0..0000000 --- a/test/test_subtract1.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// 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) - -#include "test_subtract.hpp" - -#define TESTX(a, b, c, d) \ - TEST_IMPL( \ - a, \ - b, \ - boost::numeric::safe, \ - boost::numeric::safe \ - ) \ -/**/ - -bool test_subtract1(){ - bool rval = true; - #pragma message("0") - EACH_TYPE1(boost::int8_t); - #pragma message("1") - EACH_TYPE1(boost::uint8_t); - #pragma message("2") - EACH_TYPE1(boost::int16_t); - #pragma message("3") - EACH_TYPE1(boost::uint16_t); - #pragma message("4") - EACH_TYPE1(boost::int32_t); - #pragma message("5") - EACH_TYPE1(boost::uint32_t); - #pragma message("6") - EACH_TYPE1(boost::int64_t); - #pragma message("7") - EACH_TYPE1(boost::uint64_t); - return rval; -}