diff --git a/doc/boostbook/automatic.xml b/doc/boostbook/automatic.xml index f302342..b3a58f8 100644 --- a/doc/boostbook/automatic.xml +++ b/doc/boostbook/automatic.xml @@ -7,8 +7,77 @@
Description - This type contains the functions to return a type with sufficient - capacity to hold the result of a given arithmetic operation. + This type contains the meta-functions to return a type with + sufficient capacity to hold the result of a given binary arithmetic + operation. + + The standard C/C++ procedure for executing arithmetic operations on + operands of different types is: + + + + Convert operands to some common type using a somewhat + elaborate elaborate rules defined in the C++ standard. + + + + Execute the operation. + + + + If the result of the operation cannot fit in the common type + of the operands, discard the high order bits. + + The automatic promotion policy replaces the standard + C/C++ procedure for the following one: + + Determine the signed/unsigned property of the result according + to the following rules. + + For addition. If the operands are both unsigned the + result will be unsigned. Otherwise it will be signed. + + + + For subtraction, the result will be signed. + + + + For multiplication, division and modulus, if both + operations are unsigned the result will be unsigned. + Otherwise, the result will be signed. + + + + For left/right shift, the sign of the result will be the + sign of the left operand. + + + + + + Determine the smallest size of the signed or unsigned type + which can be guaranteed hold the result. + + + + If this size exceeds the maximum size supported by the + compiler, use the maximum size supported by the compiler. + + + + Execute the operation. + + If the result cannot be contained in the result type as + above, invoke an error procedure. + + + + Otherwise, return the result in the result type + + + +
diff --git a/doc/boostbook/checked_result.xml b/doc/boostbook/checked_result.xml index b366a01..952425d 100644 --- a/doc/boostbook/checked_result.xml +++ b/doc/boostbook/checked_result.xml @@ -99,7 +99,14 @@ r - An object of type R + An instance of type R + + + + e + + An instance of type exception_type @@ -164,9 +171,9 @@ static_cast<const char *>(c) - R + const char * - extract wrapped value - asserts if not possible + returns pointer to error message @@ -187,7 +194,7 @@ boost::logic::tribool compare the wrapped values of two checked_result - instances. If either one contains an invalid value, return + instances. If either one contains an error_type value, return boost::logic::tribool::indeterminant. diff --git a/doc/boostbook/cpp.xml b/doc/boostbook/cpp.xml index 2b575f4..dc1f801 100644 --- a/doc/boostbook/cpp.xml +++ b/doc/boostbook/cpp.xml @@ -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 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 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 following code is taken from a real project which has used this method. @@ -182,8 +182,6 @@ uint16 get_stopping_distance(LEMPARAMETER velocity){ ... Note the usage of the compile time trap policy in order to detect at - compile time any possible error conditions. As I write this, this is still - being refined. Hopefully this will be available by the time you read - this. + compile time any possible error conditions.
diff --git a/doc/boostbook/eliminate_runtime_penalty.xml b/doc/boostbook/eliminate_runtime_penalty.xml index b71e5df..692e474 100644 --- a/doc/boostbook/eliminate_runtime_penalty.xml +++ b/doc/boostbook/eliminate_runtime_penalty.xml @@ -17,6 +17,12 @@ 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 The promotion rules implemented in the default native - type promotion policy are consistent with those of standard C++ + linkend="safe_numerics.types.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. 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.types.automatic">automatic type + promotion policy, we can change the rules of C++ arithmetic for safe types + to something like the following: @@ -110,7 +116,7 @@ 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.types.automatic">automatic type promotion policy will achieve the equivalent result without these problems. @@ -119,7 +125,7 @@ long z = (long)x + (long)y; // can never overflow 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 trap_exception + linkend="safe_numerics.types.trap_exception">trap_exception exception policy enforces this guarantee. @@ -150,7 +156,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.types.trap_exception">trap_exception exception policy. This small example illustrates how to use automatic type promotion @@ -172,14 +178,14 @@ z = <long>[-4294967296,4294967294] = 2147483649 that: the automatic + linkend="safe_numerics.types.automatic">automatic type promotion policy has rendered the result of the some of two integers as a long type. our program compiles without error - even when using the trap_exception + linkend="safe_numerics.types.trap_exception">trap_exception exception policy diff --git a/doc/boostbook/exception_policy_concept.xml b/doc/boostbook/exception_policy_concept.xml index 8e8604a..8d7c810 100644 --- a/doc/boostbook/exception_policy_concept.xml +++ b/doc/boostbook/exception_policy_concept.xml @@ -28,15 +28,15 @@ EP - A type that full fills the requirements of an + A type that fulfills the requirements of an ExceptionPollicy 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. - + - + @@ -156,15 +156,13 @@ - 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 trap "exceptions" by - calling your own special functions. + 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. diff --git a/doc/boostbook/exception_type.xml b/doc/boostbook/exception_type.xml index 20f8e5a..16b57ae 100644 --- a/doc/boostbook/exception_type.xml +++ b/doc/boostbook/exception_type.xml @@ -124,7 +124,7 @@ dispatch<EP>(const exception_type & e, const char * msg);#include "exception_policy.hpp" -dispatch(overflow_error, "operation resulted in overflow"); +dispatch<boost::numeric::throw_exception>(overflow_error, "operation resulted in overflow"); diff --git a/doc/boostbook/faq.xml b/doc/boostbook/faq.xml index 1a585f4..aa0329b 100644 --- a/doc/boostbook/faq.xml +++ b/doc/boostbook/faq.xml @@ -24,7 +24,7 @@ - Can safe types be used as drop-in replacement for built-in + Can safe types be used as drop-in replacements for built-in types? @@ -214,12 +214,12 @@ using safe_t = boost::numeric::safe< pic16_promotion, boost::numeric::throw_exception // use for compiling and running tests >; -typedef safe_t<std::int16_t> int16_t; -typedef safe_t<std::int32_t> int32_t; +typedef safe_t<std::int_least16_t> int16_t; +typedef safe_t<std::int_least32_t> int32_t; #else /* using C on embedded platform */ -typedef int int16_t; -typedef long int32_t; +typedef int int_least16_t; +typedef long int_least16_t; #endif diff --git a/doc/boostbook/native.xml b/doc/boostbook/native.xml index 711b5c0..023d4c1 100644 --- a/doc/boostbook/native.xml +++ b/doc/boostbook/native.xml @@ -1,7 +1,7 @@ -
+
native
@@ -12,7 +12,7 @@ Usage of this policy with safe types will produce the exact same arithmetic results that using normal unsafe integer types will. Hence this - policy is suitable as a drop-in replacement for these unsafe types. It's + policy is suitable as a drop-in replacement for these unsafe types. Its main function is to trap incorrect arithmetic results when using C++ for integer arithmetic.
@@ -56,7 +56,7 @@ void int f(safe_int x, safe_int y){ The following example illustrates the native type being passed as a template parameter for the type safe<int>. This example is slightly contrived in that safe<int> - has native as it's default promotion parameter so explicitly + has native as its default promotion parameter so explicitly using native is not necessary. #include <cassert> diff --git a/doc/boostbook/no_exception_support.xml b/doc/boostbook/no_exception_support.xml index 2f62882..f6f3e6b 100644 --- a/doc/boostbook/no_exception_support.xml +++ b/doc/boostbook/no_exception_support.xml @@ -2,7 +2,8 @@
- no_exception_support<O, U = O, R =O, D = O> + no_exception_support<NoError, UnInitalized, Overflow, Underflow, + Range, Domain>
Description @@ -27,8 +28,6 @@ - - @@ -37,8 +36,6 @@ Parameter - Default - Type Requirements Description @@ -47,43 +44,61 @@ - O + NoError - + void (*NoError)(const char *) - void (*O)(const char *) + Function to call on when an operation is invoked + which COULD throw but does not. + + + + UnInitalized + + void (*UnInitalizized)(const char *) + + Function to call on when value is + uninitialized + + + + Overflow + + void (*Overflow)(const char *) Function to call on overflow error - O + Overflow - O + void (*Overflow)(const char *) - void (*U)(const char *) + Function to call on overflow error + + + + Underflow + + void (*Underflow)(const char *) Function to call on underflow error - O + Range - O - - void (*R)(const char *) + void (*Range)(const char *) Function to call on range error - O + Domain - O + void (*Domain)(const char *) - void (*D)(const char *) - - Function to call on domain error + Function to call on domain error @@ -110,12 +125,16 @@ [A code fragment involving the type.] - void overflow(const char * msg); + void no_error(const char * msg); +void uninitialize(const char * msg); +void overflow(const char * msg); void underflow(const char * msg); void range_error(const char * msg); void domain_error(const char * msg); using ep = ignore_exception< + no_error, + uninitialized, overflow, underflow, range_error, diff --git a/doc/boostbook/promotion_policy_concept.xml b/doc/boostbook/promotion_policy_concept.xml index c12354c..eb2d1d4 100644 --- a/doc/boostbook/promotion_policy_concept.xml +++ b/doc/boostbook/promotion_policy_concept.xml @@ -25,9 +25,9 @@ auto z = x + ythe type of z will be an - + - + @@ -38,15 +38,16 @@ auto z = x + ythe type of z will be an - T, U, V + T, U A type that is a model of the Numeric concept - t, u, v + R - An object of type modeling Numeric + An object of type modeling Numeric which can be used to + construct a SafeNumeric type. @@ -56,8 +57,11 @@ auto z = x + ythe type of z will be an
Valid Expressions - Any operations which result in integers which cannot be represented - as some Numeric type will throw an exception. + Any operations which result in integers which cannot be represented + as some Numeric type will throw an exception.These expressions return a + type which can be used as the basis create a SafeNumeric type. + + @@ -107,19 +111,21 @@ auto z = x + ythe type of z will be an - PP::left_shift_result<T>::type + PP::left_shift_result<T, + U>::type unspecified Numeric type - PP::right_shift_result<T>::type + PP::right_shift_result<T, + u>::type unspecified Numeric type - PP::bitwise_result<T>::type + PP::bitwise_result<T, U>::type unspecified Numeric type @@ -150,12 +156,16 @@ auto z = x + ythe type of z will be an int x; char y; auto z = x + y; // could result in overflow -safe<int> sx; -auto sz = sx + y; // includes code which traps overflows at runtime +safe<int, native> sx; +auto sz = sx + y; // standard C++ code which detects errors - The type of sz will be safe< type of z >. + Type sz will be a SafeNumeric type + based on int. If the result exceeds the maximum value + that can be stored in an int, an error is + detected. - This policy is documented in The native policy is documented in Promotion Policies - native. @@ -172,13 +182,14 @@ auto sz = sx + y; // includes code which traps overflows at runtimeint x; char y; auto z = x + y; // could result in overflow -safe<int> sx; -auto sz = sx + y; // promotes expression type to a safe<long int> which requires no result checking -is guaranteed not to overflow. - +safe<int, automatic> sx; +auto sz = sx + y; + // sz is a safe type based on long + // hence sz is guaranteed not to overflow. safe_unsigned_range<1, 4> a; safe_unsigned_range<2, 4> b; -auto c = a + b; // c will be of type safe_unsigned_range<3, 8> and cannot overflow +auto c = a + b; // c will be a safe type with a range [3,8] and cannot overflow + Type sz will be a SafeNumeric type @@ -191,8 +202,8 @@ auto c = a + b; // c will be of type safe_unsigned_range<3, 8> and cannot guaranteed to hold the sum so no overflow checking is done. This policy is documented in Promotion Policies - - automatic + linkend="safe_numerics.promotion_policies.automatic">Promotion + Policies - automatic
diff --git a/doc/boostbook/safe.xml b/doc/boostbook/safe.xml index 046921b..79c2a01 100644 --- a/doc/boostbook/safe.xml +++ b/doc/boostbook/safe.xml @@ -4,7 +4,7 @@
safe<T, PP, EP> - +
Description @@ -35,8 +35,7 @@ T - Underlying type from which a safe type is being - derived + Underlying type from on which safe type based @@ -181,8 +180,8 @@ 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 an incorrect - arithmetic type. Behavior of this program could vary according to the + message at runtime if any of several events result in incorrect + arithmetic results. Behavior of this program could vary according to the machine architecture in question. #include <exception> @@ -217,10 +216,12 @@ int main(){ creates the possibility of subtle bugs. It conflicts with the purpose of the library in a fundamental way. The library specifies that these conversions are errors that are to be invoked at compile time. If one - wants to switch between save and built-in types via an alias, this type + 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. + 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.
@@ -234,10 +235,11 @@ int main(){ 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> @@ -255,18 +257,18 @@ void f(){ Adjust type promotion rules. Another way to avoid arithmetic errors like overflow is to promote - types to larger sizes before doing the arithmetic. This can be justified - by the observe + 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 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 the work is done at - compile time - checking for exceptions necessary (input is of course an - exception). The following example illustrates this. + 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. #include <boost/numeric/safe.hpp> #include <iostream> @@ -281,6 +283,13 @@ 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 8766f6e..56d3207 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 solution - 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 + solutions are similar, we'll confine the current discussion to just this + one example.
@@ -90,10 +90,11 @@ This library implements special versions of int, unsigned, etc. which behave exactly like the original ones except that the results of these - operations are guaranteed to be either arithmetically correct or invoke an - error. Using this library, the above example would be rendered as: + 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: - #include <boost/safe_numeric/safe_integer.hpp> + #include <boost/safe_numerics/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 @@ -215,9 +216,9 @@ safe<int> f(safe<int> x, safe<int> y){ Enforce of 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. + 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.
diff --git a/doc/boostbook/safe_literal.xml b/doc/boostbook/safe_literal.xml index 314a923..fe6f6bc 100644 --- a/doc/boostbook/safe_literal.xml +++ b/doc/boostbook/safe_literal.xml @@ -82,8 +82,7 @@ PromotionPolicy<PP> - Default value is boost::numeric::native + Default value is void @@ -93,8 +92,7 @@ linkend="safe_numerics.exception_policy">Exception Policy<EP> - Default value is boost::numeric::throw_exception + Default value is void diff --git a/doc/boostbook/safe_numeric_concept.xml b/doc/boostbook/safe_numeric_concept.xml index 4879689..1842c3e 100644 --- a/doc/boostbook/safe_numeric_concept.xml +++ b/doc/boostbook/safe_numeric_concept.xml @@ -44,7 +44,8 @@ T, U Types fulfilling Numeric type + linkend="safe_numerics.numeric">Numeric or Integer type requirements @@ -55,7 +56,7 @@ - S, S1, S2 + S A type fulfilling SafeNumeric type requirements @@ -69,19 +70,20 @@ op - C++ infix operator + C++ infix operator which is supported by underlying numeric + type prefix_op - C++ prefix operator: -, +, ~ + C++ prefix operator: ++, --, -, +, ~ postfix_op - C++ postfix operator + C++ postfix operator: ++, -- @@ -99,7 +101,7 @@ - + @@ -146,7 +148,7 @@ prefix_op S - unspecified S + S invoke safe C++ operator prefix_op and return another SafeNumeric type. @@ -155,7 +157,7 @@ S postfix_op - unspecified S + S invoke safe C++ operator postfix_op and return another SafeNumeric type. @@ -164,20 +166,20 @@ s assign_op t - S1 + S convert t to type S1 and assign it to s1. If the - value t cannot be represented as an instance of type S1, it is - an error. + value t cannot be represented as an instance of type S, it is an + error. S(t) - unspecified S + S construct a instance of S from a value of type T. f - the value t cannot be represented as an instance of type S1, it + the value t cannot be represented as an instance of type S, it is an error. @@ -200,16 +202,57 @@ 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. 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. + 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. @@ -227,8 +270,19 @@ - Binary expressions which are not assignments require that - promotion and exception policies be identical. + 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. + + @@ -241,8 +295,9 @@ int main(){ safe<long> y; f(y); // compile time error return 0; -}
This behavior prevents a safe<T> from - being a "drop-in" replacement for a T. +}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.
@@ -250,20 +305,20 @@ int main(){
Complexity Guarantees - 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); + There are no complexity guarantees explicitly enforced here. Usage + of safe operations should add a fixed amount of time to compilation with + each usage.
Invariants The fundamental requirement of a SafeNumeric type is that implements - all C++ operations permitted on it's base type in a way the prevents the + 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 573b154..f922e76 100644 --- a/doc/boostbook/safe_numerics.xml +++ b/doc/boostbook/safe_numerics.xml @@ -118,7 +118,7 @@ fundamental software components described here. It is not necessary to know about these components to use the library. This information has been included to help those who want to understand how the library works so - they can extend it, correct bugs in it, or understand it's limitations. + they can extend it, correct bugs in it, or understand its limitations. These components are also interesting in their own right. For all these reasons, they are documented here. In general terms, the library works in the following manner: @@ -179,10 +179,10 @@ - - MIN, MAX - Minimum and maximum values that the range can - represent. + Minimum and maximum Numeric values that the + range can represent. @@ -60,15 +61,17 @@ 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 @@ -100,19 +103,19 @@ MIN - must be non-integer literal + must be Numeric literal - The minimum non-negative integer value that this type may - hold + The minimum maximum value that this type may hold MAX - must be a non-negative literal + must be a Numeric literal - The maximum non-negative integer value that this type may - hold + The maximum lowest value that this type may hold @@ -177,11 +180,12 @@
Example of use - #include <safe/numeric/safe_range.hpp> + #include <safe/numerics/safe_range.hpp> // safe_unsigned range +#include <safe/numerics/safe_integer.hpp> // safe 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 @@ -193,9 +197,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/trap_exception.xml b/doc/boostbook/trap_exception.xml index 6780dfb..0249aa2 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 + This exception policy will trap at compile time any operation which 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 diff --git a/doc/boostbook/tutorial.xml b/doc/boostbook/tutorial.xml index 84d9df4..66f3d90 100644 --- a/doc/boostbook/tutorial.xml +++ b/doc/boostbook/tutorial.xml @@ -15,12 +15,15 @@ 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, 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. + 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. This program demonstrates this problem. The solution is to replace - instances of int type with safe<int> + instances of type int 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. This is a common problem with for loops. + beyond it's domain. @@ -150,7 +153,12 @@ detected error:converted negative value to unsigned does illustrate the usage of safe_range<T> for assigning legal range to variables. This will guarantee that under no circumstances will the variable contain a value outside of the specified - range. + 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.
diff --git a/examples/Motor.c b/examples/Motor.c index d17c249..d03afd0 100644 --- a/examples/Motor.c +++ b/examples/Motor.c @@ -116,7 +116,6 @@ void isr_motor_step() } // if (ramp_sts != ramp_idle) } // isr_motor_step() - void motor_run(short pos_new) { // set up to drive motor to pos_new (absolute step#) if (pos_new < motor_pos) // get direction & #steps diff --git a/examples/example10.cpp b/examples/example10.cpp index fc2e99b..3609856 100644 --- a/examples/example10.cpp +++ b/examples/example10.cpp @@ -35,7 +35,7 @@ int main(){ safe_f(100, 100); // works as expected safe_f(100, -100); // throw error } - catch(const exception & e){ + catch(const std::exception & e){ cout << "detected error:" << e.what() << endl;; } return 0; diff --git a/examples/example13.cpp b/examples/example13.cpp index 3bf7ee9..35cbac8 100644 --- a/examples/example13.cpp +++ b/examples/example13.cpp @@ -28,7 +28,7 @@ int main(int argc, const char * argv[]){ safe x = 1; safe y = 0; std::cout << x / y; - std::cout << " error detected!" << std::endl; + std::cout << " error NOT detected!" << std::endl; } catch(std::exception & e){ std::cout << e.what() << std::endl; diff --git a/examples/example83.cpp b/examples/example83.cpp index e4b1ca6..dd36933 100644 --- a/examples/example83.cpp +++ b/examples/example83.cpp @@ -25,8 +25,8 @@ int main(int argc, const char * argv[]){ // since the sum of x and y wouldn't be in the legal // range for z. // const safe_signed_literal<20> x; - const safe_signed_literal<10> x; // no problem - const safe_signed_literal<67> y; + const safe_signed_literal<10, native, trap_exception> x; // no problem + const safe_signed_literal<67, native, trap_exception> y; const safe_t z = x + y; std::cout << "x = " << safe_format(x) << std::endl; diff --git a/examples/example84.cpp b/examples/example84.cpp index 4f25809..c017f71 100644 --- a/examples/example84.cpp +++ b/examples/example84.cpp @@ -21,7 +21,7 @@ using input_safe_t = safe_signed_range< -24, 82, automatic, // we don't need automatic in this case - throw_exception // these variables need to + throw_exception // assignment of out of range value should throw >; // function arguments can never be outside of limits diff --git a/examples/example91.cpp b/examples/example91.cpp index bd8c748..d4b4106 100644 --- a/examples/example91.cpp +++ b/examples/example91.cpp @@ -46,7 +46,7 @@ using safe_bool_t = boost::numeric::safe_unsigned_range< >; #define DESKTOP -#include "Motor1.c" +#include "motor1.c" #include #include diff --git a/include/automatic.hpp b/include/automatic.hpp index 4a352a9..f94abc2 100644 --- a/include/automatic.hpp +++ b/include/automatic.hpp @@ -12,10 +12,8 @@ // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -// policy which creates results types equal to that of C++ promotions. -// Using the policy will permit the program to build and run in release -// mode which is identical to that in debug mode except for the fact -// that errors aren't trapped. +// policy which creates expanded results types designed +// to avoid overflows. #include #include // (u)intmax_t, @@ -23,7 +21,6 @@ #include #include -#include "utility.hpp" #include "safe_common.hpp" #include "checked_result.hpp" #include "interval.hpp" @@ -32,73 +29,8 @@ namespace boost { namespace numeric { struct automatic { - // section 4.13 integer conversion rank - template - using rank = - typename boost::mpl::if_c< - sizeof(char) == sizeof(T), - std::integral_constant, - typename boost::mpl::if_c< - sizeof(short) == sizeof(T), - std::integral_constant, - typename boost::mpl::if_c< - sizeof(int) == sizeof(T), - std::integral_constant, - typename boost::mpl::if_c< - sizeof(long) == sizeof(T), - std::integral_constant, - typename boost::mpl::if_c< - sizeof(long long) == sizeof(T), - std::integral_constant, - void - >::type >::type >::type >::type >::type; - - // note presumption that T & U don't have he same sign - // if that's not true, these won't work - template - using select_signed = typename boost::mpl::if_c< - std::numeric_limits::is_signed, - T, - U - >::type; - - template - using select_unsigned = typename boost::mpl::if_c< - std::numeric_limits::is_signed, - U, - T - >::type; - - template - using calculate_max_t = - typename boost::mpl::if_c< - // clause 1 - if both operands have the same sign - std::numeric_limits::is_signed - == std::numeric_limits::is_signed, - // use that sign - typename boost::mpl::if_c< - std::numeric_limits::is_signed, - std::intmax_t, - std::uintmax_t - >::type, - // clause 2 - otherwise if the rank of the unsigned type exceeds - // the rank of the of the maximum signed type - typename boost::mpl::if_c< - (rank< select_unsigned>::value - > rank< std::intmax_t >::value), - // use unsigned type - std::uintmax_t, - // clause 3 - otherwise if the type of the signed integer type can - // represent all the values of the unsigned type - typename boost::mpl::if_c< - std::numeric_limits< std::intmax_t >::digits >= - std::numeric_limits< select_unsigned >::digits, - // use signed type - std::intmax_t, - // clause 4 - otherwise use unsigned version of the signed type - std::uintmax_t - >::type >::type >::type; - + // the following returns the "true" type. After calculating the new max and min + // these return the minimum size type which can hold the expected result. template struct defer_stored_signed_lazily { using type = signed_stored_type; @@ -117,43 +49,42 @@ struct automatic { defer_stored_unsigned_lazily >::type; + /////////////////////////////////////////////////////////////////////// template struct addition_result { + using temp_base_type = typename boost::mpl::if_c< + // if both arguments are unsigned + ! std::numeric_limits::is_signed + && ! std::numeric_limits::is_signed, + // result is unsigned + std::uintmax_t, + // otherwise result is signed + std::intmax_t + >::type; - using result_base_type = calculate_max_t; - using t_base_type = typename base_type::type; - using u_base_type = typename base_type::type; - - // filter out case were overflow cannot occur - // note: subtle trickery. Suppose t is safe_range. Then - // std::numeric_limits::min() will be safe_range t_interval{ + constexpr static const interval::type> t_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - constexpr static const interval u_interval{ + constexpr static const interval::type> u_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - // when we add the temporary intervals above, we'll get a new interval - // with the correct range for the sum ! - constexpr static const checked_result> r_interval - = add(t_interval, u_interval); + constexpr static const checked_result> + r_interval = add(t_interval, u_interval); - constexpr static const interval result_interval = - r_interval.no_exception() ? - static_cast>(r_interval) + constexpr static const interval + result_interval = r_interval.no_exception() ? + static_cast>(r_interval) : - interval{} + interval{} ; using type = typename result_type< - result_base_type, + temp_base_type, result_interval.l, result_interval.u >::type; @@ -162,34 +93,30 @@ struct automatic { /////////////////////////////////////////////////////////////////////// template struct subtraction_result { - // subtraction can result in negative result regardless of the - // operand types ! - using result_base_type = std::intmax_t; - using t_base_type = typename base_type::type; - using u_base_type = typename base_type::type; + // result of subtraction are always signed. + using temp_base_type = intmax_t; - constexpr static const interval t_interval{ + constexpr static const interval::type> t_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - constexpr static const interval u_interval{ + constexpr static const interval::type> u_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - - constexpr static const checked_result> r_interval - = subtract(t_interval, u_interval); - - constexpr static const interval result_interval = + constexpr static const checked_result> + r_interval = subtract(t_interval, u_interval); + + constexpr static const interval result_interval = r_interval.no_exception() ? - static_cast>(r_interval) + static_cast>(r_interval) : - interval{} + interval{} ; using type = typename result_type< - result_base_type, + temp_base_type, result_interval.l, result_interval.u >::type; @@ -198,32 +125,38 @@ struct automatic { /////////////////////////////////////////////////////////////////////// template struct multiplication_result { - using result_base_type = calculate_max_t; - using t_base_type = typename base_type::type; - using u_base_type = typename base_type::type; + using temp_base_type = typename boost::mpl::if_c< + // if both arguments are unsigned + ! std::numeric_limits::is_signed + && ! std::numeric_limits::is_signed, + // result is unsigned + std::uintmax_t, + // otherwise result is signed + std::intmax_t + >::type; - constexpr static const interval t_interval{ + constexpr static const interval::type> t_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - constexpr static const interval u_interval{ + constexpr static const interval::type> u_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - constexpr static const checked_result> r_interval - = multiply(t_interval, u_interval); + constexpr static const checked_result> + r_interval = multiply(t_interval, u_interval); - constexpr static const interval result_interval = + constexpr static const interval result_interval = r_interval.no_exception() ? - static_cast>(r_interval) + static_cast>(r_interval) : - interval{} + interval{} ; using type = typename result_type< - result_base_type, + temp_base_type, result_interval.l, result_interval.u >::type; @@ -232,39 +165,39 @@ struct automatic { /////////////////////////////////////////////////////////////////////// template struct division_result { - using t_base_type = typename base_type::type; - using u_base_type = typename base_type::type; + using temp_base_type = typename boost::mpl::if_c< + // if both arguments are unsigned + ! std::numeric_limits::is_signed + && ! std::numeric_limits::is_signed, + // result is unsigned + std::uintmax_t, + // otherwise result is signed + std::intmax_t + >::type; - constexpr static const interval t_interval{ + constexpr static const interval::type> t_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - constexpr static const interval u_interval{ + constexpr static const interval::type> u_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - using result_base_type = typename boost::mpl::if_c< - std::numeric_limits::is_signed - || std::numeric_limits::is_signed, - std::intmax_t, - std::uintmax_t - >::type; + constexpr static const checked_result> + r_interval = divide_nz(t_interval, u_interval); - constexpr static checked_result> r { - divide_nz(t_interval, u_interval) - }; - constexpr static const interval result_interval { - r.no_exception() ? - static_cast>(r) + constexpr static const interval result_interval { + r_interval.no_exception() ? + static_cast>(r_interval) : - interval{} + interval{} }; using type = typename result_type< - result_base_type, + temp_base_type, result_interval.l, result_interval.u >::type; @@ -283,33 +216,39 @@ struct automatic { /////////////////////////////////////////////////////////////////////// template struct modulus_result { - using t_base_type = typename base_type::type; - using u_base_type = typename base_type::type; + using temp_base_type = typename boost::mpl::if_c< + // if both arguments are unsigned + ! std::numeric_limits::is_signed + && ! std::numeric_limits::is_signed, + // result is unsigned + std::uintmax_t, + // otherwise result is signed + std::intmax_t + >::type; - constexpr static const interval t_interval{ + constexpr static const interval::type> t_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - constexpr static const interval u_interval{ + constexpr static const interval::type> u_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - using r_base_type = std::make_unsigned_t; + constexpr static const checked_result> + r_interval = modulus_nz(t_interval, u_interval); - constexpr static const checked_result> r - { modulus_nz(t_interval, u_interval) }; - constexpr static const interval result_interval = - r.no_exception() ? - static_cast>(r) + constexpr static const interval result_interval { + r_interval.no_exception() ? + static_cast>(r_interval) : - interval{} - ; + interval{} + }; using type = typename result_type< - r_base_type, + temp_base_type, result_interval.l, result_interval.u >::type; @@ -326,62 +265,8 @@ struct automatic { } /////////////////////////////////////////////////////////////////////// - // temporary version -/* - template - struct left_shift_result { - // calculate the number of bits were going to need to hold - // the shifted result - constexpr static int result_base_digits = boost::static_signed_min< - // the largest number of bits possible - std::numeric_limits::digits, - // the largest number of bits that the shifted result might be - std::numeric_limits::digits + - // the number of bits in the max possible value of U - boost::static_signed_min< - // the largest number of bits of any type - std::numeric_limits::digits, - // the number bits to hold the maximum value of U - // static_cast(std::numeric_limits::max()) - std::numeric_limits::max() % std::numeric_limits::max() - >::value - >::value; + // shift operations - using result_base_type = typename boost::mpl::if_c< - std::numeric_limits::is_signed, - typename boost::int_t::least, - typename boost::uint_t::least - >::type; - - using t_base_type = typename base_type::type; - using u_base_type = typename base_type::type; - constexpr static const interval t_interval{ - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) - }; - constexpr static const interval u_interval{ - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) - }; - // when we add the temporary intervals above, we'll get a new interval - // with the correct range for the shifted result ! - constexpr static const checked_result> r_interval - = left_shift(t_interval, u_interval); - - constexpr static const interval result_interval = - r_interval.no_exception() ? - static_cast>(r_interval) - : - interval{} - ; - using type = typename result_type< - result_base_type, - result_interval.l, - result_interval.u - >::type; - - }; -*/ template struct left_shift_result { using t_base_type = typename base_type::type; @@ -448,7 +333,27 @@ struct automatic { /////////////////////////////////////////////////////////////////////// template - struct bitwise_result { + struct bitwise_and_result { + using t_base_type = typename base_type::type; + using u_base_type = typename base_type::type; + using type = typename boost::mpl::if_c< + (sizeof(t_base_type) > sizeof(u_base_type)), + u_base_type, + t_base_type + >::type; + }; + template + struct bitwise_or_result { + using t_base_type = typename base_type::type; + using u_base_type = typename base_type::type; + using type = typename boost::mpl::if_c< + (sizeof(t_base_type) > sizeof(u_base_type)), + t_base_type, + u_base_type + >::type; + }; + template + struct bitwise_xor_result { using t_base_type = typename base_type::type; using u_base_type = typename base_type::type; using type = typename boost::mpl::if_c< diff --git a/include/checked.hpp b/include/checked.hpp index d1874ea..23b9438 100644 --- a/include/checked.hpp +++ b/include/checked.hpp @@ -87,7 +87,7 @@ namespace detail { template constexpr static checked_result invoke(const T & t){ - // INT32-C Ensure that operations on signed + // INT32-C Ensure that operations on unsigned // integers do not overflow return t > std::numeric_limits::max() ? @@ -224,10 +224,10 @@ constexpr checked_result add( ) { static_assert(std::is_fundamental::value, "only intrinsic types permitted"); const checked_result rt(cast(t)); - if(! rt.no_exception() ) + if(rt.exception() ) return rt; const checked_result ru(cast(u)); - if(! ru.no_exception() ) + if(ru.exception() ) return ru; return detail::add(t, u); } @@ -408,8 +408,8 @@ namespace detail { < static_cast(std::numeric_limits::min()) ) ? checked_result( - exception_type::underflow_error, - "multiplication underflow" + exception_type::overflow_error, + "multiplication overflow" ) : checked_result(t * u) @@ -528,15 +528,15 @@ constexpr divide( "divide by zero" ); } - auto tx = cast(t); - auto ux = cast(u); - if(!tx.no_exception() - || !ux.no_exception()) + checked_result tx = cast(t); + checked_result ux = cast(u); + if(tx.exception() + || ux.exception()) return checked_result( exception_type::overflow_error, "failure converting argument types" ); - return detail::divide(tx.m_r, ux.m_r); + return detail::divide(tx, ux); } namespace detail_automatic { @@ -596,6 +596,7 @@ constexpr divide_automatic( //////////////////////////////// // safe modulus on unsafe types +// built-in abs isn't constexpr - so fix this here template constexpr std::make_unsigned_t abs(const T & t){ @@ -619,12 +620,21 @@ constexpr modulus( "denominator is zero" ); - return cast(abs(t) % abs(u)); + // why to we need abs here? the sign of the modulus is the sign + // consider -128 % -1 The result of this operation should be -1 + // but if I use t % u the x86 hardware uses the divide instruction + // capturing the modulus as a side effect. When it does this, it + // invokes the operation -128 / -1 -> 128 which overflows a signed type + // and provokes a hardware exception. We can fix this using abs() + // since -128 % -1 = -128 % 1 = 0 + return t % abs(u); } /////////////////////////////////// // shift operations +// left shift + namespace detail { // INT34-C C++ @@ -718,7 +728,6 @@ constexpr checked_left_shift( } // detail -// left shift template constexpr checked_result left_shift( const T & t, @@ -739,6 +748,7 @@ constexpr checked_result left_shift( return detail::checked_left_shift(t, u); } +// right shift namespace detail { // standard paragraph 5.8 / 3 @@ -836,19 +846,21 @@ namespace detail { check_bitwise_operand(const T & t){ return true; } -} +} // detail template constexpr checked_result bitwise_or( const T & t, const U & u ) { + /* if(! detail::check_bitwise_operand(t)) return checked_result( exception_type::domain_error, "bitwise operands cannot be negative" ); - + */ + const checked_result rt = cast(t); if(! rt.no_exception()) return rt; diff --git a/include/checked_result.hpp b/include/checked_result.hpp index 011c32b..e579ac4 100644 --- a/include/checked_result.hpp +++ b/include/checked_result.hpp @@ -32,20 +32,8 @@ struct checked_result { R m_r; char const * m_msg; }; - // constructors - // can't select constructor based on the current status of another - // checked_result object. So no copy constructor - /* - constexpr checked_result(const checked_result & r) : - m_e(r.m_e) - { - (no_exception()) ? - (m_r = r.m_r), 0 - : - (m_msg = r.m_msg), 0 - ; - } - */ + // constructors - use default copy constructor + // don't permit construction without initial value; checked_result() = delete; @@ -64,6 +52,8 @@ struct checked_result { // accesors constexpr operator R() const { + // can't invoke assert at compiler time so it's in compatible + // with constexpr //assert(no_exception()); return m_r; } @@ -97,7 +87,7 @@ struct checked_result { } template constexpr boost::logic::tribool operator<=(const checked_result & t) const { - return ! operator>(t) && ! operator<(t); + return ! operator>(t); } template constexpr boost::logic::tribool operator!=(const checked_result & t) const { @@ -130,6 +120,10 @@ template constexpr bool no_exception(const checked_result & cr){ return cr.no_exception(); } +template +constexpr bool exception(const checked_result & cr){ + return ! cr.no_exception(); +} } // numeric } // boost @@ -139,9 +133,9 @@ constexpr bool no_exception(const checked_result & cr){ namespace std { -template -std::ostream & operator<<( - std::ostream & os, +template +inline std::basic_ostream & operator<<( + std::basic_ostream & os, const boost::numeric::checked_result & r ){ if(r.no_exception()) @@ -151,10 +145,10 @@ std::ostream & operator<<( return os; } -template<> -std::ostream & operator<<( - std::ostream & os, - const boost::numeric::checked_result & r +template +inline std::basic_ostream & operator<<( + std::basic_ostream os, + const boost::numeric::checked_result & r ){ if(r.no_exception()) os << static_cast(r); @@ -163,10 +157,10 @@ std::ostream & operator<<( return os; } -template<> -std::ostream & operator<<( - std::ostream & os, - const boost::numeric::checked_result & r +template +inline std::basic_ostream & operator<<( + std::basic_ostream os, + const boost::numeric::checked_result & r ){ if(r.no_exception()) os << static_cast(r); @@ -175,29 +169,36 @@ std::ostream & operator<<( return os; } -/* -template -std::istream & operator>>(std::istream & is, const boost::numeric::checked_result & r){ +template +inline std::basic_istream & operator>>( + std::basic_istream & is, + boost::numeric::checked_result & r +){ is >> r.m_r; return is; } -template -std::istream & operator>>(std::istream & is, const boost::numeric::checked_result & r){ +template +inline std::basic_istream & operator>>( + std::basic_istream & is, + boost::numeric::checked_result & r +){ std::int16_t i; is >> i; r.m_r = i; return is; } -template -std::istream & operator>>(std::istream & is, const boost::numeric::checked_result & r){ +template +inline std::basic_istream & operator>>( + std::basic_istream & is, + boost::numeric::checked_result & r +){ std::uint16_t i; is >> i; r.m_r = i; return is; } -*/ } // std diff --git a/include/concept/exception_policy.hpp b/include/concept/exception_policy.hpp index 6e15a65..28ee551 100644 --- a/include/concept/exception_policy.hpp +++ b/include/concept/exception_policy.hpp @@ -20,6 +20,8 @@ struct ExceptionPolicy { const char * message; /* BOOST_CONCEPT_USAGE(ExceptionPolicy){ + EP::no_error(message); + EP::uninitialize_error(message); EP::overflow_error(message); EP::underflow_error(message); EP::range_error(message); diff --git a/include/concept/promotion_policy.hpp b/include/concept/promotion_policy.hpp index 50dc797..39b17c5 100644 --- a/include/concept/promotion_policy.hpp +++ b/include/concept/promotion_policy.hpp @@ -28,7 +28,9 @@ struct PromotionPolicy { using mod_type = typename PP::template modulus_result; using ls_type = typename PP::template left_shift_result; using rs_type = typename PP::template right_shift_result; - using bw_type = typename PP::template bitwise_result; + using baw_type = typename PP::template bitwise_and_result; + using bow_type = typename PP::template bitwise_or_result; + using bxw_type = typename PP::template bitwise_xor_result; checked_result::type> divide(){ return PP::template divide::type>(0, 0); diff --git a/include/cpp.hpp b/include/cpp.hpp index 206bd65..358620e 100755 --- a/include/cpp.hpp +++ b/include/cpp.hpp @@ -22,7 +22,7 @@ #include // integer type selection #include -#include "utility.hpp" +#include "utility.hpp" // rank #include "safe_common.hpp" #include "checked.hpp" @@ -50,26 +50,6 @@ struct cpp { using local_long_type = typename boost::int_t::exact; using local_long_long_type = typename boost::int_t::exact; - template - using rank = - typename boost::mpl::if_c< - sizeof(char) == sizeof(T), - std::integral_constant, - typename boost::mpl::if_c< - sizeof(short) == sizeof(T), - std::integral_constant, - typename boost::mpl::if_c< - sizeof(int) == sizeof(T), - std::integral_constant, - typename boost::mpl::if_c< - sizeof(long) == sizeof(T), - std::integral_constant, - typename boost::mpl::if_c< - sizeof(long long) == sizeof(T), - std::integral_constant, - void - >::type >::type >::type >::type >::type; - // section 4.5 integral promotions template using integral_promotion = typename boost::mpl::if_c< @@ -136,8 +116,8 @@ struct cpp { template using result_type = usual_arithmetic_conversions< - integral_promotion, - integral_promotion + integral_promotion::type>, + integral_promotion::type> >; template @@ -188,7 +168,15 @@ struct cpp { using type = result_type; }; template - struct bitwise_result { + struct bitwise_and_result { + using type = result_type; + }; + template + struct bitwise_or_result { + using type = result_type; + }; + template + struct bitwise_xor_result { using type = result_type; }; }; diff --git a/include/exception.hpp b/include/exception.hpp index 002eb6a..1fdc24b 100644 --- a/include/exception.hpp +++ b/include/exception.hpp @@ -12,8 +12,8 @@ // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -// contains operations for doing checked aritmetic on NATIVE -// C++ types. +// contains error indicators for results of doing checked +// arithmetic on native C++ types #include "concept/exception_policy.hpp" diff --git a/include/exception_policies.hpp b/include/exception_policies.hpp index 0f5a0f7..0cc5df3 100644 --- a/include/exception_policies.hpp +++ b/include/exception_policies.hpp @@ -1,5 +1,5 @@ -#ifndef BOOST_NUMERIC_POLICIES_HPP -#define BOOST_NUMERIC_POLICIES_HPP +#ifndef BOOST_NUMERIC_EXCEPTION_POLICIES_HPP +#define BOOST_NUMERIC_EXCEPTION_POLICIES_HPP // MS compatible compilers support #pragma once #if defined(_MSC_VER) && (_MSC_VER >= 1020) @@ -41,9 +41,9 @@ template< void (*FUNCTION_NO_EXCEPTION)(const char *), void (*FUNCTION_UNINITIALIZED)(const char *), void (*FUNCTION_OVERFLOW)(const char *), - void (*FUNCTION_UNDERFLOW)(const char *) = FUNCTION_OVERFLOW, - void (*FUNCTION_RANGE)(const char *) = FUNCTION_OVERFLOW, - void (*FUNCTION_DOMAIN)(const char *) = FUNCTION_OVERFLOW + void (*FUNCTION_UNDERFLOW)(const char *), + void (*FUNCTION_RANGE)(const char *), + void (*FUNCTION_DOMAIN)(const char *) > struct no_exception_support { static void no_error(const char * message) { @@ -100,4 +100,4 @@ struct trap_exception { } // namespace numeric } // namespace boost -#endif // BOOST_NUMERIC_POLICIES_HPP +#endif // BOOST_NUMERIC_EXCEPTION_POLICIES_HPP diff --git a/include/interval.hpp b/include/interval.hpp index 067811e..ef96636 100644 --- a/include/interval.hpp +++ b/include/interval.hpp @@ -15,9 +15,7 @@ #include #include #include -#include // quick_exit #include -#include #include #include @@ -81,7 +79,7 @@ struct interval { template constexpr interval::interval() : - l(std::numeric_limits::min()), + l(std::numeric_limits::lowest()), u(std::numeric_limits::max()) {} // account for the fact that for floats and doubles @@ -98,7 +96,7 @@ constexpr interval::interval() : u(std::numeric_limits::max()) {} -namespace { +namespace detail { template constexpr checked_result> failed_result( @@ -109,59 +107,36 @@ constexpr checked_result> failed_result( // create constexpr versions of stl algorthms which are not (yet) // constexpr. template -constexpr InputIt find_if_not(InputIt first, InputIt last, UnaryPredicate q) -{ +constexpr InputIt find_if(InputIt first, InputIt last, UnaryPredicate q){ for (; first != last; ++first) { - if (!q(*first)) { - return first; - } - } - return last; -} - -template -constexpr InputIt find(InputIt first, InputIt last, const T& value) -{ - for (; first != last; ++first) { - if (*first == value) { + if (q(*first)) { return first; } } return last; } +// helper function used below to select the minimum and maximum value +// from a list of values template -constexpr bool less_than( - const checked_result & lhs, - const checked_result & rhs -){ - return - (lhs.no_exception() && rhs.no_exception()) ? - safe_compare::less_than(lhs.m_r, rhs.m_r) - : - false - ; -} - -template -constexpr checked_result> select( +constexpr checked_result> minmax( const std::initializer_list> & acr ){ - typename std::initializer_list>::const_iterator const e = find_if_not( + typename std::initializer_list>::const_iterator const e = find_if( acr.begin(), acr.end(), - no_exception + exception ); return (acr.end() == e) ? checked_result>( - interval(std::minmax(acr, less_than)) + interval(std::minmax(acr, & safe_compare::less_than)) ) : failed_result// throw assert_failure([]{assert(!"input not in range");}) ; } -} // namespace +} // detail template constexpr checked_result> add( @@ -171,10 +146,10 @@ constexpr checked_result> add( // adapted from https://en.wikipedia.org/wiki/Interval_arithmetic checked_result lower = checked::add(static_cast(t.l), static_cast(u.l)); if(! lower.no_exception()) - return failed_result; + return detail::failed_result; checked_result upper = checked::add(static_cast(t.u), static_cast(u.u)); if(! upper.no_exception()) - return failed_result; + return detail::failed_result; return interval(lower, upper); } @@ -183,18 +158,17 @@ constexpr checked_result> subtract(const interval & t, const inte // adapted from https://en.wikipedia.org/wiki/Interval_arithmetic checked_result lower = checked::subtract(static_cast(t.l), static_cast(u.u)); if(! lower.no_exception()) - return failed_result; + return detail::failed_result; checked_result upper = checked::subtract(static_cast(t.u), static_cast(u.l)); if(! upper.no_exception()) - return failed_result; + return detail::failed_result; return interval(lower, upper); } template constexpr checked_result> multiply(const interval & t, const interval & u){ // adapted from https://en.wikipedia.org/wiki/Interval_arithmetic - - return select( + return detail::minmax( std::initializer_list> { checked::multiply(t.l, u.l), checked::multiply(t.l, u.u), @@ -212,7 +186,7 @@ constexpr inline checked_result> divide_nz( const interval & u ){ // adapted from https://en.wikipedia.org/wiki/Interval_arithmetic - return select( (u.u < 0 || u.l > 0) ? + return detail::minmax( (u.u < 0 || u.l > 0) ? std::initializer_list> { checked::divide(t.l, u.l), checked::divide(t.l, u.u), @@ -252,22 +226,26 @@ constexpr checked_result> modulus_nz( const interval & t, const interval & u ){ - return select( (u.u < 0 || u.l > 0) ? + // adapted from https://en.wikipedia.org/wiki/Modulo_operation + return detail::minmax( (u.l < 0 && 0 < u.u) ? + // if divisor range includes zero then we need include +1 and -1 + // among the possible divisors. But since % +1 and % -1 yield + // the same result, which is 0, then adding 0 to the possibilities + // is sufficient to make sure we've got the whole possible range std::initializer_list> { checked::modulus(t.l, u.l), checked::modulus(t.l, u.u), checked::modulus(t.u, u.l), - checked::modulus(t.u, u.u) + checked::modulus(t.u, u.u), + 0 // % 1 or % -1 } - : + : + // if divisor range doesn't include zero, the following should be + // sufficient. std::initializer_list> { checked::modulus(t.l, u.l), - checked::modulus(t.l, -1), - checked::modulus(t.u, u.l), - checked::modulus(t.u, 1), - checked::modulus(t.l, 1), checked::modulus(t.l, u.u), - checked::modulus(t.u, 1), + checked::modulus(t.u, u.l), checked::modulus(t.u, u.u) } ); @@ -292,7 +270,7 @@ constexpr checked_result> left_shift( const interval & t, const interval & u ){ - return select( std::initializer_list> { + return detail::minmax( std::initializer_list> { checked::left_shift(t.l, u.l), checked::left_shift(t.l, u.u), checked::left_shift(t.u, u.l), @@ -305,7 +283,7 @@ constexpr checked_result> right_shift( const interval & t, const interval & u ){ - return select( std::initializer_list> { + return detail::minmax( std::initializer_list> { checked::right_shift(t.l, u.l), checked::right_shift(t.l, u.u), checked::right_shift(t.u, u.l), @@ -323,7 +301,7 @@ constexpr checked_result> right_shift_positive( const U ux = boost::numeric::log(std::numeric_limits::max()); const U uu = safe_compare::less_than(u.u, ux) ? u.u : ux; - return select( std::initializer_list> { + return detail::minmax( std::initializer_list> { checked::right_shift(t.l, ul), checked::right_shift(t.l, uu), checked::right_shift(t.u, ul), @@ -336,10 +314,12 @@ constexpr checked_result> intersection( const interval & t, const interval & u ){ - const R rl = safe_compare::greater_than(t.l, u.l) ? t.l : u.l; - const R ru = safe_compare::less_than(t, u) ? t.u : u.u; + const checked_result rl = + checked::cast(safe_compare::greater_than(t.l, u.l) ? t.l : u.l); + const checked_result ru = + checked::cast(safe_compare::less_than(t, u) ? t.u : u.u); - if(safe_compare::greater_than(rl, ru)){ + if(rl > ru){ return checked_result>( exception_type::uninitialized, "null intersection" @@ -353,15 +333,11 @@ constexpr checked_result> union_interval( const interval & t, const interval & u ){ - const R rl = safe_compare::less_than(t.l, u.l) ? t.l : u.l; - const R ru = safe_compare::greater_than(t, u) ? t.u : u.u; + const checked_result rl = + checked::cast(safe_compare::less_than(t.l, u.l) ? t.l : u.l); + const checked_result ru = + checked::cast(safe_compare::greater_than(t, u) ? t.u : u.u); - if(safe_compare::greater_than(rl, ru)){ - return checked_result>( - exception_type::uninitialized, - "null intersection" - ); - } return interval(rl, ru); } @@ -444,20 +420,30 @@ constexpr boost::logic::tribool operator>=( namespace std { -template -std::ostream & operator<<(std::ostream & os, const boost::numeric::interval & i){ - os << "[" << i.l << "," << i.u << "]"; - return os; +template +inline std::basic_ostream & +operator<<( + std::basic_ostream & os, + const boost::numeric::interval & i +){ + return os << '[' << i.l << ',' << i.u << ']'; } - -template<> -std::ostream & operator<<(std::ostream & os, const boost::numeric::interval & i){ +template +inline std::basic_ostream & +operator<<( + std::basic_ostream & os, + const boost::numeric::interval & i +){ os << "[" << (unsigned)i.l << "," << (unsigned)i.u << "]"; return os; } -template<> -std::ostream & operator<<(std::ostream & os, const boost::numeric::interval & i){ +template +inline std::basic_ostream & +operator<<( + std::basic_ostream & os, + const boost::numeric::interval & i +){ os << "[" << (int)i.l << "," << (int)i.u << "]"; return os; } diff --git a/include/native.hpp b/include/native.hpp index 818fcd7..73af391 100644 --- a/include/native.hpp +++ b/include/native.hpp @@ -24,11 +24,6 @@ namespace boost { namespace numeric { struct native { - // Standard C++ type promotion for expressions doesn't depend - // on the operation being performed so we can just as well - // use any operation to determine it. We choose + for this - // purpose. - template using additive_operator_type = decltype( @@ -107,7 +102,15 @@ struct native { }; template - struct bitwise_result { + struct bitwise_or_result { + using type = bitwise_logic_operator_type; + }; + template + struct bitwise_and_result { + using type = bitwise_logic_operator_type; + }; + template + struct bitwise_xor_result { using type = bitwise_logic_operator_type; }; }; diff --git a/include/safe_base.hpp b/include/safe_base.hpp index 56c3ed0..e9b5897 100644 --- a/include/safe_base.hpp +++ b/include/safe_base.hpp @@ -103,30 +103,6 @@ constexpr T base_value( return static_cast(st); } -template< - class T, - T Min, - T Max, - class P, // promotion policy - class E // exception policy -> -std::ostream & operator<<( - std::ostream & os, - const safe_base & t -); - -template< - class T, - T Min, - T Max, - class P, // promotion policy - class E // exception policy -> -std::istream & operator>>( - std::istream & is, - safe_base & t -); - template< typename T, T N, @@ -146,20 +122,54 @@ template< class E // exception policy > class safe_base { +//public: BOOST_CONCEPT_ASSERT((Integer)); BOOST_CONCEPT_ASSERT((PromotionPolicy

)); BOOST_CONCEPT_ASSERT((ExceptionPolicy)); Stored m_t; - friend std::ostream & operator<< ( - std::ostream & os, + template< + class CharT, + class Traits + > + friend std::basic_ostream & operator<<( + std::basic_ostream & os, const safe_base & t - ); + ){ + return os << ( + (::std::is_same::value + || ::std::is_same::value + || ::std::is_same::value + ) ? + static_cast(t.m_t) + : + t.m_t + ); + }; - friend std::istream & operator>> ( - std::istream & is, + template< + class CharT, + class Traits + > + friend std::basic_istream & operator>>( + std::basic_istream & is, safe_base & t - ); + ){ + int tx; + if(::std::is_same::value + || ::std::is_same::value + || ::std::is_same::value + ){ + is >> tx; + t = tx; + } + else{ + is >> t; + } + if(is.fail()) + E::domain_error("error in file input"); + return is; + } template< class StoredX, @@ -170,9 +180,7 @@ class safe_base { > friend class safe_base; - friend class std::numeric_limits< - safe_base - >; + friend class std::numeric_limits; template constexpr Stored validated_cast(const T & t) const; @@ -188,7 +196,9 @@ public: // constructors constexpr explicit safe_base(const Stored & rhs, std::false_type); + // default constructor + /* constexpr explicit safe_base() { // this permits creating of invalid instances. This is inline // with C++ built-in but violates the premises of the whole library @@ -200,6 +210,8 @@ public: // "overhead" // still pending on this. } + */ + constexpr safe_base() = default; template constexpr /*explicit*/ safe_base(const T & t); @@ -237,10 +249,10 @@ public: operator=(const safe_base & rhs); // mutating unary operators - safe_base operator++(){ // pre increment + safe_base & operator++(){ // pre increment return *this = *this + 1; } - safe_base operator--(){ // pre increment + safe_base & operator--(){ // pre decrement return *this = *this - 1; } safe_base operator++(int){ // post increment @@ -248,18 +260,27 @@ public: ++(*this); return old_t; } - safe_base & operator--(int){ // post decrement + safe_base operator--(int){ // post decrement safe_base old_t = *this; --(*this); return old_t; } // non mutating unary operators - constexpr auto operator-() const { // unary minus - return 0 - *this; - } - constexpr auto operator+() const { // unary plus + constexpr safe_base & operator+() const { // unary plus return *this; } + // after much consideration, I've permited the resulting value of a unary + // - to change the type. The C++ standard does invoke integral promotions + // so it's changing the type as well. + constexpr auto operator-() const { // unary minus + // if this is a unsigned type and the promotion policy is native + // the result will be unsigned. But then the operation will fail + // according to the requirements of arithmetic correctness. + // if this is an unsigned type and the promotion policy is automatic. + // the result will be signed. + return 0 - *this; + } + // argument or this is the same as the argument above template constexpr auto operator~() const { // complement return ~Stored(0u) ^ *this; diff --git a/include/safe_base_operations.hpp b/include/safe_base_operations.hpp index 07162de..daecba0 100644 --- a/include/safe_base_operations.hpp +++ b/include/safe_base_operations.hpp @@ -14,13 +14,10 @@ #include #include // is_base_of, is_same, enable_if -#include -#include #include #include #include - #include // lazy_enable_if #include "safe_base.hpp" @@ -28,6 +25,7 @@ #include "safe_compare.hpp" #include "checked_result.hpp" #include "interval.hpp" +#include "checked.hpp" namespace boost { namespace numeric { @@ -241,10 +239,8 @@ struct common_promotion_policy { }; -// Note: the following global operators will be only found via -// argument dependent lookup. So they won't conflict any -// other global operators for types in namespaces other than -// boost::numeric +// Note: the following global operators will be found via +// argument dependent lookup. ///////////////////////////////////////////////////////////////// // addition @@ -252,38 +248,29 @@ struct common_promotion_policy { template struct addition_result { using promotion_policy = typename common_promotion_policy::type; - using t_base_type = typename base_type::type; - using u_base_type = typename base_type::type; using result_base_type = typename promotion_policy::template addition_result< - t_base_type, - u_base_type + T, + U >::type; - // filter out case were overflow cannot occur - // note: subtle trickery. Suppose t is safe_range. Then - // std::numeric_limits::min() will be safe_range t_interval{ + constexpr static const interval::type> t_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - constexpr static const interval u_interval{ + constexpr static const interval::type> u_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - // when we add the temporary intervals above, we'll get a new interval // with the correct range for the sum ! constexpr static const checked_result> r_interval = add(t_interval, u_interval); constexpr static bool exception_possible() { - return ! r_interval.no_exception(); + return r_interval.exception(); } - constexpr static const interval type_interval = exception_possible() ? interval{} @@ -292,6 +279,7 @@ struct addition_result { ; using exception_policy = typename common_exception_policy::type; + struct safe_type { using type = safe_base< result_base_type, @@ -345,11 +333,11 @@ struct addition_result { }; template -typename boost::lazy_enable_if_c< +typename std::enable_if< boost::numeric::is_safe::value || boost::numeric::is_safe::value , - addition_result + typename addition_result::type >::type constexpr inline operator+(const T & t, const U & u){ using ar = addition_result; @@ -363,11 +351,11 @@ constexpr inline operator+(const T & t, const U & u){ } template -typename boost::lazy_enable_if_c< +typename std::enable_if< boost::numeric::is_safe::value || boost::numeric::is_safe::value , - boost::mpl::identity + T >::type constexpr inline operator+=(T & t, const U & u){ t = static_cast(t + u); @@ -406,7 +394,6 @@ struct subtraction_result { constexpr static bool exception_possible() { return ! r_interval.no_exception(); } - constexpr static const interval type_interval = exception_possible() ? interval{} @@ -468,11 +455,11 @@ struct subtraction_result { }; template -typename boost::lazy_enable_if_c< +typename std::enable_if< boost::numeric::is_safe::value || boost::numeric::is_safe::value , - subtraction_result + typename subtraction_result::type >::type constexpr operator-(const T & t, const U & u){ using sr = subtraction_result; @@ -486,11 +473,11 @@ constexpr operator-(const T & t, const U & u){ } template -typename boost::lazy_enable_if_c< +typename std::enable_if< boost::numeric::is_safe::value || boost::numeric::is_safe::value , - boost::mpl::identity + T >::type constexpr inline operator-=(T & t, const U & u){ t = static_cast(t - u); @@ -600,11 +587,11 @@ struct multiplication_result { }; template -typename boost::lazy_enable_if_c< +typename std::enable_if< boost::numeric::is_safe::value || boost::numeric::is_safe::value , - multiplication_result + typename multiplication_result::type >::type constexpr operator*(const T & t, const U & u){ // argument dependent lookup should guarentee that we only get here @@ -619,11 +606,11 @@ constexpr operator*(const T & t, const U & u){ } template -typename boost::lazy_enable_if_c< +typename std::enable_if< boost::numeric::is_safe::value || boost::numeric::is_safe::value , - boost::mpl::identity + T >::type constexpr inline operator*=(T & t, const U & u){ t = static_cast(t * u); @@ -667,7 +654,7 @@ struct division_result { ); return // if over/under flow or domain error possible - ! r_interval.no_exception() + r_interval.exception() // if the denominator can contain zero || (u_interval.l <= 0 && u_interval.u >= 0 ) ; @@ -738,14 +725,13 @@ struct division_result { }; template -typename boost::lazy_enable_if_c< +typename std::enable_if< boost::numeric::is_safe::value || boost::numeric::is_safe::value , - division_result + typename division_result::type >::type constexpr operator/(const T & t, const U & u){ - // argument dependent lookup should guarentee that we only get here using dr = division_result; return dr::make( dr::return_value( @@ -757,11 +743,11 @@ constexpr operator/(const T & t, const U & u){ } template -typename boost::lazy_enable_if_c< +typename std::enable_if< boost::numeric::is_safe::value || boost::numeric::is_safe::value , - boost::mpl::identity + T >::type constexpr inline operator/=(T & t, const U & u){ t = static_cast(t / u); @@ -801,7 +787,7 @@ struct modulus_result { constexpr static bool exception_possible() { return // if over/under flow or domain error possible - ! r_interval.no_exception() + r_interval.exception() // if the denominator can contain zero || (u_interval.l <= 0 && u_interval.u >=0 ) ; @@ -872,11 +858,11 @@ struct modulus_result { }; template -typename boost::lazy_enable_if_c< +typename std::enable_if< boost::numeric::is_safe::value || boost::numeric::is_safe::value , - modulus_result + typename modulus_result::type >::type inline operator%(const T & t, const U & u){ using mr = modulus_result; @@ -890,11 +876,11 @@ inline operator%(const T & t, const U & u){ } template -typename boost::lazy_enable_if_c< +typename std::enable_if< boost::numeric::is_safe::value || boost::numeric::is_safe::value , - boost::mpl::identity + T >::type constexpr inline operator%=(T & t, const U & u){ t = static_cast(t % u); @@ -905,11 +891,11 @@ constexpr inline operator%=(T & t, const U & u){ // comparison template -typename boost::lazy_enable_if_c< +typename std::enable_if< boost::numeric::is_safe::value || boost::numeric::is_safe::value , - boost::mpl::identity + bool >::type constexpr operator<(const T & lhs, const U & rhs) { using t_base_type = typename base_type::type; @@ -926,61 +912,48 @@ constexpr operator<(const T & lhs, const U & rhs) { base_value(std::numeric_limits::max()) ); - constexpr const boost::logic::tribool r = - t_interval < u_interval; - - return - // if the ranges don't overlap - (! boost::logic::indeterminate(r)) ? - // values in those ranges can't be equal - false - : - // otherwise we have to check - safe_compare::less_than(base_value(lhs), base_value(rhs)); - ; + return safe_compare::less_than(base_value(lhs), base_value(rhs)); ; } template -typename boost::lazy_enable_if_c< +typename std::enable_if< boost::numeric::is_safe::value || boost::numeric::is_safe::value , - boost::mpl::identity + bool >::type constexpr operator>(const T & lhs, const U & rhs) { - using t_base_type = typename base_type::type; - using u_base_type = typename base_type::type; - - constexpr const interval t_interval( - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) - ); - - constexpr const interval u_interval( - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) - ); - - constexpr const boost::logic::tribool r = - t_interval > u_interval; - - return - // if the ranges don't overlap - (! boost::logic::indeterminate(r)) ? - // values in those ranges can't be equal - false - : - // otherwise we have to check - safe_compare::greater_than(base_value(lhs), base_value(rhs)); - ; + return rhs < lhs; } template -typename boost::lazy_enable_if_c< +typename std::enable_if< boost::numeric::is_safe::value || boost::numeric::is_safe::value , - boost::mpl::identity + bool +>::type +constexpr operator>=(const T & lhs, const U & rhs) { + return ! ( rhs < lhs ); +} + +template +typename std::enable_if< + boost::numeric::is_safe::value + || boost::numeric::is_safe::value + , + bool +>::type +constexpr operator<=(const T & lhs, const U & rhs) { + return ! ( lhs > rhs ); +} + +template +typename std::enable_if< + boost::numeric::is_safe::value + || boost::numeric::is_safe::value + , + bool >::type constexpr operator==(const T & lhs, const U & rhs) { using t_base_type = typename base_type::type; @@ -1008,38 +981,16 @@ constexpr operator==(const T & lhs, const U & rhs) { } template -typename boost::lazy_enable_if_c< +typename std::enable_if< boost::numeric::is_safe::value || boost::numeric::is_safe::value , - boost::mpl::identity + bool >::type constexpr operator!=(const T & lhs, const U & rhs) { return ! (lhs == rhs); } -template -typename boost::lazy_enable_if_c< - boost::numeric::is_safe::value - || boost::numeric::is_safe::value - , - boost::mpl::identity ->::type -constexpr operator>=(const T & lhs, const U & rhs) { - return ! ( rhs < lhs ); -} - -template -typename boost::lazy_enable_if_c< - boost::numeric::is_safe::value - || boost::numeric::is_safe::value - , - boost::mpl::identity ->::type -constexpr operator<=(const T & lhs, const U & rhs) { - return ! ( lhs > rhs ); -} - ///////////////////////////////////////////////////////////////// // shift operators @@ -1144,11 +1095,11 @@ constexpr inline operator<<(const T & t, const U & u){ } template -typename boost::lazy_enable_if_c< +typename std::enable_if< boost::numeric::is_safe::value || boost::numeric::is_safe::value , - boost::mpl::identity + T >::type constexpr inline operator<<=(T & t, const U & u){ t = static_cast(t << u); @@ -1177,11 +1128,11 @@ struct right_shift_result { base_value(std::numeric_limits::max()) }; - constexpr static const checked_result> r_interval { + constexpr static const checked_result> r_interval{ right_shift(t_interval, u_interval) }; - constexpr static bool exception_possible() { + constexpr static bool exception_possible(){ return ! r_interval.no_exception(); } @@ -1227,7 +1178,7 @@ struct right_shift_result { template typename boost::lazy_enable_if_c< // handle safe << int, int << safe, safe << safe - // exclude std::ostream << ... + // exclude std::istream << ... (! std::is_base_of::value) && ( boost::numeric::is_safe::value @@ -1255,11 +1206,11 @@ constexpr inline operator>>(const T & t, const U & u){ } template -typename boost::lazy_enable_if_c< +typename std::enable_if< boost::numeric::is_safe::value || boost::numeric::is_safe::value , - boost::mpl::identity + T >::type constexpr inline operator>>=(T & t, const U & u){ t = static_cast(t >> u); @@ -1269,13 +1220,6 @@ constexpr inline operator>>=(T & t, const U & u){ ///////////////////////////////////////////////////////////////// // bitwise operators -// I considered making this illegal on signed integers because -// I'm thinking it doesn't make much sense from the point of -// view of an applications programmer. But after running some -// test on user code I see it's going to create problems. So -// I'm going to follow strictly the standard sections 5.11-5.13 -// related to bitwise operators - // operator | template struct bitwise_or_result { @@ -1283,20 +1227,15 @@ struct bitwise_or_result { using t_base_type = typename base_type::type; using u_base_type = typename base_type::type; using result_base_type = - typename promotion_policy::template bitwise_result< + typename promotion_policy::template bitwise_or_result< t_base_type, u_base_type >::type; - constexpr static result_base_type r = - safe_compare::greater_than( - base_value(std::numeric_limits::max()), - base_value(std::numeric_limits::max()) - ) ? - base_value(std::numeric_limits::max()) - : - base_value(std::numeric_limits::max()) - ; + constexpr const static checked_result r = checked::bitwise_or( + base_value(std::numeric_limits::max()), + base_value(std::numeric_limits::max()) + ); using exception_policy = typename common_exception_policy::type; using type = safe_base< @@ -1309,21 +1248,13 @@ struct bitwise_or_result { }; template -typename boost::lazy_enable_if_c< +typename std::enable_if< boost::numeric::is_safe::value || boost::numeric::is_safe::value , - bitwise_or_result + typename bitwise_or_result::type >::type constexpr inline operator|(const T & t, const U & u){ - // see above - /* - static_assert( - ! std::numeric_limits::is_signed - && ! std::numeric_limits::is_signed, - "bitwise or on signed integer types is not always well defined" - ); - */ using bwr = bitwise_or_result; using result_base_type = typename bwr::result_base_type; @@ -1340,11 +1271,11 @@ constexpr inline operator|(const T & t, const U & u){ } template -typename boost::lazy_enable_if_c< +typename std::enable_if< boost::numeric::is_safe::value || boost::numeric::is_safe::value , - boost::mpl::identity + T >::type constexpr inline operator|=(T & t, const U & u){ t = static_cast(t | u); @@ -1359,7 +1290,7 @@ struct bitwise_and_result { using t_base_type = typename base_type::type; using u_base_type = typename base_type::type; using result_base_type = - typename promotion_policy::template bitwise_result< + typename promotion_policy::template bitwise_and_result< t_base_type, u_base_type >::type; @@ -1384,21 +1315,13 @@ struct bitwise_and_result { }; template -typename boost::lazy_enable_if_c< +typename std::enable_if< boost::numeric::is_safe::value || boost::numeric::is_safe::value , - bitwise_and_result + typename bitwise_and_result::type >::type constexpr inline operator&(const T & t, const U & u){ - // see above - /* - static_assert( - ! std::numeric_limits::is_signed - && ! std::numeric_limits::is_signed, - "bitwise and on signed integer types is not always well defined" - ); - */ using bwr = bitwise_and_result; using result_base_type = typename bwr::result_base_type; @@ -1415,11 +1338,11 @@ constexpr inline operator&(const T & t, const U & u){ } template -typename boost::lazy_enable_if_c< +typename std::enable_if< boost::numeric::is_safe::value || boost::numeric::is_safe::value , - boost::mpl::identity + T >::type constexpr inline operator&=(T & t, const U & u){ t = static_cast(t & u); @@ -1428,22 +1351,45 @@ constexpr inline operator&=(T & t, const U & u){ // operator ^ template -typename boost::lazy_enable_if_c< +struct bitwise_xor_result { + using exception_policy = typename common_exception_policy::type; + using promotion_policy = typename common_promotion_policy::type; + using t_base_type = typename base_type::type; + using u_base_type = typename base_type::type; + using result_base_type = + typename promotion_policy::template bitwise_xor_result< + t_base_type, + u_base_type + >::type; + + constexpr static result_base_type r = + safe_compare::less_than( + base_value(std::numeric_limits::max()), + base_value(std::numeric_limits::max()) + ) ? + base_value(std::numeric_limits::max()) + : + base_value(std::numeric_limits::max()) + ; + + using type = safe_base< + result_base_type, + 0, + r, + promotion_policy, + exception_policy + >; +}; + +template +typename std::enable_if< boost::numeric::is_safe::value || boost::numeric::is_safe::value , - bitwise_or_result + typename bitwise_xor_result::type >::type constexpr inline operator^(const T & t, const U & u){ - // see above - /* - static_assert( - ! std::numeric_limits::is_signed - && ! std::numeric_limits::is_signed, - "bitwise xor on signed integer types is not always well defined" - ); - */ - using bwr = bitwise_or_result; + using bwr = bitwise_xor_result; using result_base_type = typename bwr::result_base_type; const checked_result r = @@ -1456,79 +1402,17 @@ constexpr inline operator^(const T & t, const U & u){ } template -typename boost::lazy_enable_if_c< +typename std::enable_if< boost::numeric::is_safe::value || boost::numeric::is_safe::value , - boost::mpl::identity + T >::type constexpr inline operator^=(T & t, const U & u){ t = static_cast(t ^ u); return t; } -///////////////////////////////////////////////////////////////// -// stream operators - -template< - class T, - T Min, - T Max, - class P, // promotion polic - class E // exception policy -> -std::ostream & operator<<( - std::ostream & os, - const safe_base & t -){ - return os << ( - (std::is_same::value - || std::is_same::value - ) ? - static_cast(t.m_t) - : - t.m_t - ); -} - -namespace { - template - struct Temp { - T value; - }; // primary template - template<> struct Temp { - int value; - }; // secondary template - template<> struct Temp { - int value; - }; // secondary template - template<> struct Temp { - int value; - }; // secondary template - template<> struct Temp { - int value; - }; // secondary template -} - -template< - class T, - T Min, - T Max, - class P, // promotion polic - class E // exception policy -> -std::istream & operator>>( - std::istream & is, - safe_base & t -){ - Temp tx; - is >> tx.value; - if(is.fail()) - E::domain_error("error in file input"); - t = tx.value; - return is; -} - } // numeric } // boost diff --git a/include/safe_common.hpp b/include/safe_common.hpp index 4bfa198..e9ab94e 100644 --- a/include/safe_common.hpp +++ b/include/safe_common.hpp @@ -42,20 +42,6 @@ struct get_exception_policy { using type = void; }; -// used for debugging -// usage - print_type; -// provokes error message with name of type T - -template -using print_type = typename Tx::error_message; - -template -struct print_value -{ - enum test : unsigned char { - value = N + 256 - }; -}; } // numeric } // boost diff --git a/include/safe_integer.hpp b/include/safe_integer.hpp index 3b492d2..194de58 100644 --- a/include/safe_integer.hpp +++ b/include/safe_integer.hpp @@ -28,8 +28,8 @@ template < > using safe = safe_base< T, - std::numeric_limits::min(), - std::numeric_limits::max(), + ::std::numeric_limits::min(), + ::std::numeric_limits::max(), P, E >; diff --git a/include/safe_literal.hpp b/include/safe_literal.hpp index 13d6c36..4a03ee0 100644 --- a/include/safe_literal.hpp +++ b/include/safe_literal.hpp @@ -13,6 +13,8 @@ // http://www.boost.org/LICENSE_1_0.txt) #include // for intmax_t/uintmax_t +#include +#include #include #include "utility.hpp" @@ -52,9 +54,9 @@ constexpr T base_value( return N; } -template -std::ostream & operator<<( - std::ostream & os, +template +inline std::basic_ostream & operator<<( + std::basic_ostream & os, const safe_literal_impl & t ){ return os << ( @@ -69,10 +71,25 @@ std::ostream & operator<<( template class safe_literal_impl { - friend std::ostream & operator<< ( - std::ostream & os, + + template< + class CharT, + class Traits + > + friend std::basic_ostream & operator<<( + std::basic_ostream & os, const safe_literal_impl & t - ); + ){ + return os << ( + (::std::is_same::value + || ::std::is_same::value + || ::std::is_same::value + ) ? + static_cast(N) + : + N + ); + }; public: @@ -100,8 +117,8 @@ public: template< std::intmax_t N, - class P = native, - class E = throw_exception + class P = void, + class E = void > using safe_signed_literal = safe_literal_impl< typename boost::numeric::signed_stored_type, diff --git a/include/utility.hpp b/include/utility.hpp index 298c76c..4f8a303 100644 --- a/include/utility.hpp +++ b/include/utility.hpp @@ -18,6 +18,40 @@ namespace boost { namespace numeric { + // section 4.13 integer conversion rank + template + struct rank; + + template<> + struct rank : public std::integral_constant{}; + template<> + struct rank : public std::integral_constant{}; + template<> + struct rank : public std::integral_constant{}; + + template<> + struct rank : public std::integral_constant{}; + + template<> + struct rank : public std::integral_constant{}; + template<> + struct rank : public std::integral_constant{}; + + template<> + struct rank : public std::integral_constant{}; + template<> + struct rank : public std::integral_constant{}; + + template<> + struct rank : public std::integral_constant{}; + template<> + struct rank : public std::integral_constant{}; + + template<> + struct rank : public std::integral_constant{}; + template<> + struct rank : public std::integral_constant{}; + // the number of bits required to render the value in x template typename std::enable_if< @@ -62,6 +96,22 @@ namespace numeric { using unsigned_stored_type = typename boost::uint_t< std::max({log(Min), log(Max)}) >::least ; + + // used for debugging + // usage - print_type; + // provokes error message with name of type T + + template + using print_type = typename Tx::error_message; + + template + struct print_value + { + enum test : unsigned char { + value = N + 256 + }; + }; + } // numeric } // boost diff --git a/test/test_add.hpp b/test/test_add.hpp index c2caf2c..0193b9c 100644 --- a/test/test_add.hpp +++ b/test/test_add.hpp @@ -9,7 +9,6 @@ #include #include -#include #include "../include/safe_integer.hpp" @@ -21,22 +20,24 @@ bool test_add( const char *av2, char expected_result ){ - std::cout - << "testing " - << av1 << " + " << av2 - << std::endl; + std::cout << "testing"<< std::endl; { safe_t t1 = v1; using result_type = decltype(t1 + v2); + std::cout << "safe<" << av1 << "> + " << av2 << " -> "; + static_assert( + boost::numeric::is_safe >::value, + "safe_t not safe!" + ); static_assert( boost::numeric::is_safe::value, "Expression failed to return safe type" ); result_type result; - try{ result = t1 + v2; - + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; if(expected_result == 'x'){ std::cout << "failed to detect error in addition " @@ -50,7 +51,9 @@ bool test_add( return false; } } - catch(std::exception e){ + catch(std::exception){ + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; if(expected_result == '.'){ std::cout << "erroneously detected error in addition " @@ -60,7 +63,7 @@ bool test_add( try{ t1 + v2; } - catch(std::exception e){} + catch(std::exception){} return false; } } @@ -68,6 +71,11 @@ bool test_add( { safe_t t2 = v2; using result_type = decltype(v1 + t2); + std::cout << av1 << " + " << "safe<" << av2 << "> -> "; + static_assert( + boost::numeric::is_safe >::value, + "safe_t not safe!" + ); static_assert( boost::numeric::is_safe::value, "Expression failed to return safe type" @@ -76,6 +84,8 @@ bool test_add( try{ result = v1 + t2; + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; if(expected_result == 'x'){ std::cout << "failed to detect error in addition " @@ -90,6 +100,8 @@ bool test_add( } } catch(std::exception){ + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; if(expected_result == '.'){ std::cout << "erroneously detected error in addition " @@ -108,6 +120,7 @@ bool test_add( safe_t t1 = v1; safe_t t2 = v2; using result_type = decltype(t1 + t2); + std::cout << "safe<" << av1 << "> + " << "safe<" << av2 << "> -> "; static_assert( boost::numeric::is_safe::value, "Expression failed to return safe type" @@ -116,6 +129,8 @@ bool test_add( try{ result = t1 + t2; + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; if(expected_result == 'x'){ std::cout << "failed to detect error in addition " @@ -130,6 +145,8 @@ bool test_add( } } catch(std::exception){ + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; if(expected_result == '.'){ std::cout << "erroneously detected error in addition " diff --git a/test/test_add_automatic.cpp b/test/test_add_automatic.cpp index 5b7cd21..56257c2 100644 --- a/test/test_add_automatic.cpp +++ b/test/test_add_automatic.cpp @@ -6,7 +6,6 @@ #include #include -#include #include "../include/safe_integer.hpp" #include "../include/automatic.hpp" @@ -29,23 +28,23 @@ const char *test_addition_result[VALUE_ARRAY_SIZE] = { // 0 0 0 0 // 01234567012345670123456701234567 // 01234567890123456789012345678901 -/* 0*/ ".............x.................x", -/* 1*/ ".............x.................x", -/* 2*/ "..............x.............xxxx", -/* 3*/ "..............x.............xxxx", -/* 4*/ ".............x.................x", -/* 5*/ ".............x.................x", -/* 6*/ "..............x.............xxxx", -/* 7*/ "..............x.............xxxx", +/* 0*/ ".............x...............xxx", +/* 1*/ ".............x...............xxx", +/* 2*/ "..............x...............xx", +/* 3*/ "..............x...............xx", +/* 4*/ ".............x...............xxx", +/* 5*/ ".............x...............xxx", +/* 6*/ "..............x...............xx", +/* 7*/ "..............x...............xx", -/* 8*/ ".............x.................x", -/* 9*/ ".............x.................x", -/*10*/ "..............x.............xxxx", -/*11*/ "..............x.............xxxx", -/*12*/ ".............x.................x", -/*13*/ "xx..xx..xx..xx..xxxxxxxxxxxx...x", -/*14*/ "..xx..xx..xx..xx............xxxx", -/*15*/ "..............x.............xxxx", +/* 8*/ ".............x...............xxx", +/* 9*/ ".............x...............xxx", +/*10*/ "..............x...............xx", +/*11*/ "..............x...............xx", +/*12*/ ".............x...............xxx", +/*13*/ "xx..xx..xx..xx..xxxxxxxxxxxxxxxx", +/*14*/ "..xx..xx..xx..xx..............xx", +/*15*/ "..............x...............xx", // 0 0 0 0 // 01234567012345670123456701234567 @@ -63,9 +62,9 @@ const char *test_addition_result[VALUE_ARRAY_SIZE] = { /*25*/ ".............x.................x", /*26*/ ".............x.................x", /*27*/ ".............x.................x", -/*28*/ "..xx..xx..xx..xx...............x", -/*29*/ "..xx..xx..xx..xx...............x", -/*30*/ "..xx..xx..xx..xx..............xx", +/*28*/ ".............x.................x", +/*29*/ "xx..xx..xx..xx.................x", +/*30*/ "xxxxxxxxxxxxxxxx..............xx", /*31*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }; @@ -80,7 +79,7 @@ const char *test_addition_result[VALUE_ARRAY_SIZE] = { /**/ #define TESTX(value_index1, value_index2) \ - (std::cout << value_index1 << ',' << value_index2 << std::endl); \ + (std::cout << value_index1 << ',' << value_index2 << ','); \ TEST_IMPL( \ BOOST_PP_ARRAY_ELEM(value_index1, VALUES), \ BOOST_PP_ARRAY_ELEM(value_index2, VALUES), \ diff --git a/test/test_auto.cpp b/test/test_auto.cpp index fbb6e40..68c2516 100644 --- a/test/test_auto.cpp +++ b/test/test_auto.cpp @@ -100,4 +100,4 @@ int main(){ test_log(); */ test_auto(); -} \ No newline at end of file +} diff --git a/test/test_checked_add.cpp b/test/test_checked_add.cpp index 2380fd2..a5dba0a 100644 --- a/test/test_checked_add.cpp +++ b/test/test_checked_add.cpp @@ -48,7 +48,7 @@ bool test_checked_add( return false; } else - if(! result.no_exception() + if(result.exception() && expected_result != 'x'){ std::cout << "erroneously detected error " diff --git a/test/test_checked_divide_automatic.cpp b/test/test_checked_divide_automatic.cpp index 0936916..46c4764 100644 --- a/test/test_checked_divide_automatic.cpp +++ b/test/test_checked_divide_automatic.cpp @@ -5,7 +5,7 @@ // http://www.boost.org/LICENSE_1_0.txt) #include -#include +//#include #include // EXIT_SUCCESS #include #include // int_t::fast @@ -50,7 +50,7 @@ bool test_checked_divide( return false; } else - if(! result.no_exception() + if(result.exception() && expected_result != 'x'){ std::cout << "erroneously detected error " diff --git a/test/test_checked_divide_native.cpp b/test/test_checked_divide_native.cpp index fbaec17..4c04150 100644 --- a/test/test_checked_divide_native.cpp +++ b/test/test_checked_divide_native.cpp @@ -5,7 +5,7 @@ // http://www.boost.org/LICENSE_1_0.txt) #include -#include +//#include #include // EXIT_SUCCESS #include diff --git a/test/test_checked_result.cpp b/test/test_checked_result.cpp index 7ef5480..352d408 100644 --- a/test/test_checked_result.cpp +++ b/test/test_checked_result.cpp @@ -32,4 +32,4 @@ int main(){ test1() && test2() ) ? 0 : 1; -} \ No newline at end of file +} diff --git a/test/test_checked_subtract.cpp b/test/test_checked_subtract.cpp index ba256fd..13c13b6 100644 --- a/test/test_checked_subtract.cpp +++ b/test/test_checked_subtract.cpp @@ -42,7 +42,7 @@ bool test_checked_subtract( return false; } else - if(! result.no_exception() + if(result.exception() && expected_result != 'x'){ std::cout << "erroneously detected error " diff --git a/test/test_compare.cpp b/test/test_compare.cpp index 32ad7ec..748a944 100644 --- a/test/test_compare.cpp +++ b/test/test_compare.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include "../include/safe_integer.hpp" diff --git a/test/test_divide_automatic.cpp b/test/test_divide_automatic.cpp index e02f6f2..781b3ec 100644 --- a/test/test_divide_automatic.cpp +++ b/test/test_divide_automatic.cpp @@ -6,7 +6,7 @@ #include #include -#include +//#include #include "../include/safe_integer.hpp" #include "../include/automatic.hpp" diff --git a/test/test_divide_native.cpp b/test/test_divide_native.cpp index 3c4fca2..3772300 100644 --- a/test/test_divide_native.cpp +++ b/test/test_divide_native.cpp @@ -6,7 +6,7 @@ #include #include -#include +//#include #include "../include/safe_integer.hpp" diff --git a/test/test_interval.cpp b/test/test_interval.cpp index 3e1db5c..8de3704 100644 --- a/test/test_interval.cpp +++ b/test/test_interval.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include // demangle #include // max, min #include @@ -358,4 +358,4 @@ int main(){ ); std::cout << (rval ? "success!" : "failure") << std::endl; return rval ? EXIT_SUCCESS : EXIT_FAILURE; -} \ No newline at end of file +} diff --git a/test/test_left_shift.hpp b/test/test_left_shift.hpp index c560b58..88e66dc 100644 --- a/test/test_left_shift.hpp +++ b/test/test_left_shift.hpp @@ -9,7 +9,7 @@ #include #include -#include +//#include #include "../include/safe_integer.hpp" diff --git a/test/test_modulus.hpp b/test/test_modulus.hpp index 6568258..7e10b16 100644 --- a/test/test_modulus.hpp +++ b/test/test_modulus.hpp @@ -9,7 +9,7 @@ #include #include -#include +//#include #include "../include/safe_integer.hpp" @@ -21,23 +21,25 @@ bool test_modulus( const char *av2, char expected_result ){ - std::cout - << "testing " - << av1 << " % " << av2 - << std::endl; + std::cout << "testing"<< std::endl; { safe_t t1 = v1; - safe_t result; - + using result_type = decltype(t1 % v2); + std::cout << "safe<" << av1 << "> & " << av2 << " -> "; + static_assert( + boost::numeric::is_safe >::value, + "safe_t not safe!" + ); + static_assert( + boost::numeric::is_safe::value, + "Expression failed to return safe type" + ); + result_type result; try{ result = t1 % v2; - - static_assert( - boost::numeric::is_safe::value, - "Expression failed to return safe type" - ); - - if(expected_result != '.'){ + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; + if(expected_result == 'x'){ std::cout << "failed to detect error in modulus " << std::hex << result << "(" << std::dec << result << ")" @@ -50,8 +52,10 @@ bool test_modulus( return false; } } - catch(std::exception){ - if(expected_result != 'x'){ + catch(std::exception e){ + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; + if(expected_result == '.'){ std::cout << "erroneously detected error in modulus " << std::hex << result << "(" << std::dec << result << ")" @@ -67,17 +71,23 @@ bool test_modulus( } { safe_t t2 = v2; - safe_t result; + using result_type = decltype(v1 + t2); + std::cout << av1 << " % " << "safe<" << av2 << "> -> "; + static_assert( + boost::numeric::is_safe >::value, + "safe_t not safe!" + ); + static_assert( + boost::numeric::is_safe::value, + "Expression failed to return safe type" + ); + result_type result; try{ result = v1 % t2; - - static_assert( - boost::numeric::is_safe::value, - "Expression failed to return safe type" - ); - - if(expected_result != '.'){ + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; + if(expected_result =='x'){ std::cout << "failed to detect error in modulus " << std::hex << result << "(" << std::dec << result << ")" @@ -91,6 +101,8 @@ bool test_modulus( } } catch(std::exception){ + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; if(expected_result != 'x'){ std::cout << "erroneously detected error in modulus " @@ -108,12 +120,17 @@ bool test_modulus( { safe_t t1 = v1; safe_t t2 = v2; - - safe_t result; - + using result_type = decltype(t1 + t2); + std::cout << "safe<" << av1 << "> % " << "safe<" << av2 << "> -> "; + static_assert( + boost::numeric::is_safe::value, + "Expression failed to return safe type" + ); + result_type result; try{ result = t1 % t2; - + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; if(expected_result != '.'){ std::cout << "failed to detect error in modulus " @@ -128,6 +145,8 @@ bool test_modulus( } } catch(std::exception){ + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; if(expected_result != 'x'){ std::cout << "erroneously detected error in modulus " diff --git a/test/test_multiply.hpp b/test/test_multiply.hpp index 0993662..b011ec6 100644 --- a/test/test_multiply.hpp +++ b/test/test_multiply.hpp @@ -37,8 +37,6 @@ bool test_multiply( try{ result = t1 * v2; - std::cout << std::hex << result << "(" << std::dec << result << ")" - << std::endl; if(expected_result == 'x'){ const std::type_info & ti = typeid(result); int status; @@ -54,8 +52,6 @@ bool test_multiply( } } catch(std::exception){ - std::cout << std::hex << result << "(" << std::dec << result << ")" - << std::endl; if(expected_result == '.'){ const std::type_info & ti = typeid(result); int status; @@ -82,8 +78,6 @@ bool test_multiply( try{ result = v1 * t2; - std::cout << std::hex << result << "(" << std::dec << result << ")" - << std::endl; if(expected_result == 'x'){ const std::type_info & ti = typeid(result); int status; @@ -99,8 +93,6 @@ bool test_multiply( } } catch(std::exception){ - std::cout << std::hex << result << "(" << std::dec << result << ")" - << std::endl; if(expected_result == '.'){ const std::type_info & ti = typeid(result); int status; @@ -129,8 +121,6 @@ bool test_multiply( try{ result = t1 * t2; - std::cout << std::hex << result << "(" << std::dec << result << ")" - << std::endl; if(expected_result == 'x'){ const std::type_info & ti = typeid(result); int status; @@ -146,8 +136,6 @@ bool test_multiply( } } catch(std::exception){ - std::cout << std::hex << result << "(" << std::dec << result << ")" - << std::endl; if(expected_result == '.'){ const std::type_info & ti = typeid(result); int status; diff --git a/test/test_multiply_automatic.cpp b/test/test_multiply_automatic.cpp index d6e413c..80d63eb 100644 --- a/test/test_multiply_automatic.cpp +++ b/test/test_multiply_automatic.cpp @@ -6,7 +6,7 @@ #include #include -#include +//#include #include "../include/safe_integer.hpp" #include "../include/automatic.hpp" @@ -29,23 +29,23 @@ const char *test_multiplication_result[VALUE_ARRAY_SIZE] = { // 0 0 0 0 // 01234567012345670123456701234567 // 01234567890123456789012345678901 -/* 0*/ "................................", +/* 0*/ "..............................xx", /* 1*/ ".............xx..............xxx", -/* 2*/ ".............xx.............xxxx", -/* 3*/ "..............x.............xxxx", -/* 4*/ "................................", +/* 2*/ ".............xx..............xxx", +/* 3*/ "..............x...............xx", +/* 4*/ "..............................xx", /* 5*/ ".............xx..............xxx", -/* 6*/ ".............xx.............xxxx", -/* 7*/ "..............x.............xxxx", +/* 6*/ ".............xx..............xxx", +/* 7*/ "..............x...............xx", -/* 8*/ "................................", +/* 8*/ "..............................xx", /* 9*/ ".............xx..............xxx", -/*10*/ ".............xx.............xxxx", -/*11*/ "..............x.............xxxx", -/*12*/ "................................", +/*10*/ ".............xx..............xxx", +/*11*/ "..............x...............xx", +/*12*/ "..............................xx", /*13*/ ".xx..xx..xx..xx..xxx.xxx.xxx.xxx", -/*14*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxxxxxx", -/*15*/ "..............x.............xxxx", +/*14*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*15*/ "..............x...............xx", // 0 0 0 0 // 01234567012345670123456701234567 @@ -63,10 +63,10 @@ const char *test_multiplication_result[VALUE_ARRAY_SIZE] = { /*25*/ ".............xx..............xxx", /*26*/ ".............xx..............xxx", /*27*/ ".............xx..............xxx", -/*28*/ "..xx..xx..xx..xx................", -/*29*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", -/*30*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", -/*31*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx" +/*28*/ "................................", +/*29*/ ".xx..xx..xx..xx..xxx.xxx.xxx.xxx", +/*30*/ "xxxxxxxxxxxxxxxx.xxx.xxx.xxx.xxx", +/*31*/ "xxxxxxxxxxxxxxxx.xxx.xxx.xxx.xxx" }; #define TEST_IMPL(v1, v2, result) \ diff --git a/test/test_multiply_native.cpp b/test/test_multiply_native.cpp index b11231a..e1080a6 100644 --- a/test/test_multiply_native.cpp +++ b/test/test_multiply_native.cpp @@ -6,7 +6,7 @@ #include #include -#include +//#include #include "../include/safe_integer.hpp" diff --git a/test/test_right_shift.hpp b/test/test_right_shift.hpp index f895908..01be335 100644 --- a/test/test_right_shift.hpp +++ b/test/test_right_shift.hpp @@ -9,7 +9,7 @@ #include #include -#include +//#include #include "../include/safe_integer.hpp" diff --git a/test/test_subtract.hpp b/test/test_subtract.hpp index df8c00a..3b5a22a 100644 --- a/test/test_subtract.hpp +++ b/test/test_subtract.hpp @@ -9,7 +9,7 @@ #include #include -#include +//#include #include "../include/safe_integer.hpp" diff --git a/test/test_subtract_automatic.cpp b/test/test_subtract_automatic.cpp index fb8ad42..d45b36d 100644 --- a/test/test_subtract_automatic.cpp +++ b/test/test_subtract_automatic.cpp @@ -6,7 +6,6 @@ #include #include -#include #include "../include/safe_integer.hpp" #include "../include/automatic.hpp" diff --git a/test/test_z.cpp b/test/test_z.cpp index 1196d26..7586c70 100644 --- a/test/test_z.cpp +++ b/test/test_z.cpp @@ -408,6 +408,89 @@ int main(){ return 0; } +// test utility +#include "../include/utility.hpp" + +int main(){ + using namespace boost::numeric; + using x = unsigned_stored_type<0, 42>; + print_type p1; + + return 0; +} + + +// test automatic type promotion +#include "../include/automatic.hpp" +#include "../include/safe_integer.hpp" +#include +#include +#include + +int main(){ + using namespace boost::numeric; + using ar = automatic::addition_result; + static_assert( + std::is_same::value, + "sum of two 8 bit unsigned integers should fit in on 16 bit unsigned integer" + ); + return 0; +} + + +// test automatic type promotion +#include "../include/safe_integer.hpp" +#include "../include/safe_range.hpp" +#include "../include/safe_literal.hpp" +#include "../include/automatic.hpp" +#include +#include +#include + +int main(){ + using namespace boost::numeric; + unsigned char t1 = 1; + constexpr const safe_unsigned_literal<42, automatic, throw_exception> v2; + using result_type = decltype(t1 + v2); + + static_assert( + std::is_same< + result_type, + safe_unsigned_range<42, 297, automatic, throw_exception> + >::value, + "result type should have a range 42-297" + ); + return 0; +} +void f1(){ + using namespace boost::numeric; + constexpr safe j = 0; + constexpr safe k = 3; + constexpr safe l = j + k; // compile error +} + +void f2(){ + using namespace boost::numeric; + constexpr safe j = boost::numeric::safe_signed_literal<0>(); + constexpr safe k = boost::numeric::safe_signed_literal<3>(); + constexpr safe l = j + k; // compile error +} + +void f3(){ + using namespace boost::numeric; + constexpr auto j = safe_signed_literal<0, native, trap_exception>(); + constexpr auto k = safe_signed_literal<3>(); + constexpr const safe l = j + k; +} + +void f4(){ + using namespace boost::numeric; + safe_signed_literal<0, native, trap_exception> j; + safe_signed_literal<3> k; + constexpr auto l = safe_signed_literal<3>(); + constexpr const safe l2 = j + k; +} + #endif int main(){